##// END OF EJS Templates
merge: use labels in subrepo merge...
Simon Farnsworth -
r30060:a145161d default
parent child Browse files
Show More
@@ -1,1674 +1,1674 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import hashlib
11 import hashlib
12 import os
12 import os
13 import shutil
13 import shutil
14 import struct
14 import struct
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 bin,
18 bin,
19 hex,
19 hex,
20 nullhex,
20 nullhex,
21 nullid,
21 nullid,
22 nullrev,
22 nullrev,
23 )
23 )
24 from . import (
24 from . import (
25 copies,
25 copies,
26 destutil,
26 destutil,
27 error,
27 error,
28 filemerge,
28 filemerge,
29 obsolete,
29 obsolete,
30 scmutil,
30 scmutil,
31 subrepo,
31 subrepo,
32 util,
32 util,
33 worker,
33 worker,
34 )
34 )
35
35
36 _pack = struct.pack
36 _pack = struct.pack
37 _unpack = struct.unpack
37 _unpack = struct.unpack
38
38
39 def _droponode(data):
39 def _droponode(data):
40 # used for compatibility for v1
40 # used for compatibility for v1
41 bits = data.split('\0')
41 bits = data.split('\0')
42 bits = bits[:-2] + bits[-1:]
42 bits = bits[:-2] + bits[-1:]
43 return '\0'.join(bits)
43 return '\0'.join(bits)
44
44
45 class mergestate(object):
45 class mergestate(object):
46 '''track 3-way merge state of individual files
46 '''track 3-way merge state of individual files
47
47
48 The merge state is stored on disk when needed. Two files are used: one with
48 The merge state is stored on disk when needed. Two files are used: one with
49 an old format (version 1), and one with a new format (version 2). Version 2
49 an old format (version 1), and one with a new format (version 2). Version 2
50 stores a superset of the data in version 1, including new kinds of records
50 stores a superset of the data in version 1, including new kinds of records
51 in the future. For more about the new format, see the documentation for
51 in the future. For more about the new format, see the documentation for
52 `_readrecordsv2`.
52 `_readrecordsv2`.
53
53
54 Each record can contain arbitrary content, and has an associated type. This
54 Each record can contain arbitrary content, and has an associated type. This
55 `type` should be a letter. If `type` is uppercase, the record is mandatory:
55 `type` should be a letter. If `type` is uppercase, the record is mandatory:
56 versions of Mercurial that don't support it should abort. If `type` is
56 versions of Mercurial that don't support it should abort. If `type` is
57 lowercase, the record can be safely ignored.
57 lowercase, the record can be safely ignored.
58
58
59 Currently known records:
59 Currently known records:
60
60
61 L: the node of the "local" part of the merge (hexified version)
61 L: the node of the "local" part of the merge (hexified version)
62 O: the node of the "other" part of the merge (hexified version)
62 O: the node of the "other" part of the merge (hexified version)
63 F: a file to be merged entry
63 F: a file to be merged entry
64 C: a change/delete or delete/change conflict
64 C: a change/delete or delete/change conflict
65 D: a file that the external merge driver will merge internally
65 D: a file that the external merge driver will merge internally
66 (experimental)
66 (experimental)
67 m: the external merge driver defined for this merge plus its run state
67 m: the external merge driver defined for this merge plus its run state
68 (experimental)
68 (experimental)
69 f: a (filename, dictonary) tuple of optional values for a given file
69 f: a (filename, dictonary) tuple of optional values for a given file
70 X: unsupported mandatory record type (used in tests)
70 X: unsupported mandatory record type (used in tests)
71 x: unsupported advisory record type (used in tests)
71 x: unsupported advisory record type (used in tests)
72 l: the labels for the parts of the merge.
72 l: the labels for the parts of the merge.
73
73
74 Merge driver run states (experimental):
74 Merge driver run states (experimental):
75 u: driver-resolved files unmarked -- needs to be run next time we're about
75 u: driver-resolved files unmarked -- needs to be run next time we're about
76 to resolve or commit
76 to resolve or commit
77 m: driver-resolved files marked -- only needs to be run before commit
77 m: driver-resolved files marked -- only needs to be run before commit
78 s: success/skipped -- does not need to be run any more
78 s: success/skipped -- does not need to be run any more
79
79
80 '''
80 '''
81 statepathv1 = 'merge/state'
81 statepathv1 = 'merge/state'
82 statepathv2 = 'merge/state2'
82 statepathv2 = 'merge/state2'
83
83
84 @staticmethod
84 @staticmethod
85 def clean(repo, node=None, other=None, labels=None):
85 def clean(repo, node=None, other=None, labels=None):
86 """Initialize a brand new merge state, removing any existing state on
86 """Initialize a brand new merge state, removing any existing state on
87 disk."""
87 disk."""
88 ms = mergestate(repo)
88 ms = mergestate(repo)
89 ms.reset(node, other, labels)
89 ms.reset(node, other, labels)
90 return ms
90 return ms
91
91
92 @staticmethod
92 @staticmethod
93 def read(repo):
93 def read(repo):
94 """Initialize the merge state, reading it from disk."""
94 """Initialize the merge state, reading it from disk."""
95 ms = mergestate(repo)
95 ms = mergestate(repo)
96 ms._read()
96 ms._read()
97 return ms
97 return ms
98
98
99 def __init__(self, repo):
99 def __init__(self, repo):
100 """Initialize the merge state.
100 """Initialize the merge state.
101
101
102 Do not use this directly! Instead call read() or clean()."""
102 Do not use this directly! Instead call read() or clean()."""
103 self._repo = repo
103 self._repo = repo
104 self._dirty = False
104 self._dirty = False
105 self._labels = None
105 self._labels = None
106
106
107 def reset(self, node=None, other=None, labels=None):
107 def reset(self, node=None, other=None, labels=None):
108 self._state = {}
108 self._state = {}
109 self._stateextras = {}
109 self._stateextras = {}
110 self._local = None
110 self._local = None
111 self._other = None
111 self._other = None
112 self._labels = labels
112 self._labels = labels
113 for var in ('localctx', 'otherctx'):
113 for var in ('localctx', 'otherctx'):
114 if var in vars(self):
114 if var in vars(self):
115 delattr(self, var)
115 delattr(self, var)
116 if node:
116 if node:
117 self._local = node
117 self._local = node
118 self._other = other
118 self._other = other
119 self._readmergedriver = None
119 self._readmergedriver = None
120 if self.mergedriver:
120 if self.mergedriver:
121 self._mdstate = 's'
121 self._mdstate = 's'
122 else:
122 else:
123 self._mdstate = 'u'
123 self._mdstate = 'u'
124 shutil.rmtree(self._repo.join('merge'), True)
124 shutil.rmtree(self._repo.join('merge'), True)
125 self._results = {}
125 self._results = {}
126 self._dirty = False
126 self._dirty = False
127
127
128 def _read(self):
128 def _read(self):
129 """Analyse each record content to restore a serialized state from disk
129 """Analyse each record content to restore a serialized state from disk
130
130
131 This function process "record" entry produced by the de-serialization
131 This function process "record" entry produced by the de-serialization
132 of on disk file.
132 of on disk file.
133 """
133 """
134 self._state = {}
134 self._state = {}
135 self._stateextras = {}
135 self._stateextras = {}
136 self._local = None
136 self._local = None
137 self._other = None
137 self._other = None
138 for var in ('localctx', 'otherctx'):
138 for var in ('localctx', 'otherctx'):
139 if var in vars(self):
139 if var in vars(self):
140 delattr(self, var)
140 delattr(self, var)
141 self._readmergedriver = None
141 self._readmergedriver = None
142 self._mdstate = 's'
142 self._mdstate = 's'
143 unsupported = set()
143 unsupported = set()
144 records = self._readrecords()
144 records = self._readrecords()
145 for rtype, record in records:
145 for rtype, record in records:
146 if rtype == 'L':
146 if rtype == 'L':
147 self._local = bin(record)
147 self._local = bin(record)
148 elif rtype == 'O':
148 elif rtype == 'O':
149 self._other = bin(record)
149 self._other = bin(record)
150 elif rtype == 'm':
150 elif rtype == 'm':
151 bits = record.split('\0', 1)
151 bits = record.split('\0', 1)
152 mdstate = bits[1]
152 mdstate = bits[1]
153 if len(mdstate) != 1 or mdstate not in 'ums':
153 if len(mdstate) != 1 or mdstate not in 'ums':
154 # the merge driver should be idempotent, so just rerun it
154 # the merge driver should be idempotent, so just rerun it
155 mdstate = 'u'
155 mdstate = 'u'
156
156
157 self._readmergedriver = bits[0]
157 self._readmergedriver = bits[0]
158 self._mdstate = mdstate
158 self._mdstate = mdstate
159 elif rtype in 'FDC':
159 elif rtype in 'FDC':
160 bits = record.split('\0')
160 bits = record.split('\0')
161 self._state[bits[0]] = bits[1:]
161 self._state[bits[0]] = bits[1:]
162 elif rtype == 'f':
162 elif rtype == 'f':
163 filename, rawextras = record.split('\0', 1)
163 filename, rawextras = record.split('\0', 1)
164 extraparts = rawextras.split('\0')
164 extraparts = rawextras.split('\0')
165 extras = {}
165 extras = {}
166 i = 0
166 i = 0
167 while i < len(extraparts):
167 while i < len(extraparts):
168 extras[extraparts[i]] = extraparts[i + 1]
168 extras[extraparts[i]] = extraparts[i + 1]
169 i += 2
169 i += 2
170
170
171 self._stateextras[filename] = extras
171 self._stateextras[filename] = extras
172 elif rtype == 'l':
172 elif rtype == 'l':
173 labels = record.split('\0', 2)
173 labels = record.split('\0', 2)
174 self._labels = [l for l in labels if len(l) > 0]
174 self._labels = [l for l in labels if len(l) > 0]
175 elif not rtype.islower():
175 elif not rtype.islower():
176 unsupported.add(rtype)
176 unsupported.add(rtype)
177 self._results = {}
177 self._results = {}
178 self._dirty = False
178 self._dirty = False
179
179
180 if unsupported:
180 if unsupported:
181 raise error.UnsupportedMergeRecords(unsupported)
181 raise error.UnsupportedMergeRecords(unsupported)
182
182
183 def _readrecords(self):
183 def _readrecords(self):
184 """Read merge state from disk and return a list of record (TYPE, data)
184 """Read merge state from disk and return a list of record (TYPE, data)
185
185
186 We read data from both v1 and v2 files and decide which one to use.
186 We read data from both v1 and v2 files and decide which one to use.
187
187
188 V1 has been used by version prior to 2.9.1 and contains less data than
188 V1 has been used by version prior to 2.9.1 and contains less data than
189 v2. We read both versions and check if no data in v2 contradicts
189 v2. We read both versions and check if no data in v2 contradicts
190 v1. If there is not contradiction we can safely assume that both v1
190 v1. If there is not contradiction we can safely assume that both v1
191 and v2 were written at the same time and use the extract data in v2. If
191 and v2 were written at the same time and use the extract data in v2. If
192 there is contradiction we ignore v2 content as we assume an old version
192 there is contradiction we ignore v2 content as we assume an old version
193 of Mercurial has overwritten the mergestate file and left an old v2
193 of Mercurial has overwritten the mergestate file and left an old v2
194 file around.
194 file around.
195
195
196 returns list of record [(TYPE, data), ...]"""
196 returns list of record [(TYPE, data), ...]"""
197 v1records = self._readrecordsv1()
197 v1records = self._readrecordsv1()
198 v2records = self._readrecordsv2()
198 v2records = self._readrecordsv2()
199 if self._v1v2match(v1records, v2records):
199 if self._v1v2match(v1records, v2records):
200 return v2records
200 return v2records
201 else:
201 else:
202 # v1 file is newer than v2 file, use it
202 # v1 file is newer than v2 file, use it
203 # we have to infer the "other" changeset of the merge
203 # we have to infer the "other" changeset of the merge
204 # we cannot do better than that with v1 of the format
204 # we cannot do better than that with v1 of the format
205 mctx = self._repo[None].parents()[-1]
205 mctx = self._repo[None].parents()[-1]
206 v1records.append(('O', mctx.hex()))
206 v1records.append(('O', mctx.hex()))
207 # add place holder "other" file node information
207 # add place holder "other" file node information
208 # nobody is using it yet so we do no need to fetch the data
208 # nobody is using it yet so we do no need to fetch the data
209 # if mctx was wrong `mctx[bits[-2]]` may fails.
209 # if mctx was wrong `mctx[bits[-2]]` may fails.
210 for idx, r in enumerate(v1records):
210 for idx, r in enumerate(v1records):
211 if r[0] == 'F':
211 if r[0] == 'F':
212 bits = r[1].split('\0')
212 bits = r[1].split('\0')
213 bits.insert(-2, '')
213 bits.insert(-2, '')
214 v1records[idx] = (r[0], '\0'.join(bits))
214 v1records[idx] = (r[0], '\0'.join(bits))
215 return v1records
215 return v1records
216
216
217 def _v1v2match(self, v1records, v2records):
217 def _v1v2match(self, v1records, v2records):
218 oldv2 = set() # old format version of v2 record
218 oldv2 = set() # old format version of v2 record
219 for rec in v2records:
219 for rec in v2records:
220 if rec[0] == 'L':
220 if rec[0] == 'L':
221 oldv2.add(rec)
221 oldv2.add(rec)
222 elif rec[0] == 'F':
222 elif rec[0] == 'F':
223 # drop the onode data (not contained in v1)
223 # drop the onode data (not contained in v1)
224 oldv2.add(('F', _droponode(rec[1])))
224 oldv2.add(('F', _droponode(rec[1])))
225 for rec in v1records:
225 for rec in v1records:
226 if rec not in oldv2:
226 if rec not in oldv2:
227 return False
227 return False
228 else:
228 else:
229 return True
229 return True
230
230
231 def _readrecordsv1(self):
231 def _readrecordsv1(self):
232 """read on disk merge state for version 1 file
232 """read on disk merge state for version 1 file
233
233
234 returns list of record [(TYPE, data), ...]
234 returns list of record [(TYPE, data), ...]
235
235
236 Note: the "F" data from this file are one entry short
236 Note: the "F" data from this file are one entry short
237 (no "other file node" entry)
237 (no "other file node" entry)
238 """
238 """
239 records = []
239 records = []
240 try:
240 try:
241 f = self._repo.vfs(self.statepathv1)
241 f = self._repo.vfs(self.statepathv1)
242 for i, l in enumerate(f):
242 for i, l in enumerate(f):
243 if i == 0:
243 if i == 0:
244 records.append(('L', l[:-1]))
244 records.append(('L', l[:-1]))
245 else:
245 else:
246 records.append(('F', l[:-1]))
246 records.append(('F', l[:-1]))
247 f.close()
247 f.close()
248 except IOError as err:
248 except IOError as err:
249 if err.errno != errno.ENOENT:
249 if err.errno != errno.ENOENT:
250 raise
250 raise
251 return records
251 return records
252
252
253 def _readrecordsv2(self):
253 def _readrecordsv2(self):
254 """read on disk merge state for version 2 file
254 """read on disk merge state for version 2 file
255
255
256 This format is a list of arbitrary records of the form:
256 This format is a list of arbitrary records of the form:
257
257
258 [type][length][content]
258 [type][length][content]
259
259
260 `type` is a single character, `length` is a 4 byte integer, and
260 `type` is a single character, `length` is a 4 byte integer, and
261 `content` is an arbitrary byte sequence of length `length`.
261 `content` is an arbitrary byte sequence of length `length`.
262
262
263 Mercurial versions prior to 3.7 have a bug where if there are
263 Mercurial versions prior to 3.7 have a bug where if there are
264 unsupported mandatory merge records, attempting to clear out the merge
264 unsupported mandatory merge records, attempting to clear out the merge
265 state with hg update --clean or similar aborts. The 't' record type
265 state with hg update --clean or similar aborts. The 't' record type
266 works around that by writing out what those versions treat as an
266 works around that by writing out what those versions treat as an
267 advisory record, but later versions interpret as special: the first
267 advisory record, but later versions interpret as special: the first
268 character is the 'real' record type and everything onwards is the data.
268 character is the 'real' record type and everything onwards is the data.
269
269
270 Returns list of records [(TYPE, data), ...]."""
270 Returns list of records [(TYPE, data), ...]."""
271 records = []
271 records = []
272 try:
272 try:
273 f = self._repo.vfs(self.statepathv2)
273 f = self._repo.vfs(self.statepathv2)
274 data = f.read()
274 data = f.read()
275 off = 0
275 off = 0
276 end = len(data)
276 end = len(data)
277 while off < end:
277 while off < end:
278 rtype = data[off]
278 rtype = data[off]
279 off += 1
279 off += 1
280 length = _unpack('>I', data[off:(off + 4)])[0]
280 length = _unpack('>I', data[off:(off + 4)])[0]
281 off += 4
281 off += 4
282 record = data[off:(off + length)]
282 record = data[off:(off + length)]
283 off += length
283 off += length
284 if rtype == 't':
284 if rtype == 't':
285 rtype, record = record[0], record[1:]
285 rtype, record = record[0], record[1:]
286 records.append((rtype, record))
286 records.append((rtype, record))
287 f.close()
287 f.close()
288 except IOError as err:
288 except IOError as err:
289 if err.errno != errno.ENOENT:
289 if err.errno != errno.ENOENT:
290 raise
290 raise
291 return records
291 return records
292
292
293 @util.propertycache
293 @util.propertycache
294 def mergedriver(self):
294 def mergedriver(self):
295 # protect against the following:
295 # protect against the following:
296 # - A configures a malicious merge driver in their hgrc, then
296 # - A configures a malicious merge driver in their hgrc, then
297 # pauses the merge
297 # pauses the merge
298 # - A edits their hgrc to remove references to the merge driver
298 # - A edits their hgrc to remove references to the merge driver
299 # - A gives a copy of their entire repo, including .hg, to B
299 # - A gives a copy of their entire repo, including .hg, to B
300 # - B inspects .hgrc and finds it to be clean
300 # - B inspects .hgrc and finds it to be clean
301 # - B then continues the merge and the malicious merge driver
301 # - B then continues the merge and the malicious merge driver
302 # gets invoked
302 # gets invoked
303 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
303 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
304 if (self._readmergedriver is not None
304 if (self._readmergedriver is not None
305 and self._readmergedriver != configmergedriver):
305 and self._readmergedriver != configmergedriver):
306 raise error.ConfigError(
306 raise error.ConfigError(
307 _("merge driver changed since merge started"),
307 _("merge driver changed since merge started"),
308 hint=_("revert merge driver change or abort merge"))
308 hint=_("revert merge driver change or abort merge"))
309
309
310 return configmergedriver
310 return configmergedriver
311
311
312 @util.propertycache
312 @util.propertycache
313 def localctx(self):
313 def localctx(self):
314 if self._local is None:
314 if self._local is None:
315 raise RuntimeError("localctx accessed but self._local isn't set")
315 raise RuntimeError("localctx accessed but self._local isn't set")
316 return self._repo[self._local]
316 return self._repo[self._local]
317
317
318 @util.propertycache
318 @util.propertycache
319 def otherctx(self):
319 def otherctx(self):
320 if self._other is None:
320 if self._other is None:
321 raise RuntimeError("otherctx accessed but self._other isn't set")
321 raise RuntimeError("otherctx accessed but self._other isn't set")
322 return self._repo[self._other]
322 return self._repo[self._other]
323
323
324 def active(self):
324 def active(self):
325 """Whether mergestate is active.
325 """Whether mergestate is active.
326
326
327 Returns True if there appears to be mergestate. This is a rough proxy
327 Returns True if there appears to be mergestate. This is a rough proxy
328 for "is a merge in progress."
328 for "is a merge in progress."
329 """
329 """
330 # Check local variables before looking at filesystem for performance
330 # Check local variables before looking at filesystem for performance
331 # reasons.
331 # reasons.
332 return bool(self._local) or bool(self._state) or \
332 return bool(self._local) or bool(self._state) or \
333 self._repo.vfs.exists(self.statepathv1) or \
333 self._repo.vfs.exists(self.statepathv1) or \
334 self._repo.vfs.exists(self.statepathv2)
334 self._repo.vfs.exists(self.statepathv2)
335
335
336 def commit(self):
336 def commit(self):
337 """Write current state on disk (if necessary)"""
337 """Write current state on disk (if necessary)"""
338 if self._dirty:
338 if self._dirty:
339 records = self._makerecords()
339 records = self._makerecords()
340 self._writerecords(records)
340 self._writerecords(records)
341 self._dirty = False
341 self._dirty = False
342
342
343 def _makerecords(self):
343 def _makerecords(self):
344 records = []
344 records = []
345 records.append(('L', hex(self._local)))
345 records.append(('L', hex(self._local)))
346 records.append(('O', hex(self._other)))
346 records.append(('O', hex(self._other)))
347 if self.mergedriver:
347 if self.mergedriver:
348 records.append(('m', '\0'.join([
348 records.append(('m', '\0'.join([
349 self.mergedriver, self._mdstate])))
349 self.mergedriver, self._mdstate])))
350 for d, v in self._state.iteritems():
350 for d, v in self._state.iteritems():
351 if v[0] == 'd':
351 if v[0] == 'd':
352 records.append(('D', '\0'.join([d] + v)))
352 records.append(('D', '\0'.join([d] + v)))
353 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
353 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
354 # older versions of Mercurial
354 # older versions of Mercurial
355 elif v[1] == nullhex or v[6] == nullhex:
355 elif v[1] == nullhex or v[6] == nullhex:
356 records.append(('C', '\0'.join([d] + v)))
356 records.append(('C', '\0'.join([d] + v)))
357 else:
357 else:
358 records.append(('F', '\0'.join([d] + v)))
358 records.append(('F', '\0'.join([d] + v)))
359 for filename, extras in sorted(self._stateextras.iteritems()):
359 for filename, extras in sorted(self._stateextras.iteritems()):
360 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
360 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
361 extras.iteritems())
361 extras.iteritems())
362 records.append(('f', '%s\0%s' % (filename, rawextras)))
362 records.append(('f', '%s\0%s' % (filename, rawextras)))
363 if self._labels is not None:
363 if self._labels is not None:
364 labels = '\0'.join(self._labels)
364 labels = '\0'.join(self._labels)
365 records.append(('l', labels))
365 records.append(('l', labels))
366 return records
366 return records
367
367
368 def _writerecords(self, records):
368 def _writerecords(self, records):
369 """Write current state on disk (both v1 and v2)"""
369 """Write current state on disk (both v1 and v2)"""
370 self._writerecordsv1(records)
370 self._writerecordsv1(records)
371 self._writerecordsv2(records)
371 self._writerecordsv2(records)
372
372
373 def _writerecordsv1(self, records):
373 def _writerecordsv1(self, records):
374 """Write current state on disk in a version 1 file"""
374 """Write current state on disk in a version 1 file"""
375 f = self._repo.vfs(self.statepathv1, 'w')
375 f = self._repo.vfs(self.statepathv1, 'w')
376 irecords = iter(records)
376 irecords = iter(records)
377 lrecords = next(irecords)
377 lrecords = next(irecords)
378 assert lrecords[0] == 'L'
378 assert lrecords[0] == 'L'
379 f.write(hex(self._local) + '\n')
379 f.write(hex(self._local) + '\n')
380 for rtype, data in irecords:
380 for rtype, data in irecords:
381 if rtype == 'F':
381 if rtype == 'F':
382 f.write('%s\n' % _droponode(data))
382 f.write('%s\n' % _droponode(data))
383 f.close()
383 f.close()
384
384
385 def _writerecordsv2(self, records):
385 def _writerecordsv2(self, records):
386 """Write current state on disk in a version 2 file
386 """Write current state on disk in a version 2 file
387
387
388 See the docstring for _readrecordsv2 for why we use 't'."""
388 See the docstring for _readrecordsv2 for why we use 't'."""
389 # these are the records that all version 2 clients can read
389 # these are the records that all version 2 clients can read
390 whitelist = 'LOF'
390 whitelist = 'LOF'
391 f = self._repo.vfs(self.statepathv2, 'w')
391 f = self._repo.vfs(self.statepathv2, 'w')
392 for key, data in records:
392 for key, data in records:
393 assert len(key) == 1
393 assert len(key) == 1
394 if key not in whitelist:
394 if key not in whitelist:
395 key, data = 't', '%s%s' % (key, data)
395 key, data = 't', '%s%s' % (key, data)
396 format = '>sI%is' % len(data)
396 format = '>sI%is' % len(data)
397 f.write(_pack(format, key, len(data), data))
397 f.write(_pack(format, key, len(data), data))
398 f.close()
398 f.close()
399
399
400 def add(self, fcl, fco, fca, fd):
400 def add(self, fcl, fco, fca, fd):
401 """add a new (potentially?) conflicting file the merge state
401 """add a new (potentially?) conflicting file the merge state
402 fcl: file context for local,
402 fcl: file context for local,
403 fco: file context for remote,
403 fco: file context for remote,
404 fca: file context for ancestors,
404 fca: file context for ancestors,
405 fd: file path of the resulting merge.
405 fd: file path of the resulting merge.
406
406
407 note: also write the local version to the `.hg/merge` directory.
407 note: also write the local version to the `.hg/merge` directory.
408 """
408 """
409 if fcl.isabsent():
409 if fcl.isabsent():
410 hash = nullhex
410 hash = nullhex
411 else:
411 else:
412 hash = hashlib.sha1(fcl.path()).hexdigest()
412 hash = hashlib.sha1(fcl.path()).hexdigest()
413 self._repo.vfs.write('merge/' + hash, fcl.data())
413 self._repo.vfs.write('merge/' + hash, fcl.data())
414 self._state[fd] = ['u', hash, fcl.path(),
414 self._state[fd] = ['u', hash, fcl.path(),
415 fca.path(), hex(fca.filenode()),
415 fca.path(), hex(fca.filenode()),
416 fco.path(), hex(fco.filenode()),
416 fco.path(), hex(fco.filenode()),
417 fcl.flags()]
417 fcl.flags()]
418 self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
418 self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
419 self._dirty = True
419 self._dirty = True
420
420
421 def __contains__(self, dfile):
421 def __contains__(self, dfile):
422 return dfile in self._state
422 return dfile in self._state
423
423
424 def __getitem__(self, dfile):
424 def __getitem__(self, dfile):
425 return self._state[dfile][0]
425 return self._state[dfile][0]
426
426
427 def __iter__(self):
427 def __iter__(self):
428 return iter(sorted(self._state))
428 return iter(sorted(self._state))
429
429
430 def files(self):
430 def files(self):
431 return self._state.keys()
431 return self._state.keys()
432
432
433 def mark(self, dfile, state):
433 def mark(self, dfile, state):
434 self._state[dfile][0] = state
434 self._state[dfile][0] = state
435 self._dirty = True
435 self._dirty = True
436
436
437 def mdstate(self):
437 def mdstate(self):
438 return self._mdstate
438 return self._mdstate
439
439
440 def unresolved(self):
440 def unresolved(self):
441 """Obtain the paths of unresolved files."""
441 """Obtain the paths of unresolved files."""
442
442
443 for f, entry in self._state.items():
443 for f, entry in self._state.items():
444 if entry[0] == 'u':
444 if entry[0] == 'u':
445 yield f
445 yield f
446
446
447 def driverresolved(self):
447 def driverresolved(self):
448 """Obtain the paths of driver-resolved files."""
448 """Obtain the paths of driver-resolved files."""
449
449
450 for f, entry in self._state.items():
450 for f, entry in self._state.items():
451 if entry[0] == 'd':
451 if entry[0] == 'd':
452 yield f
452 yield f
453
453
454 def extras(self, filename):
454 def extras(self, filename):
455 return self._stateextras.setdefault(filename, {})
455 return self._stateextras.setdefault(filename, {})
456
456
457 def _resolve(self, preresolve, dfile, wctx):
457 def _resolve(self, preresolve, dfile, wctx):
458 """rerun merge process for file path `dfile`"""
458 """rerun merge process for file path `dfile`"""
459 if self[dfile] in 'rd':
459 if self[dfile] in 'rd':
460 return True, 0
460 return True, 0
461 stateentry = self._state[dfile]
461 stateentry = self._state[dfile]
462 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
462 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
463 octx = self._repo[self._other]
463 octx = self._repo[self._other]
464 extras = self.extras(dfile)
464 extras = self.extras(dfile)
465 anccommitnode = extras.get('ancestorlinknode')
465 anccommitnode = extras.get('ancestorlinknode')
466 if anccommitnode:
466 if anccommitnode:
467 actx = self._repo[anccommitnode]
467 actx = self._repo[anccommitnode]
468 else:
468 else:
469 actx = None
469 actx = None
470 fcd = self._filectxorabsent(hash, wctx, dfile)
470 fcd = self._filectxorabsent(hash, wctx, dfile)
471 fco = self._filectxorabsent(onode, octx, ofile)
471 fco = self._filectxorabsent(onode, octx, ofile)
472 # TODO: move this to filectxorabsent
472 # TODO: move this to filectxorabsent
473 fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
473 fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
474 # "premerge" x flags
474 # "premerge" x flags
475 flo = fco.flags()
475 flo = fco.flags()
476 fla = fca.flags()
476 fla = fca.flags()
477 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
477 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
478 if fca.node() == nullid:
478 if fca.node() == nullid:
479 if preresolve:
479 if preresolve:
480 self._repo.ui.warn(
480 self._repo.ui.warn(
481 _('warning: cannot merge flags for %s\n') % afile)
481 _('warning: cannot merge flags for %s\n') % afile)
482 elif flags == fla:
482 elif flags == fla:
483 flags = flo
483 flags = flo
484 if preresolve:
484 if preresolve:
485 # restore local
485 # restore local
486 if hash != nullhex:
486 if hash != nullhex:
487 f = self._repo.vfs('merge/' + hash)
487 f = self._repo.vfs('merge/' + hash)
488 self._repo.wwrite(dfile, f.read(), flags)
488 self._repo.wwrite(dfile, f.read(), flags)
489 f.close()
489 f.close()
490 else:
490 else:
491 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
491 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
492 complete, r, deleted = filemerge.premerge(self._repo, self._local,
492 complete, r, deleted = filemerge.premerge(self._repo, self._local,
493 lfile, fcd, fco, fca,
493 lfile, fcd, fco, fca,
494 labels=self._labels)
494 labels=self._labels)
495 else:
495 else:
496 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
496 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
497 lfile, fcd, fco, fca,
497 lfile, fcd, fco, fca,
498 labels=self._labels)
498 labels=self._labels)
499 if r is None:
499 if r is None:
500 # no real conflict
500 # no real conflict
501 del self._state[dfile]
501 del self._state[dfile]
502 self._stateextras.pop(dfile, None)
502 self._stateextras.pop(dfile, None)
503 self._dirty = True
503 self._dirty = True
504 elif not r:
504 elif not r:
505 self.mark(dfile, 'r')
505 self.mark(dfile, 'r')
506
506
507 if complete:
507 if complete:
508 action = None
508 action = None
509 if deleted:
509 if deleted:
510 if fcd.isabsent():
510 if fcd.isabsent():
511 # dc: local picked. Need to drop if present, which may
511 # dc: local picked. Need to drop if present, which may
512 # happen on re-resolves.
512 # happen on re-resolves.
513 action = 'f'
513 action = 'f'
514 else:
514 else:
515 # cd: remote picked (or otherwise deleted)
515 # cd: remote picked (or otherwise deleted)
516 action = 'r'
516 action = 'r'
517 else:
517 else:
518 if fcd.isabsent(): # dc: remote picked
518 if fcd.isabsent(): # dc: remote picked
519 action = 'g'
519 action = 'g'
520 elif fco.isabsent(): # cd: local picked
520 elif fco.isabsent(): # cd: local picked
521 if dfile in self.localctx:
521 if dfile in self.localctx:
522 action = 'am'
522 action = 'am'
523 else:
523 else:
524 action = 'a'
524 action = 'a'
525 # else: regular merges (no action necessary)
525 # else: regular merges (no action necessary)
526 self._results[dfile] = r, action
526 self._results[dfile] = r, action
527
527
528 return complete, r
528 return complete, r
529
529
530 def _filectxorabsent(self, hexnode, ctx, f):
530 def _filectxorabsent(self, hexnode, ctx, f):
531 if hexnode == nullhex:
531 if hexnode == nullhex:
532 return filemerge.absentfilectx(ctx, f)
532 return filemerge.absentfilectx(ctx, f)
533 else:
533 else:
534 return ctx[f]
534 return ctx[f]
535
535
536 def preresolve(self, dfile, wctx):
536 def preresolve(self, dfile, wctx):
537 """run premerge process for dfile
537 """run premerge process for dfile
538
538
539 Returns whether the merge is complete, and the exit code."""
539 Returns whether the merge is complete, and the exit code."""
540 return self._resolve(True, dfile, wctx)
540 return self._resolve(True, dfile, wctx)
541
541
542 def resolve(self, dfile, wctx):
542 def resolve(self, dfile, wctx):
543 """run merge process (assuming premerge was run) for dfile
543 """run merge process (assuming premerge was run) for dfile
544
544
545 Returns the exit code of the merge."""
545 Returns the exit code of the merge."""
546 return self._resolve(False, dfile, wctx)[1]
546 return self._resolve(False, dfile, wctx)[1]
547
547
548 def counts(self):
548 def counts(self):
549 """return counts for updated, merged and removed files in this
549 """return counts for updated, merged and removed files in this
550 session"""
550 session"""
551 updated, merged, removed = 0, 0, 0
551 updated, merged, removed = 0, 0, 0
552 for r, action in self._results.itervalues():
552 for r, action in self._results.itervalues():
553 if r is None:
553 if r is None:
554 updated += 1
554 updated += 1
555 elif r == 0:
555 elif r == 0:
556 if action == 'r':
556 if action == 'r':
557 removed += 1
557 removed += 1
558 else:
558 else:
559 merged += 1
559 merged += 1
560 return updated, merged, removed
560 return updated, merged, removed
561
561
562 def unresolvedcount(self):
562 def unresolvedcount(self):
563 """get unresolved count for this merge (persistent)"""
563 """get unresolved count for this merge (persistent)"""
564 return len([True for f, entry in self._state.iteritems()
564 return len([True for f, entry in self._state.iteritems()
565 if entry[0] == 'u'])
565 if entry[0] == 'u'])
566
566
567 def actions(self):
567 def actions(self):
568 """return lists of actions to perform on the dirstate"""
568 """return lists of actions to perform on the dirstate"""
569 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
569 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
570 for f, (r, action) in self._results.iteritems():
570 for f, (r, action) in self._results.iteritems():
571 if action is not None:
571 if action is not None:
572 actions[action].append((f, None, "merge result"))
572 actions[action].append((f, None, "merge result"))
573 return actions
573 return actions
574
574
575 def recordactions(self):
575 def recordactions(self):
576 """record remove/add/get actions in the dirstate"""
576 """record remove/add/get actions in the dirstate"""
577 branchmerge = self._repo.dirstate.p2() != nullid
577 branchmerge = self._repo.dirstate.p2() != nullid
578 recordupdates(self._repo, self.actions(), branchmerge)
578 recordupdates(self._repo, self.actions(), branchmerge)
579
579
580 def queueremove(self, f):
580 def queueremove(self, f):
581 """queues a file to be removed from the dirstate
581 """queues a file to be removed from the dirstate
582
582
583 Meant for use by custom merge drivers."""
583 Meant for use by custom merge drivers."""
584 self._results[f] = 0, 'r'
584 self._results[f] = 0, 'r'
585
585
586 def queueadd(self, f):
586 def queueadd(self, f):
587 """queues a file to be added to the dirstate
587 """queues a file to be added to the dirstate
588
588
589 Meant for use by custom merge drivers."""
589 Meant for use by custom merge drivers."""
590 self._results[f] = 0, 'a'
590 self._results[f] = 0, 'a'
591
591
592 def queueget(self, f):
592 def queueget(self, f):
593 """queues a file to be marked modified in the dirstate
593 """queues a file to be marked modified in the dirstate
594
594
595 Meant for use by custom merge drivers."""
595 Meant for use by custom merge drivers."""
596 self._results[f] = 0, 'g'
596 self._results[f] = 0, 'g'
597
597
598 def _getcheckunknownconfig(repo, section, name):
598 def _getcheckunknownconfig(repo, section, name):
599 config = repo.ui.config(section, name, default='abort')
599 config = repo.ui.config(section, name, default='abort')
600 valid = ['abort', 'ignore', 'warn']
600 valid = ['abort', 'ignore', 'warn']
601 if config not in valid:
601 if config not in valid:
602 validstr = ', '.join(["'" + v + "'" for v in valid])
602 validstr = ', '.join(["'" + v + "'" for v in valid])
603 raise error.ConfigError(_("%s.%s not valid "
603 raise error.ConfigError(_("%s.%s not valid "
604 "('%s' is none of %s)")
604 "('%s' is none of %s)")
605 % (section, name, config, validstr))
605 % (section, name, config, validstr))
606 return config
606 return config
607
607
608 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
608 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
609 if f2 is None:
609 if f2 is None:
610 f2 = f
610 f2 = f
611 return (repo.wvfs.audit.check(f)
611 return (repo.wvfs.audit.check(f)
612 and repo.wvfs.isfileorlink(f)
612 and repo.wvfs.isfileorlink(f)
613 and repo.dirstate.normalize(f) not in repo.dirstate
613 and repo.dirstate.normalize(f) not in repo.dirstate
614 and mctx[f2].cmp(wctx[f]))
614 and mctx[f2].cmp(wctx[f]))
615
615
616 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
616 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
617 """
617 """
618 Considers any actions that care about the presence of conflicting unknown
618 Considers any actions that care about the presence of conflicting unknown
619 files. For some actions, the result is to abort; for others, it is to
619 files. For some actions, the result is to abort; for others, it is to
620 choose a different action.
620 choose a different action.
621 """
621 """
622 conflicts = set()
622 conflicts = set()
623 warnconflicts = set()
623 warnconflicts = set()
624 abortconflicts = set()
624 abortconflicts = set()
625 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
625 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
626 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
626 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
627 if not force:
627 if not force:
628 def collectconflicts(conflicts, config):
628 def collectconflicts(conflicts, config):
629 if config == 'abort':
629 if config == 'abort':
630 abortconflicts.update(conflicts)
630 abortconflicts.update(conflicts)
631 elif config == 'warn':
631 elif config == 'warn':
632 warnconflicts.update(conflicts)
632 warnconflicts.update(conflicts)
633
633
634 for f, (m, args, msg) in actions.iteritems():
634 for f, (m, args, msg) in actions.iteritems():
635 if m in ('c', 'dc'):
635 if m in ('c', 'dc'):
636 if _checkunknownfile(repo, wctx, mctx, f):
636 if _checkunknownfile(repo, wctx, mctx, f):
637 conflicts.add(f)
637 conflicts.add(f)
638 elif m == 'dg':
638 elif m == 'dg':
639 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
639 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
640 conflicts.add(f)
640 conflicts.add(f)
641
641
642 ignoredconflicts = set([c for c in conflicts
642 ignoredconflicts = set([c for c in conflicts
643 if repo.dirstate._ignore(c)])
643 if repo.dirstate._ignore(c)])
644 unknownconflicts = conflicts - ignoredconflicts
644 unknownconflicts = conflicts - ignoredconflicts
645 collectconflicts(ignoredconflicts, ignoredconfig)
645 collectconflicts(ignoredconflicts, ignoredconfig)
646 collectconflicts(unknownconflicts, unknownconfig)
646 collectconflicts(unknownconflicts, unknownconfig)
647 else:
647 else:
648 for f, (m, args, msg) in actions.iteritems():
648 for f, (m, args, msg) in actions.iteritems():
649 if m == 'cm':
649 if m == 'cm':
650 fl2, anc = args
650 fl2, anc = args
651 different = _checkunknownfile(repo, wctx, mctx, f)
651 different = _checkunknownfile(repo, wctx, mctx, f)
652 if repo.dirstate._ignore(f):
652 if repo.dirstate._ignore(f):
653 config = ignoredconfig
653 config = ignoredconfig
654 else:
654 else:
655 config = unknownconfig
655 config = unknownconfig
656
656
657 # The behavior when force is True is described by this table:
657 # The behavior when force is True is described by this table:
658 # config different mergeforce | action backup
658 # config different mergeforce | action backup
659 # * n * | get n
659 # * n * | get n
660 # * y y | merge -
660 # * y y | merge -
661 # abort y n | merge - (1)
661 # abort y n | merge - (1)
662 # warn y n | warn + get y
662 # warn y n | warn + get y
663 # ignore y n | get y
663 # ignore y n | get y
664 #
664 #
665 # (1) this is probably the wrong behavior here -- we should
665 # (1) this is probably the wrong behavior here -- we should
666 # probably abort, but some actions like rebases currently
666 # probably abort, but some actions like rebases currently
667 # don't like an abort happening in the middle of
667 # don't like an abort happening in the middle of
668 # merge.update.
668 # merge.update.
669 if not different:
669 if not different:
670 actions[f] = ('g', (fl2, False), "remote created")
670 actions[f] = ('g', (fl2, False), "remote created")
671 elif mergeforce or config == 'abort':
671 elif mergeforce or config == 'abort':
672 actions[f] = ('m', (f, f, None, False, anc),
672 actions[f] = ('m', (f, f, None, False, anc),
673 "remote differs from untracked local")
673 "remote differs from untracked local")
674 elif config == 'abort':
674 elif config == 'abort':
675 abortconflicts.add(f)
675 abortconflicts.add(f)
676 else:
676 else:
677 if config == 'warn':
677 if config == 'warn':
678 warnconflicts.add(f)
678 warnconflicts.add(f)
679 actions[f] = ('g', (fl2, True), "remote created")
679 actions[f] = ('g', (fl2, True), "remote created")
680
680
681 for f in sorted(abortconflicts):
681 for f in sorted(abortconflicts):
682 repo.ui.warn(_("%s: untracked file differs\n") % f)
682 repo.ui.warn(_("%s: untracked file differs\n") % f)
683 if abortconflicts:
683 if abortconflicts:
684 raise error.Abort(_("untracked files in working directory "
684 raise error.Abort(_("untracked files in working directory "
685 "differ from files in requested revision"))
685 "differ from files in requested revision"))
686
686
687 for f in sorted(warnconflicts):
687 for f in sorted(warnconflicts):
688 repo.ui.warn(_("%s: replacing untracked file\n") % f)
688 repo.ui.warn(_("%s: replacing untracked file\n") % f)
689
689
690 for f, (m, args, msg) in actions.iteritems():
690 for f, (m, args, msg) in actions.iteritems():
691 backup = f in conflicts
691 backup = f in conflicts
692 if m == 'c':
692 if m == 'c':
693 flags, = args
693 flags, = args
694 actions[f] = ('g', (flags, backup), msg)
694 actions[f] = ('g', (flags, backup), msg)
695
695
696 def _forgetremoved(wctx, mctx, branchmerge):
696 def _forgetremoved(wctx, mctx, branchmerge):
697 """
697 """
698 Forget removed files
698 Forget removed files
699
699
700 If we're jumping between revisions (as opposed to merging), and if
700 If we're jumping between revisions (as opposed to merging), and if
701 neither the working directory nor the target rev has the file,
701 neither the working directory nor the target rev has the file,
702 then we need to remove it from the dirstate, to prevent the
702 then we need to remove it from the dirstate, to prevent the
703 dirstate from listing the file when it is no longer in the
703 dirstate from listing the file when it is no longer in the
704 manifest.
704 manifest.
705
705
706 If we're merging, and the other revision has removed a file
706 If we're merging, and the other revision has removed a file
707 that is not present in the working directory, we need to mark it
707 that is not present in the working directory, we need to mark it
708 as removed.
708 as removed.
709 """
709 """
710
710
711 actions = {}
711 actions = {}
712 m = 'f'
712 m = 'f'
713 if branchmerge:
713 if branchmerge:
714 m = 'r'
714 m = 'r'
715 for f in wctx.deleted():
715 for f in wctx.deleted():
716 if f not in mctx:
716 if f not in mctx:
717 actions[f] = m, None, "forget deleted"
717 actions[f] = m, None, "forget deleted"
718
718
719 if not branchmerge:
719 if not branchmerge:
720 for f in wctx.removed():
720 for f in wctx.removed():
721 if f not in mctx:
721 if f not in mctx:
722 actions[f] = 'f', None, "forget removed"
722 actions[f] = 'f', None, "forget removed"
723
723
724 return actions
724 return actions
725
725
726 def _checkcollision(repo, wmf, actions):
726 def _checkcollision(repo, wmf, actions):
727 # build provisional merged manifest up
727 # build provisional merged manifest up
728 pmmf = set(wmf)
728 pmmf = set(wmf)
729
729
730 if actions:
730 if actions:
731 # k, dr, e and rd are no-op
731 # k, dr, e and rd are no-op
732 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
732 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
733 for f, args, msg in actions[m]:
733 for f, args, msg in actions[m]:
734 pmmf.add(f)
734 pmmf.add(f)
735 for f, args, msg in actions['r']:
735 for f, args, msg in actions['r']:
736 pmmf.discard(f)
736 pmmf.discard(f)
737 for f, args, msg in actions['dm']:
737 for f, args, msg in actions['dm']:
738 f2, flags = args
738 f2, flags = args
739 pmmf.discard(f2)
739 pmmf.discard(f2)
740 pmmf.add(f)
740 pmmf.add(f)
741 for f, args, msg in actions['dg']:
741 for f, args, msg in actions['dg']:
742 pmmf.add(f)
742 pmmf.add(f)
743 for f, args, msg in actions['m']:
743 for f, args, msg in actions['m']:
744 f1, f2, fa, move, anc = args
744 f1, f2, fa, move, anc = args
745 if move:
745 if move:
746 pmmf.discard(f1)
746 pmmf.discard(f1)
747 pmmf.add(f)
747 pmmf.add(f)
748
748
749 # check case-folding collision in provisional merged manifest
749 # check case-folding collision in provisional merged manifest
750 foldmap = {}
750 foldmap = {}
751 for f in sorted(pmmf):
751 for f in sorted(pmmf):
752 fold = util.normcase(f)
752 fold = util.normcase(f)
753 if fold in foldmap:
753 if fold in foldmap:
754 raise error.Abort(_("case-folding collision between %s and %s")
754 raise error.Abort(_("case-folding collision between %s and %s")
755 % (f, foldmap[fold]))
755 % (f, foldmap[fold]))
756 foldmap[fold] = f
756 foldmap[fold] = f
757
757
758 # check case-folding of directories
758 # check case-folding of directories
759 foldprefix = unfoldprefix = lastfull = ''
759 foldprefix = unfoldprefix = lastfull = ''
760 for fold, f in sorted(foldmap.items()):
760 for fold, f in sorted(foldmap.items()):
761 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
761 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
762 # the folded prefix matches but actual casing is different
762 # the folded prefix matches but actual casing is different
763 raise error.Abort(_("case-folding collision between "
763 raise error.Abort(_("case-folding collision between "
764 "%s and directory of %s") % (lastfull, f))
764 "%s and directory of %s") % (lastfull, f))
765 foldprefix = fold + '/'
765 foldprefix = fold + '/'
766 unfoldprefix = f + '/'
766 unfoldprefix = f + '/'
767 lastfull = f
767 lastfull = f
768
768
769 def driverpreprocess(repo, ms, wctx, labels=None):
769 def driverpreprocess(repo, ms, wctx, labels=None):
770 """run the preprocess step of the merge driver, if any
770 """run the preprocess step of the merge driver, if any
771
771
772 This is currently not implemented -- it's an extension point."""
772 This is currently not implemented -- it's an extension point."""
773 return True
773 return True
774
774
775 def driverconclude(repo, ms, wctx, labels=None):
775 def driverconclude(repo, ms, wctx, labels=None):
776 """run the conclude step of the merge driver, if any
776 """run the conclude step of the merge driver, if any
777
777
778 This is currently not implemented -- it's an extension point."""
778 This is currently not implemented -- it's an extension point."""
779 return True
779 return True
780
780
781 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
781 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
782 acceptremote, followcopies):
782 acceptremote, followcopies):
783 """
783 """
784 Merge p1 and p2 with ancestor pa and generate merge action list
784 Merge p1 and p2 with ancestor pa and generate merge action list
785
785
786 branchmerge and force are as passed in to update
786 branchmerge and force are as passed in to update
787 matcher = matcher to filter file lists
787 matcher = matcher to filter file lists
788 acceptremote = accept the incoming changes without prompting
788 acceptremote = accept the incoming changes without prompting
789 """
789 """
790 if matcher is not None and matcher.always():
790 if matcher is not None and matcher.always():
791 matcher = None
791 matcher = None
792
792
793 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
793 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
794
794
795 # manifests fetched in order are going to be faster, so prime the caches
795 # manifests fetched in order are going to be faster, so prime the caches
796 [x.manifest() for x in
796 [x.manifest() for x in
797 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
797 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
798
798
799 if followcopies:
799 if followcopies:
800 ret = copies.mergecopies(repo, wctx, p2, pa)
800 ret = copies.mergecopies(repo, wctx, p2, pa)
801 copy, movewithdir, diverge, renamedelete = ret
801 copy, movewithdir, diverge, renamedelete = ret
802
802
803 repo.ui.note(_("resolving manifests\n"))
803 repo.ui.note(_("resolving manifests\n"))
804 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
804 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
805 % (bool(branchmerge), bool(force), bool(matcher)))
805 % (bool(branchmerge), bool(force), bool(matcher)))
806 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
806 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
807
807
808 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
808 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
809 copied = set(copy.values())
809 copied = set(copy.values())
810 copied.update(movewithdir.values())
810 copied.update(movewithdir.values())
811
811
812 if '.hgsubstate' in m1:
812 if '.hgsubstate' in m1:
813 # check whether sub state is modified
813 # check whether sub state is modified
814 if any(wctx.sub(s).dirty() for s in wctx.substate):
814 if any(wctx.sub(s).dirty() for s in wctx.substate):
815 m1['.hgsubstate'] += '+'
815 m1['.hgsubstate'] += '+'
816
816
817 # Compare manifests
817 # Compare manifests
818 if matcher is not None:
818 if matcher is not None:
819 m1 = m1.matches(matcher)
819 m1 = m1.matches(matcher)
820 m2 = m2.matches(matcher)
820 m2 = m2.matches(matcher)
821 diff = m1.diff(m2)
821 diff = m1.diff(m2)
822
822
823 actions = {}
823 actions = {}
824 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
824 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
825 if n1 and n2: # file exists on both local and remote side
825 if n1 and n2: # file exists on both local and remote side
826 if f not in ma:
826 if f not in ma:
827 fa = copy.get(f, None)
827 fa = copy.get(f, None)
828 if fa is not None:
828 if fa is not None:
829 actions[f] = ('m', (f, f, fa, False, pa.node()),
829 actions[f] = ('m', (f, f, fa, False, pa.node()),
830 "both renamed from " + fa)
830 "both renamed from " + fa)
831 else:
831 else:
832 actions[f] = ('m', (f, f, None, False, pa.node()),
832 actions[f] = ('m', (f, f, None, False, pa.node()),
833 "both created")
833 "both created")
834 else:
834 else:
835 a = ma[f]
835 a = ma[f]
836 fla = ma.flags(f)
836 fla = ma.flags(f)
837 nol = 'l' not in fl1 + fl2 + fla
837 nol = 'l' not in fl1 + fl2 + fla
838 if n2 == a and fl2 == fla:
838 if n2 == a and fl2 == fla:
839 actions[f] = ('k' , (), "remote unchanged")
839 actions[f] = ('k' , (), "remote unchanged")
840 elif n1 == a and fl1 == fla: # local unchanged - use remote
840 elif n1 == a and fl1 == fla: # local unchanged - use remote
841 if n1 == n2: # optimization: keep local content
841 if n1 == n2: # optimization: keep local content
842 actions[f] = ('e', (fl2,), "update permissions")
842 actions[f] = ('e', (fl2,), "update permissions")
843 else:
843 else:
844 actions[f] = ('g', (fl2, False), "remote is newer")
844 actions[f] = ('g', (fl2, False), "remote is newer")
845 elif nol and n2 == a: # remote only changed 'x'
845 elif nol and n2 == a: # remote only changed 'x'
846 actions[f] = ('e', (fl2,), "update permissions")
846 actions[f] = ('e', (fl2,), "update permissions")
847 elif nol and n1 == a: # local only changed 'x'
847 elif nol and n1 == a: # local only changed 'x'
848 actions[f] = ('g', (fl1, False), "remote is newer")
848 actions[f] = ('g', (fl1, False), "remote is newer")
849 else: # both changed something
849 else: # both changed something
850 actions[f] = ('m', (f, f, f, False, pa.node()),
850 actions[f] = ('m', (f, f, f, False, pa.node()),
851 "versions differ")
851 "versions differ")
852 elif n1: # file exists only on local side
852 elif n1: # file exists only on local side
853 if f in copied:
853 if f in copied:
854 pass # we'll deal with it on m2 side
854 pass # we'll deal with it on m2 side
855 elif f in movewithdir: # directory rename, move local
855 elif f in movewithdir: # directory rename, move local
856 f2 = movewithdir[f]
856 f2 = movewithdir[f]
857 if f2 in m2:
857 if f2 in m2:
858 actions[f2] = ('m', (f, f2, None, True, pa.node()),
858 actions[f2] = ('m', (f, f2, None, True, pa.node()),
859 "remote directory rename, both created")
859 "remote directory rename, both created")
860 else:
860 else:
861 actions[f2] = ('dm', (f, fl1),
861 actions[f2] = ('dm', (f, fl1),
862 "remote directory rename - move from " + f)
862 "remote directory rename - move from " + f)
863 elif f in copy:
863 elif f in copy:
864 f2 = copy[f]
864 f2 = copy[f]
865 actions[f] = ('m', (f, f2, f2, False, pa.node()),
865 actions[f] = ('m', (f, f2, f2, False, pa.node()),
866 "local copied/moved from " + f2)
866 "local copied/moved from " + f2)
867 elif f in ma: # clean, a different, no remote
867 elif f in ma: # clean, a different, no remote
868 if n1 != ma[f]:
868 if n1 != ma[f]:
869 if acceptremote:
869 if acceptremote:
870 actions[f] = ('r', None, "remote delete")
870 actions[f] = ('r', None, "remote delete")
871 else:
871 else:
872 actions[f] = ('cd', (f, None, f, False, pa.node()),
872 actions[f] = ('cd', (f, None, f, False, pa.node()),
873 "prompt changed/deleted")
873 "prompt changed/deleted")
874 elif n1[20:] == 'a':
874 elif n1[20:] == 'a':
875 # This extra 'a' is added by working copy manifest to mark
875 # This extra 'a' is added by working copy manifest to mark
876 # the file as locally added. We should forget it instead of
876 # the file as locally added. We should forget it instead of
877 # deleting it.
877 # deleting it.
878 actions[f] = ('f', None, "remote deleted")
878 actions[f] = ('f', None, "remote deleted")
879 else:
879 else:
880 actions[f] = ('r', None, "other deleted")
880 actions[f] = ('r', None, "other deleted")
881 elif n2: # file exists only on remote side
881 elif n2: # file exists only on remote side
882 if f in copied:
882 if f in copied:
883 pass # we'll deal with it on m1 side
883 pass # we'll deal with it on m1 side
884 elif f in movewithdir:
884 elif f in movewithdir:
885 f2 = movewithdir[f]
885 f2 = movewithdir[f]
886 if f2 in m1:
886 if f2 in m1:
887 actions[f2] = ('m', (f2, f, None, False, pa.node()),
887 actions[f2] = ('m', (f2, f, None, False, pa.node()),
888 "local directory rename, both created")
888 "local directory rename, both created")
889 else:
889 else:
890 actions[f2] = ('dg', (f, fl2),
890 actions[f2] = ('dg', (f, fl2),
891 "local directory rename - get from " + f)
891 "local directory rename - get from " + f)
892 elif f in copy:
892 elif f in copy:
893 f2 = copy[f]
893 f2 = copy[f]
894 if f2 in m2:
894 if f2 in m2:
895 actions[f] = ('m', (f2, f, f2, False, pa.node()),
895 actions[f] = ('m', (f2, f, f2, False, pa.node()),
896 "remote copied from " + f2)
896 "remote copied from " + f2)
897 else:
897 else:
898 actions[f] = ('m', (f2, f, f2, True, pa.node()),
898 actions[f] = ('m', (f2, f, f2, True, pa.node()),
899 "remote moved from " + f2)
899 "remote moved from " + f2)
900 elif f not in ma:
900 elif f not in ma:
901 # local unknown, remote created: the logic is described by the
901 # local unknown, remote created: the logic is described by the
902 # following table:
902 # following table:
903 #
903 #
904 # force branchmerge different | action
904 # force branchmerge different | action
905 # n * * | create
905 # n * * | create
906 # y n * | create
906 # y n * | create
907 # y y n | create
907 # y y n | create
908 # y y y | merge
908 # y y y | merge
909 #
909 #
910 # Checking whether the files are different is expensive, so we
910 # Checking whether the files are different is expensive, so we
911 # don't do that when we can avoid it.
911 # don't do that when we can avoid it.
912 if not force:
912 if not force:
913 actions[f] = ('c', (fl2,), "remote created")
913 actions[f] = ('c', (fl2,), "remote created")
914 elif not branchmerge:
914 elif not branchmerge:
915 actions[f] = ('c', (fl2,), "remote created")
915 actions[f] = ('c', (fl2,), "remote created")
916 else:
916 else:
917 actions[f] = ('cm', (fl2, pa.node()),
917 actions[f] = ('cm', (fl2, pa.node()),
918 "remote created, get or merge")
918 "remote created, get or merge")
919 elif n2 != ma[f]:
919 elif n2 != ma[f]:
920 if acceptremote:
920 if acceptremote:
921 actions[f] = ('c', (fl2,), "remote recreating")
921 actions[f] = ('c', (fl2,), "remote recreating")
922 else:
922 else:
923 actions[f] = ('dc', (None, f, f, False, pa.node()),
923 actions[f] = ('dc', (None, f, f, False, pa.node()),
924 "prompt deleted/changed")
924 "prompt deleted/changed")
925
925
926 return actions, diverge, renamedelete
926 return actions, diverge, renamedelete
927
927
928 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
928 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
929 """Resolves false conflicts where the nodeid changed but the content
929 """Resolves false conflicts where the nodeid changed but the content
930 remained the same."""
930 remained the same."""
931
931
932 for f, (m, args, msg) in actions.items():
932 for f, (m, args, msg) in actions.items():
933 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
933 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
934 # local did change but ended up with same content
934 # local did change but ended up with same content
935 actions[f] = 'r', None, "prompt same"
935 actions[f] = 'r', None, "prompt same"
936 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
936 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
937 # remote did change but ended up with same content
937 # remote did change but ended up with same content
938 del actions[f] # don't get = keep local deleted
938 del actions[f] # don't get = keep local deleted
939
939
940 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
940 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
941 acceptremote, followcopies, matcher=None,
941 acceptremote, followcopies, matcher=None,
942 mergeforce=False):
942 mergeforce=False):
943 "Calculate the actions needed to merge mctx into wctx using ancestors"
943 "Calculate the actions needed to merge mctx into wctx using ancestors"
944 if len(ancestors) == 1: # default
944 if len(ancestors) == 1: # default
945 actions, diverge, renamedelete = manifestmerge(
945 actions, diverge, renamedelete = manifestmerge(
946 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
946 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
947 acceptremote, followcopies)
947 acceptremote, followcopies)
948 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
948 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
949
949
950 else: # only when merge.preferancestor=* - the default
950 else: # only when merge.preferancestor=* - the default
951 repo.ui.note(
951 repo.ui.note(
952 _("note: merging %s and %s using bids from ancestors %s\n") %
952 _("note: merging %s and %s using bids from ancestors %s\n") %
953 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
953 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
954
954
955 # Call for bids
955 # Call for bids
956 fbids = {} # mapping filename to bids (action method to list af actions)
956 fbids = {} # mapping filename to bids (action method to list af actions)
957 diverge, renamedelete = None, None
957 diverge, renamedelete = None, None
958 for ancestor in ancestors:
958 for ancestor in ancestors:
959 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
959 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
960 actions, diverge1, renamedelete1 = manifestmerge(
960 actions, diverge1, renamedelete1 = manifestmerge(
961 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
961 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
962 acceptremote, followcopies)
962 acceptremote, followcopies)
963 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
963 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
964
964
965 # Track the shortest set of warning on the theory that bid
965 # Track the shortest set of warning on the theory that bid
966 # merge will correctly incorporate more information
966 # merge will correctly incorporate more information
967 if diverge is None or len(diverge1) < len(diverge):
967 if diverge is None or len(diverge1) < len(diverge):
968 diverge = diverge1
968 diverge = diverge1
969 if renamedelete is None or len(renamedelete) < len(renamedelete1):
969 if renamedelete is None or len(renamedelete) < len(renamedelete1):
970 renamedelete = renamedelete1
970 renamedelete = renamedelete1
971
971
972 for f, a in sorted(actions.iteritems()):
972 for f, a in sorted(actions.iteritems()):
973 m, args, msg = a
973 m, args, msg = a
974 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
974 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
975 if f in fbids:
975 if f in fbids:
976 d = fbids[f]
976 d = fbids[f]
977 if m in d:
977 if m in d:
978 d[m].append(a)
978 d[m].append(a)
979 else:
979 else:
980 d[m] = [a]
980 d[m] = [a]
981 else:
981 else:
982 fbids[f] = {m: [a]}
982 fbids[f] = {m: [a]}
983
983
984 # Pick the best bid for each file
984 # Pick the best bid for each file
985 repo.ui.note(_('\nauction for merging merge bids\n'))
985 repo.ui.note(_('\nauction for merging merge bids\n'))
986 actions = {}
986 actions = {}
987 for f, bids in sorted(fbids.items()):
987 for f, bids in sorted(fbids.items()):
988 # bids is a mapping from action method to list af actions
988 # bids is a mapping from action method to list af actions
989 # Consensus?
989 # Consensus?
990 if len(bids) == 1: # all bids are the same kind of method
990 if len(bids) == 1: # all bids are the same kind of method
991 m, l = bids.items()[0]
991 m, l = bids.items()[0]
992 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
992 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
993 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
993 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
994 actions[f] = l[0]
994 actions[f] = l[0]
995 continue
995 continue
996 # If keep is an option, just do it.
996 # If keep is an option, just do it.
997 if 'k' in bids:
997 if 'k' in bids:
998 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
998 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
999 actions[f] = bids['k'][0]
999 actions[f] = bids['k'][0]
1000 continue
1000 continue
1001 # If there are gets and they all agree [how could they not?], do it.
1001 # If there are gets and they all agree [how could they not?], do it.
1002 if 'g' in bids:
1002 if 'g' in bids:
1003 ga0 = bids['g'][0]
1003 ga0 = bids['g'][0]
1004 if all(a == ga0 for a in bids['g'][1:]):
1004 if all(a == ga0 for a in bids['g'][1:]):
1005 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1005 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1006 actions[f] = ga0
1006 actions[f] = ga0
1007 continue
1007 continue
1008 # TODO: Consider other simple actions such as mode changes
1008 # TODO: Consider other simple actions such as mode changes
1009 # Handle inefficient democrazy.
1009 # Handle inefficient democrazy.
1010 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1010 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1011 for m, l in sorted(bids.items()):
1011 for m, l in sorted(bids.items()):
1012 for _f, args, msg in l:
1012 for _f, args, msg in l:
1013 repo.ui.note(' %s -> %s\n' % (msg, m))
1013 repo.ui.note(' %s -> %s\n' % (msg, m))
1014 # Pick random action. TODO: Instead, prompt user when resolving
1014 # Pick random action. TODO: Instead, prompt user when resolving
1015 m, l = bids.items()[0]
1015 m, l = bids.items()[0]
1016 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1016 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1017 (f, m))
1017 (f, m))
1018 actions[f] = l[0]
1018 actions[f] = l[0]
1019 continue
1019 continue
1020 repo.ui.note(_('end of auction\n\n'))
1020 repo.ui.note(_('end of auction\n\n'))
1021
1021
1022 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1022 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1023
1023
1024 if wctx.rev() is None:
1024 if wctx.rev() is None:
1025 fractions = _forgetremoved(wctx, mctx, branchmerge)
1025 fractions = _forgetremoved(wctx, mctx, branchmerge)
1026 actions.update(fractions)
1026 actions.update(fractions)
1027
1027
1028 return actions, diverge, renamedelete
1028 return actions, diverge, renamedelete
1029
1029
1030 def batchremove(repo, actions):
1030 def batchremove(repo, actions):
1031 """apply removes to the working directory
1031 """apply removes to the working directory
1032
1032
1033 yields tuples for progress updates
1033 yields tuples for progress updates
1034 """
1034 """
1035 verbose = repo.ui.verbose
1035 verbose = repo.ui.verbose
1036 unlink = util.unlinkpath
1036 unlink = util.unlinkpath
1037 wjoin = repo.wjoin
1037 wjoin = repo.wjoin
1038 audit = repo.wvfs.audit
1038 audit = repo.wvfs.audit
1039 i = 0
1039 i = 0
1040 for f, args, msg in actions:
1040 for f, args, msg in actions:
1041 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1041 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1042 if verbose:
1042 if verbose:
1043 repo.ui.note(_("removing %s\n") % f)
1043 repo.ui.note(_("removing %s\n") % f)
1044 audit(f)
1044 audit(f)
1045 try:
1045 try:
1046 unlink(wjoin(f), ignoremissing=True)
1046 unlink(wjoin(f), ignoremissing=True)
1047 except OSError as inst:
1047 except OSError as inst:
1048 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1048 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1049 (f, inst.strerror))
1049 (f, inst.strerror))
1050 if i == 100:
1050 if i == 100:
1051 yield i, f
1051 yield i, f
1052 i = 0
1052 i = 0
1053 i += 1
1053 i += 1
1054 if i > 0:
1054 if i > 0:
1055 yield i, f
1055 yield i, f
1056
1056
1057 def batchget(repo, mctx, actions):
1057 def batchget(repo, mctx, actions):
1058 """apply gets to the working directory
1058 """apply gets to the working directory
1059
1059
1060 mctx is the context to get from
1060 mctx is the context to get from
1061
1061
1062 yields tuples for progress updates
1062 yields tuples for progress updates
1063 """
1063 """
1064 verbose = repo.ui.verbose
1064 verbose = repo.ui.verbose
1065 fctx = mctx.filectx
1065 fctx = mctx.filectx
1066 wwrite = repo.wwrite
1066 wwrite = repo.wwrite
1067 ui = repo.ui
1067 ui = repo.ui
1068 i = 0
1068 i = 0
1069 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1069 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1070 for f, (flags, backup), msg in actions:
1070 for f, (flags, backup), msg in actions:
1071 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1071 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1072 if verbose:
1072 if verbose:
1073 repo.ui.note(_("getting %s\n") % f)
1073 repo.ui.note(_("getting %s\n") % f)
1074
1074
1075 if backup:
1075 if backup:
1076 absf = repo.wjoin(f)
1076 absf = repo.wjoin(f)
1077 orig = scmutil.origpath(ui, repo, absf)
1077 orig = scmutil.origpath(ui, repo, absf)
1078 try:
1078 try:
1079 if repo.wvfs.isfileorlink(f):
1079 if repo.wvfs.isfileorlink(f):
1080 util.rename(absf, orig)
1080 util.rename(absf, orig)
1081 except OSError as e:
1081 except OSError as e:
1082 if e.errno != errno.ENOENT:
1082 if e.errno != errno.ENOENT:
1083 raise
1083 raise
1084
1084
1085 if repo.wvfs.isdir(f) and not repo.wvfs.islink(f):
1085 if repo.wvfs.isdir(f) and not repo.wvfs.islink(f):
1086 repo.wvfs.removedirs(f)
1086 repo.wvfs.removedirs(f)
1087 wwrite(f, fctx(f).data(), flags, backgroundclose=True)
1087 wwrite(f, fctx(f).data(), flags, backgroundclose=True)
1088 if i == 100:
1088 if i == 100:
1089 yield i, f
1089 yield i, f
1090 i = 0
1090 i = 0
1091 i += 1
1091 i += 1
1092 if i > 0:
1092 if i > 0:
1093 yield i, f
1093 yield i, f
1094
1094
1095 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1095 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1096 """apply the merge action list to the working directory
1096 """apply the merge action list to the working directory
1097
1097
1098 wctx is the working copy context
1098 wctx is the working copy context
1099 mctx is the context to be merged into the working copy
1099 mctx is the context to be merged into the working copy
1100
1100
1101 Return a tuple of counts (updated, merged, removed, unresolved) that
1101 Return a tuple of counts (updated, merged, removed, unresolved) that
1102 describes how many files were affected by the update.
1102 describes how many files were affected by the update.
1103 """
1103 """
1104
1104
1105 updated, merged, removed = 0, 0, 0
1105 updated, merged, removed = 0, 0, 0
1106 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1106 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1107 moves = []
1107 moves = []
1108 for m, l in actions.items():
1108 for m, l in actions.items():
1109 l.sort()
1109 l.sort()
1110
1110
1111 # 'cd' and 'dc' actions are treated like other merge conflicts
1111 # 'cd' and 'dc' actions are treated like other merge conflicts
1112 mergeactions = sorted(actions['cd'])
1112 mergeactions = sorted(actions['cd'])
1113 mergeactions.extend(sorted(actions['dc']))
1113 mergeactions.extend(sorted(actions['dc']))
1114 mergeactions.extend(actions['m'])
1114 mergeactions.extend(actions['m'])
1115 for f, args, msg in mergeactions:
1115 for f, args, msg in mergeactions:
1116 f1, f2, fa, move, anc = args
1116 f1, f2, fa, move, anc = args
1117 if f == '.hgsubstate': # merged internally
1117 if f == '.hgsubstate': # merged internally
1118 continue
1118 continue
1119 if f1 is None:
1119 if f1 is None:
1120 fcl = filemerge.absentfilectx(wctx, fa)
1120 fcl = filemerge.absentfilectx(wctx, fa)
1121 else:
1121 else:
1122 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1122 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1123 fcl = wctx[f1]
1123 fcl = wctx[f1]
1124 if f2 is None:
1124 if f2 is None:
1125 fco = filemerge.absentfilectx(mctx, fa)
1125 fco = filemerge.absentfilectx(mctx, fa)
1126 else:
1126 else:
1127 fco = mctx[f2]
1127 fco = mctx[f2]
1128 actx = repo[anc]
1128 actx = repo[anc]
1129 if fa in actx:
1129 if fa in actx:
1130 fca = actx[fa]
1130 fca = actx[fa]
1131 else:
1131 else:
1132 # TODO: move to absentfilectx
1132 # TODO: move to absentfilectx
1133 fca = repo.filectx(f1, fileid=nullrev)
1133 fca = repo.filectx(f1, fileid=nullrev)
1134 ms.add(fcl, fco, fca, f)
1134 ms.add(fcl, fco, fca, f)
1135 if f1 != f and move:
1135 if f1 != f and move:
1136 moves.append(f1)
1136 moves.append(f1)
1137
1137
1138 audit = repo.wvfs.audit
1138 audit = repo.wvfs.audit
1139 _updating = _('updating')
1139 _updating = _('updating')
1140 _files = _('files')
1140 _files = _('files')
1141 progress = repo.ui.progress
1141 progress = repo.ui.progress
1142
1142
1143 # remove renamed files after safely stored
1143 # remove renamed files after safely stored
1144 for f in moves:
1144 for f in moves:
1145 if os.path.lexists(repo.wjoin(f)):
1145 if os.path.lexists(repo.wjoin(f)):
1146 repo.ui.debug("removing %s\n" % f)
1146 repo.ui.debug("removing %s\n" % f)
1147 audit(f)
1147 audit(f)
1148 util.unlinkpath(repo.wjoin(f))
1148 util.unlinkpath(repo.wjoin(f))
1149
1149
1150 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1150 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1151
1151
1152 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1152 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1153 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1153 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1154
1154
1155 # remove in parallel (must come first)
1155 # remove in parallel (must come first)
1156 z = 0
1156 z = 0
1157 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1157 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1158 for i, item in prog:
1158 for i, item in prog:
1159 z += i
1159 z += i
1160 progress(_updating, z, item=item, total=numupdates, unit=_files)
1160 progress(_updating, z, item=item, total=numupdates, unit=_files)
1161 removed = len(actions['r'])
1161 removed = len(actions['r'])
1162
1162
1163 # get in parallel
1163 # get in parallel
1164 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1164 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1165 for i, item in prog:
1165 for i, item in prog:
1166 z += i
1166 z += i
1167 progress(_updating, z, item=item, total=numupdates, unit=_files)
1167 progress(_updating, z, item=item, total=numupdates, unit=_files)
1168 updated = len(actions['g'])
1168 updated = len(actions['g'])
1169
1169
1170 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1170 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1171 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1171 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1172
1172
1173 # forget (manifest only, just log it) (must come first)
1173 # forget (manifest only, just log it) (must come first)
1174 for f, args, msg in actions['f']:
1174 for f, args, msg in actions['f']:
1175 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1175 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1176 z += 1
1176 z += 1
1177 progress(_updating, z, item=f, total=numupdates, unit=_files)
1177 progress(_updating, z, item=f, total=numupdates, unit=_files)
1178
1178
1179 # re-add (manifest only, just log it)
1179 # re-add (manifest only, just log it)
1180 for f, args, msg in actions['a']:
1180 for f, args, msg in actions['a']:
1181 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1181 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1182 z += 1
1182 z += 1
1183 progress(_updating, z, item=f, total=numupdates, unit=_files)
1183 progress(_updating, z, item=f, total=numupdates, unit=_files)
1184
1184
1185 # re-add/mark as modified (manifest only, just log it)
1185 # re-add/mark as modified (manifest only, just log it)
1186 for f, args, msg in actions['am']:
1186 for f, args, msg in actions['am']:
1187 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1187 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1188 z += 1
1188 z += 1
1189 progress(_updating, z, item=f, total=numupdates, unit=_files)
1189 progress(_updating, z, item=f, total=numupdates, unit=_files)
1190
1190
1191 # keep (noop, just log it)
1191 # keep (noop, just log it)
1192 for f, args, msg in actions['k']:
1192 for f, args, msg in actions['k']:
1193 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1193 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1194 # no progress
1194 # no progress
1195
1195
1196 # directory rename, move local
1196 # directory rename, move local
1197 for f, args, msg in actions['dm']:
1197 for f, args, msg in actions['dm']:
1198 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1198 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1199 z += 1
1199 z += 1
1200 progress(_updating, z, item=f, total=numupdates, unit=_files)
1200 progress(_updating, z, item=f, total=numupdates, unit=_files)
1201 f0, flags = args
1201 f0, flags = args
1202 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1202 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1203 audit(f)
1203 audit(f)
1204 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1204 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1205 util.unlinkpath(repo.wjoin(f0))
1205 util.unlinkpath(repo.wjoin(f0))
1206 updated += 1
1206 updated += 1
1207
1207
1208 # local directory rename, get
1208 # local directory rename, get
1209 for f, args, msg in actions['dg']:
1209 for f, args, msg in actions['dg']:
1210 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1210 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1211 z += 1
1211 z += 1
1212 progress(_updating, z, item=f, total=numupdates, unit=_files)
1212 progress(_updating, z, item=f, total=numupdates, unit=_files)
1213 f0, flags = args
1213 f0, flags = args
1214 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1214 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1215 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1215 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1216 updated += 1
1216 updated += 1
1217
1217
1218 # exec
1218 # exec
1219 for f, args, msg in actions['e']:
1219 for f, args, msg in actions['e']:
1220 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1220 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1221 z += 1
1221 z += 1
1222 progress(_updating, z, item=f, total=numupdates, unit=_files)
1222 progress(_updating, z, item=f, total=numupdates, unit=_files)
1223 flags, = args
1223 flags, = args
1224 audit(f)
1224 audit(f)
1225 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1225 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1226 updated += 1
1226 updated += 1
1227
1227
1228 # the ordering is important here -- ms.mergedriver will raise if the merge
1228 # the ordering is important here -- ms.mergedriver will raise if the merge
1229 # driver has changed, and we want to be able to bypass it when overwrite is
1229 # driver has changed, and we want to be able to bypass it when overwrite is
1230 # True
1230 # True
1231 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1231 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1232
1232
1233 if usemergedriver:
1233 if usemergedriver:
1234 ms.commit()
1234 ms.commit()
1235 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1235 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1236 # the driver might leave some files unresolved
1236 # the driver might leave some files unresolved
1237 unresolvedf = set(ms.unresolved())
1237 unresolvedf = set(ms.unresolved())
1238 if not proceed:
1238 if not proceed:
1239 # XXX setting unresolved to at least 1 is a hack to make sure we
1239 # XXX setting unresolved to at least 1 is a hack to make sure we
1240 # error out
1240 # error out
1241 return updated, merged, removed, max(len(unresolvedf), 1)
1241 return updated, merged, removed, max(len(unresolvedf), 1)
1242 newactions = []
1242 newactions = []
1243 for f, args, msg in mergeactions:
1243 for f, args, msg in mergeactions:
1244 if f in unresolvedf:
1244 if f in unresolvedf:
1245 newactions.append((f, args, msg))
1245 newactions.append((f, args, msg))
1246 mergeactions = newactions
1246 mergeactions = newactions
1247
1247
1248 # premerge
1248 # premerge
1249 tocomplete = []
1249 tocomplete = []
1250 for f, args, msg in mergeactions:
1250 for f, args, msg in mergeactions:
1251 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1251 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1252 z += 1
1252 z += 1
1253 progress(_updating, z, item=f, total=numupdates, unit=_files)
1253 progress(_updating, z, item=f, total=numupdates, unit=_files)
1254 if f == '.hgsubstate': # subrepo states need updating
1254 if f == '.hgsubstate': # subrepo states need updating
1255 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1255 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1256 overwrite)
1256 overwrite, labels)
1257 continue
1257 continue
1258 audit(f)
1258 audit(f)
1259 complete, r = ms.preresolve(f, wctx)
1259 complete, r = ms.preresolve(f, wctx)
1260 if not complete:
1260 if not complete:
1261 numupdates += 1
1261 numupdates += 1
1262 tocomplete.append((f, args, msg))
1262 tocomplete.append((f, args, msg))
1263
1263
1264 # merge
1264 # merge
1265 for f, args, msg in tocomplete:
1265 for f, args, msg in tocomplete:
1266 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1266 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1267 z += 1
1267 z += 1
1268 progress(_updating, z, item=f, total=numupdates, unit=_files)
1268 progress(_updating, z, item=f, total=numupdates, unit=_files)
1269 ms.resolve(f, wctx)
1269 ms.resolve(f, wctx)
1270
1270
1271 ms.commit()
1271 ms.commit()
1272
1272
1273 unresolved = ms.unresolvedcount()
1273 unresolved = ms.unresolvedcount()
1274
1274
1275 if usemergedriver and not unresolved and ms.mdstate() != 's':
1275 if usemergedriver and not unresolved and ms.mdstate() != 's':
1276 if not driverconclude(repo, ms, wctx, labels=labels):
1276 if not driverconclude(repo, ms, wctx, labels=labels):
1277 # XXX setting unresolved to at least 1 is a hack to make sure we
1277 # XXX setting unresolved to at least 1 is a hack to make sure we
1278 # error out
1278 # error out
1279 unresolved = max(unresolved, 1)
1279 unresolved = max(unresolved, 1)
1280
1280
1281 ms.commit()
1281 ms.commit()
1282
1282
1283 msupdated, msmerged, msremoved = ms.counts()
1283 msupdated, msmerged, msremoved = ms.counts()
1284 updated += msupdated
1284 updated += msupdated
1285 merged += msmerged
1285 merged += msmerged
1286 removed += msremoved
1286 removed += msremoved
1287
1287
1288 extraactions = ms.actions()
1288 extraactions = ms.actions()
1289 if extraactions:
1289 if extraactions:
1290 mfiles = set(a[0] for a in actions['m'])
1290 mfiles = set(a[0] for a in actions['m'])
1291 for k, acts in extraactions.iteritems():
1291 for k, acts in extraactions.iteritems():
1292 actions[k].extend(acts)
1292 actions[k].extend(acts)
1293 # Remove these files from actions['m'] as well. This is important
1293 # Remove these files from actions['m'] as well. This is important
1294 # because in recordupdates, files in actions['m'] are processed
1294 # because in recordupdates, files in actions['m'] are processed
1295 # after files in other actions, and the merge driver might add
1295 # after files in other actions, and the merge driver might add
1296 # files to those actions via extraactions above. This can lead to a
1296 # files to those actions via extraactions above. This can lead to a
1297 # file being recorded twice, with poor results. This is especially
1297 # file being recorded twice, with poor results. This is especially
1298 # problematic for actions['r'] (currently only possible with the
1298 # problematic for actions['r'] (currently only possible with the
1299 # merge driver in the initial merge process; interrupted merges
1299 # merge driver in the initial merge process; interrupted merges
1300 # don't go through this flow).
1300 # don't go through this flow).
1301 #
1301 #
1302 # The real fix here is to have indexes by both file and action so
1302 # The real fix here is to have indexes by both file and action so
1303 # that when the action for a file is changed it is automatically
1303 # that when the action for a file is changed it is automatically
1304 # reflected in the other action lists. But that involves a more
1304 # reflected in the other action lists. But that involves a more
1305 # complex data structure, so this will do for now.
1305 # complex data structure, so this will do for now.
1306 #
1306 #
1307 # We don't need to do the same operation for 'dc' and 'cd' because
1307 # We don't need to do the same operation for 'dc' and 'cd' because
1308 # those lists aren't consulted again.
1308 # those lists aren't consulted again.
1309 mfiles.difference_update(a[0] for a in acts)
1309 mfiles.difference_update(a[0] for a in acts)
1310
1310
1311 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1311 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1312
1312
1313 progress(_updating, None, total=numupdates, unit=_files)
1313 progress(_updating, None, total=numupdates, unit=_files)
1314
1314
1315 return updated, merged, removed, unresolved
1315 return updated, merged, removed, unresolved
1316
1316
1317 def recordupdates(repo, actions, branchmerge):
1317 def recordupdates(repo, actions, branchmerge):
1318 "record merge actions to the dirstate"
1318 "record merge actions to the dirstate"
1319 # remove (must come first)
1319 # remove (must come first)
1320 for f, args, msg in actions.get('r', []):
1320 for f, args, msg in actions.get('r', []):
1321 if branchmerge:
1321 if branchmerge:
1322 repo.dirstate.remove(f)
1322 repo.dirstate.remove(f)
1323 else:
1323 else:
1324 repo.dirstate.drop(f)
1324 repo.dirstate.drop(f)
1325
1325
1326 # forget (must come first)
1326 # forget (must come first)
1327 for f, args, msg in actions.get('f', []):
1327 for f, args, msg in actions.get('f', []):
1328 repo.dirstate.drop(f)
1328 repo.dirstate.drop(f)
1329
1329
1330 # re-add
1330 # re-add
1331 for f, args, msg in actions.get('a', []):
1331 for f, args, msg in actions.get('a', []):
1332 repo.dirstate.add(f)
1332 repo.dirstate.add(f)
1333
1333
1334 # re-add/mark as modified
1334 # re-add/mark as modified
1335 for f, args, msg in actions.get('am', []):
1335 for f, args, msg in actions.get('am', []):
1336 if branchmerge:
1336 if branchmerge:
1337 repo.dirstate.normallookup(f)
1337 repo.dirstate.normallookup(f)
1338 else:
1338 else:
1339 repo.dirstate.add(f)
1339 repo.dirstate.add(f)
1340
1340
1341 # exec change
1341 # exec change
1342 for f, args, msg in actions.get('e', []):
1342 for f, args, msg in actions.get('e', []):
1343 repo.dirstate.normallookup(f)
1343 repo.dirstate.normallookup(f)
1344
1344
1345 # keep
1345 # keep
1346 for f, args, msg in actions.get('k', []):
1346 for f, args, msg in actions.get('k', []):
1347 pass
1347 pass
1348
1348
1349 # get
1349 # get
1350 for f, args, msg in actions.get('g', []):
1350 for f, args, msg in actions.get('g', []):
1351 if branchmerge:
1351 if branchmerge:
1352 repo.dirstate.otherparent(f)
1352 repo.dirstate.otherparent(f)
1353 else:
1353 else:
1354 repo.dirstate.normal(f)
1354 repo.dirstate.normal(f)
1355
1355
1356 # merge
1356 # merge
1357 for f, args, msg in actions.get('m', []):
1357 for f, args, msg in actions.get('m', []):
1358 f1, f2, fa, move, anc = args
1358 f1, f2, fa, move, anc = args
1359 if branchmerge:
1359 if branchmerge:
1360 # We've done a branch merge, mark this file as merged
1360 # We've done a branch merge, mark this file as merged
1361 # so that we properly record the merger later
1361 # so that we properly record the merger later
1362 repo.dirstate.merge(f)
1362 repo.dirstate.merge(f)
1363 if f1 != f2: # copy/rename
1363 if f1 != f2: # copy/rename
1364 if move:
1364 if move:
1365 repo.dirstate.remove(f1)
1365 repo.dirstate.remove(f1)
1366 if f1 != f:
1366 if f1 != f:
1367 repo.dirstate.copy(f1, f)
1367 repo.dirstate.copy(f1, f)
1368 else:
1368 else:
1369 repo.dirstate.copy(f2, f)
1369 repo.dirstate.copy(f2, f)
1370 else:
1370 else:
1371 # We've update-merged a locally modified file, so
1371 # We've update-merged a locally modified file, so
1372 # we set the dirstate to emulate a normal checkout
1372 # we set the dirstate to emulate a normal checkout
1373 # of that file some time in the past. Thus our
1373 # of that file some time in the past. Thus our
1374 # merge will appear as a normal local file
1374 # merge will appear as a normal local file
1375 # modification.
1375 # modification.
1376 if f2 == f: # file not locally copied/moved
1376 if f2 == f: # file not locally copied/moved
1377 repo.dirstate.normallookup(f)
1377 repo.dirstate.normallookup(f)
1378 if move:
1378 if move:
1379 repo.dirstate.drop(f1)
1379 repo.dirstate.drop(f1)
1380
1380
1381 # directory rename, move local
1381 # directory rename, move local
1382 for f, args, msg in actions.get('dm', []):
1382 for f, args, msg in actions.get('dm', []):
1383 f0, flag = args
1383 f0, flag = args
1384 if branchmerge:
1384 if branchmerge:
1385 repo.dirstate.add(f)
1385 repo.dirstate.add(f)
1386 repo.dirstate.remove(f0)
1386 repo.dirstate.remove(f0)
1387 repo.dirstate.copy(f0, f)
1387 repo.dirstate.copy(f0, f)
1388 else:
1388 else:
1389 repo.dirstate.normal(f)
1389 repo.dirstate.normal(f)
1390 repo.dirstate.drop(f0)
1390 repo.dirstate.drop(f0)
1391
1391
1392 # directory rename, get
1392 # directory rename, get
1393 for f, args, msg in actions.get('dg', []):
1393 for f, args, msg in actions.get('dg', []):
1394 f0, flag = args
1394 f0, flag = args
1395 if branchmerge:
1395 if branchmerge:
1396 repo.dirstate.add(f)
1396 repo.dirstate.add(f)
1397 repo.dirstate.copy(f0, f)
1397 repo.dirstate.copy(f0, f)
1398 else:
1398 else:
1399 repo.dirstate.normal(f)
1399 repo.dirstate.normal(f)
1400
1400
1401 def update(repo, node, branchmerge, force, ancestor=None,
1401 def update(repo, node, branchmerge, force, ancestor=None,
1402 mergeancestor=False, labels=None, matcher=None, mergeforce=False):
1402 mergeancestor=False, labels=None, matcher=None, mergeforce=False):
1403 """
1403 """
1404 Perform a merge between the working directory and the given node
1404 Perform a merge between the working directory and the given node
1405
1405
1406 node = the node to update to, or None if unspecified
1406 node = the node to update to, or None if unspecified
1407 branchmerge = whether to merge between branches
1407 branchmerge = whether to merge between branches
1408 force = whether to force branch merging or file overwriting
1408 force = whether to force branch merging or file overwriting
1409 matcher = a matcher to filter file lists (dirstate not updated)
1409 matcher = a matcher to filter file lists (dirstate not updated)
1410 mergeancestor = whether it is merging with an ancestor. If true,
1410 mergeancestor = whether it is merging with an ancestor. If true,
1411 we should accept the incoming changes for any prompts that occur.
1411 we should accept the incoming changes for any prompts that occur.
1412 If false, merging with an ancestor (fast-forward) is only allowed
1412 If false, merging with an ancestor (fast-forward) is only allowed
1413 between different named branches. This flag is used by rebase extension
1413 between different named branches. This flag is used by rebase extension
1414 as a temporary fix and should be avoided in general.
1414 as a temporary fix and should be avoided in general.
1415 labels = labels to use for base, local and other
1415 labels = labels to use for base, local and other
1416 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1416 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1417 this is True, then 'force' should be True as well.
1417 this is True, then 'force' should be True as well.
1418
1418
1419 The table below shows all the behaviors of the update command
1419 The table below shows all the behaviors of the update command
1420 given the -c and -C or no options, whether the working directory
1420 given the -c and -C or no options, whether the working directory
1421 is dirty, whether a revision is specified, and the relationship of
1421 is dirty, whether a revision is specified, and the relationship of
1422 the parent rev to the target rev (linear, on the same named
1422 the parent rev to the target rev (linear, on the same named
1423 branch, or on another named branch).
1423 branch, or on another named branch).
1424
1424
1425 This logic is tested by test-update-branches.t.
1425 This logic is tested by test-update-branches.t.
1426
1426
1427 -c -C dirty rev | linear same cross
1427 -c -C dirty rev | linear same cross
1428 n n n n | ok (1) x
1428 n n n n | ok (1) x
1429 n n n y | ok ok ok
1429 n n n y | ok ok ok
1430 n n y n | merge (2) (2)
1430 n n y n | merge (2) (2)
1431 n n y y | merge (3) (3)
1431 n n y y | merge (3) (3)
1432 n y * * | discard discard discard
1432 n y * * | discard discard discard
1433 y n y * | (4) (4) (4)
1433 y n y * | (4) (4) (4)
1434 y n n * | ok ok ok
1434 y n n * | ok ok ok
1435 y y * * | (5) (5) (5)
1435 y y * * | (5) (5) (5)
1436
1436
1437 x = can't happen
1437 x = can't happen
1438 * = don't-care
1438 * = don't-care
1439 1 = abort: not a linear update (merge or update --check to force update)
1439 1 = abort: not a linear update (merge or update --check to force update)
1440 2 = abort: uncommitted changes (commit and merge, or update --clean to
1440 2 = abort: uncommitted changes (commit and merge, or update --clean to
1441 discard changes)
1441 discard changes)
1442 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1442 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1443 4 = abort: uncommitted changes (checked in commands.py)
1443 4 = abort: uncommitted changes (checked in commands.py)
1444 5 = incompatible options (checked in commands.py)
1444 5 = incompatible options (checked in commands.py)
1445
1445
1446 Return the same tuple as applyupdates().
1446 Return the same tuple as applyupdates().
1447 """
1447 """
1448
1448
1449 onode = node
1449 onode = node
1450 # If we're doing a partial update, we need to skip updating
1450 # If we're doing a partial update, we need to skip updating
1451 # the dirstate, so make a note of any partial-ness to the
1451 # the dirstate, so make a note of any partial-ness to the
1452 # update here.
1452 # update here.
1453 if matcher is None or matcher.always():
1453 if matcher is None or matcher.always():
1454 partial = False
1454 partial = False
1455 else:
1455 else:
1456 partial = True
1456 partial = True
1457 with repo.wlock():
1457 with repo.wlock():
1458 wc = repo[None]
1458 wc = repo[None]
1459 pl = wc.parents()
1459 pl = wc.parents()
1460 p1 = pl[0]
1460 p1 = pl[0]
1461 pas = [None]
1461 pas = [None]
1462 if ancestor is not None:
1462 if ancestor is not None:
1463 pas = [repo[ancestor]]
1463 pas = [repo[ancestor]]
1464
1464
1465 if node is None:
1465 if node is None:
1466 repo.ui.deprecwarn('update with no target', '3.9')
1466 repo.ui.deprecwarn('update with no target', '3.9')
1467 rev, _mark, _act = destutil.destupdate(repo)
1467 rev, _mark, _act = destutil.destupdate(repo)
1468 node = repo[rev].node()
1468 node = repo[rev].node()
1469
1469
1470 overwrite = force and not branchmerge
1470 overwrite = force and not branchmerge
1471
1471
1472 p2 = repo[node]
1472 p2 = repo[node]
1473 if pas[0] is None:
1473 if pas[0] is None:
1474 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1474 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1475 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1475 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1476 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1476 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1477 else:
1477 else:
1478 pas = [p1.ancestor(p2, warn=branchmerge)]
1478 pas = [p1.ancestor(p2, warn=branchmerge)]
1479
1479
1480 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1480 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1481
1481
1482 ### check phase
1482 ### check phase
1483 if not overwrite:
1483 if not overwrite:
1484 if len(pl) > 1:
1484 if len(pl) > 1:
1485 raise error.Abort(_("outstanding uncommitted merge"))
1485 raise error.Abort(_("outstanding uncommitted merge"))
1486 ms = mergestate.read(repo)
1486 ms = mergestate.read(repo)
1487 if list(ms.unresolved()):
1487 if list(ms.unresolved()):
1488 raise error.Abort(_("outstanding merge conflicts"))
1488 raise error.Abort(_("outstanding merge conflicts"))
1489 if branchmerge:
1489 if branchmerge:
1490 if pas == [p2]:
1490 if pas == [p2]:
1491 raise error.Abort(_("merging with a working directory ancestor"
1491 raise error.Abort(_("merging with a working directory ancestor"
1492 " has no effect"))
1492 " has no effect"))
1493 elif pas == [p1]:
1493 elif pas == [p1]:
1494 if not mergeancestor and p1.branch() == p2.branch():
1494 if not mergeancestor and p1.branch() == p2.branch():
1495 raise error.Abort(_("nothing to merge"),
1495 raise error.Abort(_("nothing to merge"),
1496 hint=_("use 'hg update' "
1496 hint=_("use 'hg update' "
1497 "or check 'hg heads'"))
1497 "or check 'hg heads'"))
1498 if not force and (wc.files() or wc.deleted()):
1498 if not force and (wc.files() or wc.deleted()):
1499 raise error.Abort(_("uncommitted changes"),
1499 raise error.Abort(_("uncommitted changes"),
1500 hint=_("use 'hg status' to list changes"))
1500 hint=_("use 'hg status' to list changes"))
1501 for s in sorted(wc.substate):
1501 for s in sorted(wc.substate):
1502 wc.sub(s).bailifchanged()
1502 wc.sub(s).bailifchanged()
1503
1503
1504 elif not overwrite:
1504 elif not overwrite:
1505 if p1 == p2: # no-op update
1505 if p1 == p2: # no-op update
1506 # call the hooks and exit early
1506 # call the hooks and exit early
1507 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1507 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1508 repo.hook('update', parent1=xp2, parent2='', error=0)
1508 repo.hook('update', parent1=xp2, parent2='', error=0)
1509 return 0, 0, 0, 0
1509 return 0, 0, 0, 0
1510
1510
1511 if pas not in ([p1], [p2]): # nonlinear
1511 if pas not in ([p1], [p2]): # nonlinear
1512 dirty = wc.dirty(missing=True)
1512 dirty = wc.dirty(missing=True)
1513 if dirty or onode is None:
1513 if dirty or onode is None:
1514 # Branching is a bit strange to ensure we do the minimal
1514 # Branching is a bit strange to ensure we do the minimal
1515 # amount of call to obsolete.background.
1515 # amount of call to obsolete.background.
1516 foreground = obsolete.foreground(repo, [p1.node()])
1516 foreground = obsolete.foreground(repo, [p1.node()])
1517 # note: the <node> variable contains a random identifier
1517 # note: the <node> variable contains a random identifier
1518 if repo[node].node() in foreground:
1518 if repo[node].node() in foreground:
1519 pas = [p1] # allow updating to successors
1519 pas = [p1] # allow updating to successors
1520 elif dirty:
1520 elif dirty:
1521 msg = _("uncommitted changes")
1521 msg = _("uncommitted changes")
1522 if onode is None:
1522 if onode is None:
1523 hint = _("commit and merge, or update --clean to"
1523 hint = _("commit and merge, or update --clean to"
1524 " discard changes")
1524 " discard changes")
1525 else:
1525 else:
1526 hint = _("commit or update --clean to discard"
1526 hint = _("commit or update --clean to discard"
1527 " changes")
1527 " changes")
1528 raise error.Abort(msg, hint=hint)
1528 raise error.Abort(msg, hint=hint)
1529 else: # node is none
1529 else: # node is none
1530 msg = _("not a linear update")
1530 msg = _("not a linear update")
1531 hint = _("merge or update --check to force update")
1531 hint = _("merge or update --check to force update")
1532 raise error.Abort(msg, hint=hint)
1532 raise error.Abort(msg, hint=hint)
1533 else:
1533 else:
1534 # Allow jumping branches if clean and specific rev given
1534 # Allow jumping branches if clean and specific rev given
1535 pas = [p1]
1535 pas = [p1]
1536
1536
1537 # deprecated config: merge.followcopies
1537 # deprecated config: merge.followcopies
1538 followcopies = False
1538 followcopies = False
1539 if overwrite:
1539 if overwrite:
1540 pas = [wc]
1540 pas = [wc]
1541 elif pas == [p2]: # backwards
1541 elif pas == [p2]: # backwards
1542 pas = [wc.p1()]
1542 pas = [wc.p1()]
1543 elif not branchmerge and not wc.dirty(missing=True):
1543 elif not branchmerge and not wc.dirty(missing=True):
1544 pass
1544 pass
1545 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1545 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1546 followcopies = True
1546 followcopies = True
1547
1547
1548 ### calculate phase
1548 ### calculate phase
1549 actionbyfile, diverge, renamedelete = calculateupdates(
1549 actionbyfile, diverge, renamedelete = calculateupdates(
1550 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1550 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1551 followcopies, matcher=matcher, mergeforce=mergeforce)
1551 followcopies, matcher=matcher, mergeforce=mergeforce)
1552
1552
1553 # Prompt and create actions. Most of this is in the resolve phase
1553 # Prompt and create actions. Most of this is in the resolve phase
1554 # already, but we can't handle .hgsubstate in filemerge or
1554 # already, but we can't handle .hgsubstate in filemerge or
1555 # subrepo.submerge yet so we have to keep prompting for it.
1555 # subrepo.submerge yet so we have to keep prompting for it.
1556 if '.hgsubstate' in actionbyfile:
1556 if '.hgsubstate' in actionbyfile:
1557 f = '.hgsubstate'
1557 f = '.hgsubstate'
1558 m, args, msg = actionbyfile[f]
1558 m, args, msg = actionbyfile[f]
1559 prompts = filemerge.partextras(labels)
1559 prompts = filemerge.partextras(labels)
1560 prompts['f'] = f
1560 prompts['f'] = f
1561 if m == 'cd':
1561 if m == 'cd':
1562 if repo.ui.promptchoice(
1562 if repo.ui.promptchoice(
1563 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1563 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1564 "use (c)hanged version or (d)elete?"
1564 "use (c)hanged version or (d)elete?"
1565 "$$ &Changed $$ &Delete") % prompts, 0):
1565 "$$ &Changed $$ &Delete") % prompts, 0):
1566 actionbyfile[f] = ('r', None, "prompt delete")
1566 actionbyfile[f] = ('r', None, "prompt delete")
1567 elif f in p1:
1567 elif f in p1:
1568 actionbyfile[f] = ('am', None, "prompt keep")
1568 actionbyfile[f] = ('am', None, "prompt keep")
1569 else:
1569 else:
1570 actionbyfile[f] = ('a', None, "prompt keep")
1570 actionbyfile[f] = ('a', None, "prompt keep")
1571 elif m == 'dc':
1571 elif m == 'dc':
1572 f1, f2, fa, move, anc = args
1572 f1, f2, fa, move, anc = args
1573 flags = p2[f2].flags()
1573 flags = p2[f2].flags()
1574 if repo.ui.promptchoice(
1574 if repo.ui.promptchoice(
1575 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1575 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1576 "use (c)hanged version or leave (d)eleted?"
1576 "use (c)hanged version or leave (d)eleted?"
1577 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1577 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1578 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1578 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1579 else:
1579 else:
1580 del actionbyfile[f]
1580 del actionbyfile[f]
1581
1581
1582 # Convert to dictionary-of-lists format
1582 # Convert to dictionary-of-lists format
1583 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1583 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1584 for f, (m, args, msg) in actionbyfile.iteritems():
1584 for f, (m, args, msg) in actionbyfile.iteritems():
1585 if m not in actions:
1585 if m not in actions:
1586 actions[m] = []
1586 actions[m] = []
1587 actions[m].append((f, args, msg))
1587 actions[m].append((f, args, msg))
1588
1588
1589 if not util.fscasesensitive(repo.path):
1589 if not util.fscasesensitive(repo.path):
1590 # check collision between files only in p2 for clean update
1590 # check collision between files only in p2 for clean update
1591 if (not branchmerge and
1591 if (not branchmerge and
1592 (force or not wc.dirty(missing=True, branch=False))):
1592 (force or not wc.dirty(missing=True, branch=False))):
1593 _checkcollision(repo, p2.manifest(), None)
1593 _checkcollision(repo, p2.manifest(), None)
1594 else:
1594 else:
1595 _checkcollision(repo, wc.manifest(), actions)
1595 _checkcollision(repo, wc.manifest(), actions)
1596
1596
1597 # divergent renames
1597 # divergent renames
1598 for f, fl in sorted(diverge.iteritems()):
1598 for f, fl in sorted(diverge.iteritems()):
1599 repo.ui.warn(_("note: possible conflict - %s was renamed "
1599 repo.ui.warn(_("note: possible conflict - %s was renamed "
1600 "multiple times to:\n") % f)
1600 "multiple times to:\n") % f)
1601 for nf in fl:
1601 for nf in fl:
1602 repo.ui.warn(" %s\n" % nf)
1602 repo.ui.warn(" %s\n" % nf)
1603
1603
1604 # rename and delete
1604 # rename and delete
1605 for f, fl in sorted(renamedelete.iteritems()):
1605 for f, fl in sorted(renamedelete.iteritems()):
1606 repo.ui.warn(_("note: possible conflict - %s was deleted "
1606 repo.ui.warn(_("note: possible conflict - %s was deleted "
1607 "and renamed to:\n") % f)
1607 "and renamed to:\n") % f)
1608 for nf in fl:
1608 for nf in fl:
1609 repo.ui.warn(" %s\n" % nf)
1609 repo.ui.warn(" %s\n" % nf)
1610
1610
1611 ### apply phase
1611 ### apply phase
1612 if not branchmerge: # just jump to the new rev
1612 if not branchmerge: # just jump to the new rev
1613 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1613 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1614 if not partial:
1614 if not partial:
1615 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1615 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1616 # note that we're in the middle of an update
1616 # note that we're in the middle of an update
1617 repo.vfs.write('updatestate', p2.hex())
1617 repo.vfs.write('updatestate', p2.hex())
1618
1618
1619 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1619 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1620
1620
1621 if not partial:
1621 if not partial:
1622 repo.dirstate.beginparentchange()
1622 repo.dirstate.beginparentchange()
1623 repo.setparents(fp1, fp2)
1623 repo.setparents(fp1, fp2)
1624 recordupdates(repo, actions, branchmerge)
1624 recordupdates(repo, actions, branchmerge)
1625 # update completed, clear state
1625 # update completed, clear state
1626 util.unlink(repo.join('updatestate'))
1626 util.unlink(repo.join('updatestate'))
1627
1627
1628 if not branchmerge:
1628 if not branchmerge:
1629 repo.dirstate.setbranch(p2.branch())
1629 repo.dirstate.setbranch(p2.branch())
1630 repo.dirstate.endparentchange()
1630 repo.dirstate.endparentchange()
1631
1631
1632 if not partial:
1632 if not partial:
1633 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1633 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1634 return stats
1634 return stats
1635
1635
1636 def graft(repo, ctx, pctx, labels, keepparent=False):
1636 def graft(repo, ctx, pctx, labels, keepparent=False):
1637 """Do a graft-like merge.
1637 """Do a graft-like merge.
1638
1638
1639 This is a merge where the merge ancestor is chosen such that one
1639 This is a merge where the merge ancestor is chosen such that one
1640 or more changesets are grafted onto the current changeset. In
1640 or more changesets are grafted onto the current changeset. In
1641 addition to the merge, this fixes up the dirstate to include only
1641 addition to the merge, this fixes up the dirstate to include only
1642 a single parent (if keepparent is False) and tries to duplicate any
1642 a single parent (if keepparent is False) and tries to duplicate any
1643 renames/copies appropriately.
1643 renames/copies appropriately.
1644
1644
1645 ctx - changeset to rebase
1645 ctx - changeset to rebase
1646 pctx - merge base, usually ctx.p1()
1646 pctx - merge base, usually ctx.p1()
1647 labels - merge labels eg ['local', 'graft']
1647 labels - merge labels eg ['local', 'graft']
1648 keepparent - keep second parent if any
1648 keepparent - keep second parent if any
1649
1649
1650 """
1650 """
1651 # If we're grafting a descendant onto an ancestor, be sure to pass
1651 # If we're grafting a descendant onto an ancestor, be sure to pass
1652 # mergeancestor=True to update. This does two things: 1) allows the merge if
1652 # mergeancestor=True to update. This does two things: 1) allows the merge if
1653 # the destination is the same as the parent of the ctx (so we can use graft
1653 # the destination is the same as the parent of the ctx (so we can use graft
1654 # to copy commits), and 2) informs update that the incoming changes are
1654 # to copy commits), and 2) informs update that the incoming changes are
1655 # newer than the destination so it doesn't prompt about "remote changed foo
1655 # newer than the destination so it doesn't prompt about "remote changed foo
1656 # which local deleted".
1656 # which local deleted".
1657 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1657 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1658
1658
1659 stats = update(repo, ctx.node(), True, True, pctx.node(),
1659 stats = update(repo, ctx.node(), True, True, pctx.node(),
1660 mergeancestor=mergeancestor, labels=labels)
1660 mergeancestor=mergeancestor, labels=labels)
1661
1661
1662 pother = nullid
1662 pother = nullid
1663 parents = ctx.parents()
1663 parents = ctx.parents()
1664 if keepparent and len(parents) == 2 and pctx in parents:
1664 if keepparent and len(parents) == 2 and pctx in parents:
1665 parents.remove(pctx)
1665 parents.remove(pctx)
1666 pother = parents[0].node()
1666 pother = parents[0].node()
1667
1667
1668 repo.dirstate.beginparentchange()
1668 repo.dirstate.beginparentchange()
1669 repo.setparents(repo['.'].node(), pother)
1669 repo.setparents(repo['.'].node(), pother)
1670 repo.dirstate.write(repo.currenttransaction())
1670 repo.dirstate.write(repo.currenttransaction())
1671 # fix up dirstate for copies and renames
1671 # fix up dirstate for copies and renames
1672 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1672 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1673 repo.dirstate.endparentchange()
1673 repo.dirstate.endparentchange()
1674 return stats
1674 return stats
@@ -1,1948 +1,1958 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 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 copy
10 import copy
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import posixpath
14 import posixpath
15 import re
15 import re
16 import stat
16 import stat
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tarfile
19 import tarfile
20 import xml.dom.minidom
20 import xml.dom.minidom
21
21
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 cmdutil,
25 cmdutil,
26 config,
26 config,
27 error,
27 error,
28 exchange,
28 exchange,
29 filemerge,
29 match as matchmod,
30 match as matchmod,
30 node,
31 node,
31 pathutil,
32 pathutil,
32 phases,
33 phases,
33 scmutil,
34 scmutil,
34 util,
35 util,
35 )
36 )
36
37
37 hg = None
38 hg = None
38 propertycache = util.propertycache
39 propertycache = util.propertycache
39
40
40 nullstate = ('', '', 'empty')
41 nullstate = ('', '', 'empty')
41
42
42 def _expandedabspath(path):
43 def _expandedabspath(path):
43 '''
44 '''
44 get a path or url and if it is a path expand it and return an absolute path
45 get a path or url and if it is a path expand it and return an absolute path
45 '''
46 '''
46 expandedpath = util.urllocalpath(util.expandpath(path))
47 expandedpath = util.urllocalpath(util.expandpath(path))
47 u = util.url(expandedpath)
48 u = util.url(expandedpath)
48 if not u.scheme:
49 if not u.scheme:
49 path = util.normpath(os.path.abspath(u.path))
50 path = util.normpath(os.path.abspath(u.path))
50 return path
51 return path
51
52
52 def _getstorehashcachename(remotepath):
53 def _getstorehashcachename(remotepath):
53 '''get a unique filename for the store hash cache of a remote repository'''
54 '''get a unique filename for the store hash cache of a remote repository'''
54 return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
55 return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
55
56
56 class SubrepoAbort(error.Abort):
57 class SubrepoAbort(error.Abort):
57 """Exception class used to avoid handling a subrepo error more than once"""
58 """Exception class used to avoid handling a subrepo error more than once"""
58 def __init__(self, *args, **kw):
59 def __init__(self, *args, **kw):
59 self.subrepo = kw.pop('subrepo', None)
60 self.subrepo = kw.pop('subrepo', None)
60 self.cause = kw.pop('cause', None)
61 self.cause = kw.pop('cause', None)
61 error.Abort.__init__(self, *args, **kw)
62 error.Abort.__init__(self, *args, **kw)
62
63
63 def annotatesubrepoerror(func):
64 def annotatesubrepoerror(func):
64 def decoratedmethod(self, *args, **kargs):
65 def decoratedmethod(self, *args, **kargs):
65 try:
66 try:
66 res = func(self, *args, **kargs)
67 res = func(self, *args, **kargs)
67 except SubrepoAbort as ex:
68 except SubrepoAbort as ex:
68 # This exception has already been handled
69 # This exception has already been handled
69 raise ex
70 raise ex
70 except error.Abort as ex:
71 except error.Abort as ex:
71 subrepo = subrelpath(self)
72 subrepo = subrelpath(self)
72 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
73 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
73 # avoid handling this exception by raising a SubrepoAbort exception
74 # avoid handling this exception by raising a SubrepoAbort exception
74 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
75 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
75 cause=sys.exc_info())
76 cause=sys.exc_info())
76 return res
77 return res
77 return decoratedmethod
78 return decoratedmethod
78
79
79 def state(ctx, ui):
80 def state(ctx, ui):
80 """return a state dict, mapping subrepo paths configured in .hgsub
81 """return a state dict, mapping subrepo paths configured in .hgsub
81 to tuple: (source from .hgsub, revision from .hgsubstate, kind
82 to tuple: (source from .hgsub, revision from .hgsubstate, kind
82 (key in types dict))
83 (key in types dict))
83 """
84 """
84 p = config.config()
85 p = config.config()
85 repo = ctx.repo()
86 repo = ctx.repo()
86 def read(f, sections=None, remap=None):
87 def read(f, sections=None, remap=None):
87 if f in ctx:
88 if f in ctx:
88 try:
89 try:
89 data = ctx[f].data()
90 data = ctx[f].data()
90 except IOError as err:
91 except IOError as err:
91 if err.errno != errno.ENOENT:
92 if err.errno != errno.ENOENT:
92 raise
93 raise
93 # handle missing subrepo spec files as removed
94 # handle missing subrepo spec files as removed
94 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
95 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
95 repo.pathto(f))
96 repo.pathto(f))
96 return
97 return
97 p.parse(f, data, sections, remap, read)
98 p.parse(f, data, sections, remap, read)
98 else:
99 else:
99 raise error.Abort(_("subrepo spec file \'%s\' not found") %
100 raise error.Abort(_("subrepo spec file \'%s\' not found") %
100 repo.pathto(f))
101 repo.pathto(f))
101 if '.hgsub' in ctx:
102 if '.hgsub' in ctx:
102 read('.hgsub')
103 read('.hgsub')
103
104
104 for path, src in ui.configitems('subpaths'):
105 for path, src in ui.configitems('subpaths'):
105 p.set('subpaths', path, src, ui.configsource('subpaths', path))
106 p.set('subpaths', path, src, ui.configsource('subpaths', path))
106
107
107 rev = {}
108 rev = {}
108 if '.hgsubstate' in ctx:
109 if '.hgsubstate' in ctx:
109 try:
110 try:
110 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
111 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
111 l = l.lstrip()
112 l = l.lstrip()
112 if not l:
113 if not l:
113 continue
114 continue
114 try:
115 try:
115 revision, path = l.split(" ", 1)
116 revision, path = l.split(" ", 1)
116 except ValueError:
117 except ValueError:
117 raise error.Abort(_("invalid subrepository revision "
118 raise error.Abort(_("invalid subrepository revision "
118 "specifier in \'%s\' line %d")
119 "specifier in \'%s\' line %d")
119 % (repo.pathto('.hgsubstate'), (i + 1)))
120 % (repo.pathto('.hgsubstate'), (i + 1)))
120 rev[path] = revision
121 rev[path] = revision
121 except IOError as err:
122 except IOError as err:
122 if err.errno != errno.ENOENT:
123 if err.errno != errno.ENOENT:
123 raise
124 raise
124
125
125 def remap(src):
126 def remap(src):
126 for pattern, repl in p.items('subpaths'):
127 for pattern, repl in p.items('subpaths'):
127 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
128 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
128 # does a string decode.
129 # does a string decode.
129 repl = repl.encode('string-escape')
130 repl = repl.encode('string-escape')
130 # However, we still want to allow back references to go
131 # However, we still want to allow back references to go
131 # through unharmed, so we turn r'\\1' into r'\1'. Again,
132 # through unharmed, so we turn r'\\1' into r'\1'. Again,
132 # extra escapes are needed because re.sub string decodes.
133 # extra escapes are needed because re.sub string decodes.
133 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
134 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
134 try:
135 try:
135 src = re.sub(pattern, repl, src, 1)
136 src = re.sub(pattern, repl, src, 1)
136 except re.error as e:
137 except re.error as e:
137 raise error.Abort(_("bad subrepository pattern in %s: %s")
138 raise error.Abort(_("bad subrepository pattern in %s: %s")
138 % (p.source('subpaths', pattern), e))
139 % (p.source('subpaths', pattern), e))
139 return src
140 return src
140
141
141 state = {}
142 state = {}
142 for path, src in p[''].items():
143 for path, src in p[''].items():
143 kind = 'hg'
144 kind = 'hg'
144 if src.startswith('['):
145 if src.startswith('['):
145 if ']' not in src:
146 if ']' not in src:
146 raise error.Abort(_('missing ] in subrepo source'))
147 raise error.Abort(_('missing ] in subrepo source'))
147 kind, src = src.split(']', 1)
148 kind, src = src.split(']', 1)
148 kind = kind[1:]
149 kind = kind[1:]
149 src = src.lstrip() # strip any extra whitespace after ']'
150 src = src.lstrip() # strip any extra whitespace after ']'
150
151
151 if not util.url(src).isabs():
152 if not util.url(src).isabs():
152 parent = _abssource(repo, abort=False)
153 parent = _abssource(repo, abort=False)
153 if parent:
154 if parent:
154 parent = util.url(parent)
155 parent = util.url(parent)
155 parent.path = posixpath.join(parent.path or '', src)
156 parent.path = posixpath.join(parent.path or '', src)
156 parent.path = posixpath.normpath(parent.path)
157 parent.path = posixpath.normpath(parent.path)
157 joined = str(parent)
158 joined = str(parent)
158 # Remap the full joined path and use it if it changes,
159 # Remap the full joined path and use it if it changes,
159 # else remap the original source.
160 # else remap the original source.
160 remapped = remap(joined)
161 remapped = remap(joined)
161 if remapped == joined:
162 if remapped == joined:
162 src = remap(src)
163 src = remap(src)
163 else:
164 else:
164 src = remapped
165 src = remapped
165
166
166 src = remap(src)
167 src = remap(src)
167 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
168 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
168
169
169 return state
170 return state
170
171
171 def writestate(repo, state):
172 def writestate(repo, state):
172 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
173 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
173 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
174 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
174 if state[s][1] != nullstate[1]]
175 if state[s][1] != nullstate[1]]
175 repo.wwrite('.hgsubstate', ''.join(lines), '')
176 repo.wwrite('.hgsubstate', ''.join(lines), '')
176
177
177 def submerge(repo, wctx, mctx, actx, overwrite):
178 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
178 """delegated from merge.applyupdates: merging of .hgsubstate file
179 """delegated from merge.applyupdates: merging of .hgsubstate file
179 in working context, merging context and ancestor context"""
180 in working context, merging context and ancestor context"""
180 if mctx == actx: # backwards?
181 if mctx == actx: # backwards?
181 actx = wctx.p1()
182 actx = wctx.p1()
182 s1 = wctx.substate
183 s1 = wctx.substate
183 s2 = mctx.substate
184 s2 = mctx.substate
184 sa = actx.substate
185 sa = actx.substate
185 sm = {}
186 sm = {}
186
187
187 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
188 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
188
189
189 def debug(s, msg, r=""):
190 def debug(s, msg, r=""):
190 if r:
191 if r:
191 r = "%s:%s:%s" % r
192 r = "%s:%s:%s" % r
192 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
193 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
193
194
194 for s, l in sorted(s1.iteritems()):
195 for s, l in sorted(s1.iteritems()):
195 a = sa.get(s, nullstate)
196 a = sa.get(s, nullstate)
196 ld = l # local state with possible dirty flag for compares
197 ld = l # local state with possible dirty flag for compares
197 if wctx.sub(s).dirty():
198 if wctx.sub(s).dirty():
198 ld = (l[0], l[1] + "+")
199 ld = (l[0], l[1] + "+")
199 if wctx == actx: # overwrite
200 if wctx == actx: # overwrite
200 a = ld
201 a = ld
201
202
202 if s in s2:
203 if s in s2:
204 prompts = filemerge.partextras(labels)
205 prompts['s'] = s
203 r = s2[s]
206 r = s2[s]
204 if ld == r or r == a: # no change or local is newer
207 if ld == r or r == a: # no change or local is newer
205 sm[s] = l
208 sm[s] = l
206 continue
209 continue
207 elif ld == a: # other side changed
210 elif ld == a: # other side changed
208 debug(s, "other changed, get", r)
211 debug(s, "other changed, get", r)
209 wctx.sub(s).get(r, overwrite)
212 wctx.sub(s).get(r, overwrite)
210 sm[s] = r
213 sm[s] = r
211 elif ld[0] != r[0]: # sources differ
214 elif ld[0] != r[0]: # sources differ
215 prompts['lo'] = l[0]
216 prompts['ro'] = r[0]
212 if repo.ui.promptchoice(
217 if repo.ui.promptchoice(
213 _(' subrepository sources for %s differ\n'
218 _(' subrepository sources for %(s)s differ\n'
214 'use (l)ocal source (%s) or (r)emote source (%s)?'
219 'use (l)ocal%(l)s source (%(lo)s)'
215 '$$ &Local $$ &Remote') % (s, l[0], r[0]), 0):
220 ' or (r)emote%(o)s source (%(ro)s)?'
221 '$$ &Local $$ &Remote') % prompts, 0):
216 debug(s, "prompt changed, get", r)
222 debug(s, "prompt changed, get", r)
217 wctx.sub(s).get(r, overwrite)
223 wctx.sub(s).get(r, overwrite)
218 sm[s] = r
224 sm[s] = r
219 elif ld[1] == a[1]: # local side is unchanged
225 elif ld[1] == a[1]: # local side is unchanged
220 debug(s, "other side changed, get", r)
226 debug(s, "other side changed, get", r)
221 wctx.sub(s).get(r, overwrite)
227 wctx.sub(s).get(r, overwrite)
222 sm[s] = r
228 sm[s] = r
223 else:
229 else:
224 debug(s, "both sides changed")
230 debug(s, "both sides changed")
225 srepo = wctx.sub(s)
231 srepo = wctx.sub(s)
232 prompts['sl'] = srepo.shortid(l[1])
233 prompts['sr'] = srepo.shortid(r[1])
226 option = repo.ui.promptchoice(
234 option = repo.ui.promptchoice(
227 _(' subrepository %s diverged (local revision: %s, '
235 _(' subrepository %(s)s diverged (local revision: %(sl)s, '
228 'remote revision: %s)\n'
236 'remote revision: %(sr)s)\n'
229 '(M)erge, keep (l)ocal or keep (r)emote?'
237 '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?'
230 '$$ &Merge $$ &Local $$ &Remote')
238 '$$ &Merge $$ &Local $$ &Remote')
231 % (s, srepo.shortid(l[1]), srepo.shortid(r[1])), 0)
239 % prompts, 0)
232 if option == 0:
240 if option == 0:
233 wctx.sub(s).merge(r)
241 wctx.sub(s).merge(r)
234 sm[s] = l
242 sm[s] = l
235 debug(s, "merge with", r)
243 debug(s, "merge with", r)
236 elif option == 1:
244 elif option == 1:
237 sm[s] = l
245 sm[s] = l
238 debug(s, "keep local subrepo revision", l)
246 debug(s, "keep local subrepo revision", l)
239 else:
247 else:
240 wctx.sub(s).get(r, overwrite)
248 wctx.sub(s).get(r, overwrite)
241 sm[s] = r
249 sm[s] = r
242 debug(s, "get remote subrepo revision", r)
250 debug(s, "get remote subrepo revision", r)
243 elif ld == a: # remote removed, local unchanged
251 elif ld == a: # remote removed, local unchanged
244 debug(s, "remote removed, remove")
252 debug(s, "remote removed, remove")
245 wctx.sub(s).remove()
253 wctx.sub(s).remove()
246 elif a == nullstate: # not present in remote or ancestor
254 elif a == nullstate: # not present in remote or ancestor
247 debug(s, "local added, keep")
255 debug(s, "local added, keep")
248 sm[s] = l
256 sm[s] = l
249 continue
257 continue
250 else:
258 else:
251 if repo.ui.promptchoice(
259 if repo.ui.promptchoice(
252 _(' local changed subrepository %s which remote removed\n'
260 _(' local%(l)s changed subrepository %(s)s'
261 ' which remote%(o)s removed\n'
253 'use (c)hanged version or (d)elete?'
262 'use (c)hanged version or (d)elete?'
254 '$$ &Changed $$ &Delete') % s, 0):
263 '$$ &Changed $$ &Delete') % prompts, 0):
255 debug(s, "prompt remove")
264 debug(s, "prompt remove")
256 wctx.sub(s).remove()
265 wctx.sub(s).remove()
257
266
258 for s, r in sorted(s2.items()):
267 for s, r in sorted(s2.items()):
259 if s in s1:
268 if s in s1:
260 continue
269 continue
261 elif s not in sa:
270 elif s not in sa:
262 debug(s, "remote added, get", r)
271 debug(s, "remote added, get", r)
263 mctx.sub(s).get(r)
272 mctx.sub(s).get(r)
264 sm[s] = r
273 sm[s] = r
265 elif r != sa[s]:
274 elif r != sa[s]:
266 if repo.ui.promptchoice(
275 if repo.ui.promptchoice(
267 _(' remote changed subrepository %s which local removed\n'
276 _(' remote%(o)s changed subrepository %(s)s'
277 ' which local%(l)s removed\n'
268 'use (c)hanged version or (d)elete?'
278 'use (c)hanged version or (d)elete?'
269 '$$ &Changed $$ &Delete') % s, 0) == 0:
279 '$$ &Changed $$ &Delete') % prompts, 0) == 0:
270 debug(s, "prompt recreate", r)
280 debug(s, "prompt recreate", r)
271 mctx.sub(s).get(r)
281 mctx.sub(s).get(r)
272 sm[s] = r
282 sm[s] = r
273
283
274 # record merged .hgsubstate
284 # record merged .hgsubstate
275 writestate(repo, sm)
285 writestate(repo, sm)
276 return sm
286 return sm
277
287
278 def _updateprompt(ui, sub, dirty, local, remote):
288 def _updateprompt(ui, sub, dirty, local, remote):
279 if dirty:
289 if dirty:
280 msg = (_(' subrepository sources for %s differ\n'
290 msg = (_(' subrepository sources for %s differ\n'
281 'use (l)ocal source (%s) or (r)emote source (%s)?'
291 'use (l)ocal source (%s) or (r)emote source (%s)?'
282 '$$ &Local $$ &Remote')
292 '$$ &Local $$ &Remote')
283 % (subrelpath(sub), local, remote))
293 % (subrelpath(sub), local, remote))
284 else:
294 else:
285 msg = (_(' subrepository sources for %s differ (in checked out '
295 msg = (_(' subrepository sources for %s differ (in checked out '
286 'version)\n'
296 'version)\n'
287 'use (l)ocal source (%s) or (r)emote source (%s)?'
297 'use (l)ocal source (%s) or (r)emote source (%s)?'
288 '$$ &Local $$ &Remote')
298 '$$ &Local $$ &Remote')
289 % (subrelpath(sub), local, remote))
299 % (subrelpath(sub), local, remote))
290 return ui.promptchoice(msg, 0)
300 return ui.promptchoice(msg, 0)
291
301
292 def reporelpath(repo):
302 def reporelpath(repo):
293 """return path to this (sub)repo as seen from outermost repo"""
303 """return path to this (sub)repo as seen from outermost repo"""
294 parent = repo
304 parent = repo
295 while util.safehasattr(parent, '_subparent'):
305 while util.safehasattr(parent, '_subparent'):
296 parent = parent._subparent
306 parent = parent._subparent
297 return repo.root[len(pathutil.normasprefix(parent.root)):]
307 return repo.root[len(pathutil.normasprefix(parent.root)):]
298
308
299 def subrelpath(sub):
309 def subrelpath(sub):
300 """return path to this subrepo as seen from outermost repo"""
310 """return path to this subrepo as seen from outermost repo"""
301 return sub._relpath
311 return sub._relpath
302
312
303 def _abssource(repo, push=False, abort=True):
313 def _abssource(repo, push=False, abort=True):
304 """return pull/push path of repo - either based on parent repo .hgsub info
314 """return pull/push path of repo - either based on parent repo .hgsub info
305 or on the top repo config. Abort or return None if no source found."""
315 or on the top repo config. Abort or return None if no source found."""
306 if util.safehasattr(repo, '_subparent'):
316 if util.safehasattr(repo, '_subparent'):
307 source = util.url(repo._subsource)
317 source = util.url(repo._subsource)
308 if source.isabs():
318 if source.isabs():
309 return str(source)
319 return str(source)
310 source.path = posixpath.normpath(source.path)
320 source.path = posixpath.normpath(source.path)
311 parent = _abssource(repo._subparent, push, abort=False)
321 parent = _abssource(repo._subparent, push, abort=False)
312 if parent:
322 if parent:
313 parent = util.url(util.pconvert(parent))
323 parent = util.url(util.pconvert(parent))
314 parent.path = posixpath.join(parent.path or '', source.path)
324 parent.path = posixpath.join(parent.path or '', source.path)
315 parent.path = posixpath.normpath(parent.path)
325 parent.path = posixpath.normpath(parent.path)
316 return str(parent)
326 return str(parent)
317 else: # recursion reached top repo
327 else: # recursion reached top repo
318 if util.safehasattr(repo, '_subtoppath'):
328 if util.safehasattr(repo, '_subtoppath'):
319 return repo._subtoppath
329 return repo._subtoppath
320 if push and repo.ui.config('paths', 'default-push'):
330 if push and repo.ui.config('paths', 'default-push'):
321 return repo.ui.config('paths', 'default-push')
331 return repo.ui.config('paths', 'default-push')
322 if repo.ui.config('paths', 'default'):
332 if repo.ui.config('paths', 'default'):
323 return repo.ui.config('paths', 'default')
333 return repo.ui.config('paths', 'default')
324 if repo.shared():
334 if repo.shared():
325 # chop off the .hg component to get the default path form
335 # chop off the .hg component to get the default path form
326 return os.path.dirname(repo.sharedpath)
336 return os.path.dirname(repo.sharedpath)
327 if abort:
337 if abort:
328 raise error.Abort(_("default path for subrepository not found"))
338 raise error.Abort(_("default path for subrepository not found"))
329
339
330 def _sanitize(ui, vfs, ignore):
340 def _sanitize(ui, vfs, ignore):
331 for dirname, dirs, names in vfs.walk():
341 for dirname, dirs, names in vfs.walk():
332 for i, d in enumerate(dirs):
342 for i, d in enumerate(dirs):
333 if d.lower() == ignore:
343 if d.lower() == ignore:
334 del dirs[i]
344 del dirs[i]
335 break
345 break
336 if vfs.basename(dirname).lower() != '.hg':
346 if vfs.basename(dirname).lower() != '.hg':
337 continue
347 continue
338 for f in names:
348 for f in names:
339 if f.lower() == 'hgrc':
349 if f.lower() == 'hgrc':
340 ui.warn(_("warning: removing potentially hostile 'hgrc' "
350 ui.warn(_("warning: removing potentially hostile 'hgrc' "
341 "in '%s'\n") % vfs.join(dirname))
351 "in '%s'\n") % vfs.join(dirname))
342 vfs.unlink(vfs.reljoin(dirname, f))
352 vfs.unlink(vfs.reljoin(dirname, f))
343
353
344 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
354 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
345 """return instance of the right subrepo class for subrepo in path"""
355 """return instance of the right subrepo class for subrepo in path"""
346 # subrepo inherently violates our import layering rules
356 # subrepo inherently violates our import layering rules
347 # because it wants to make repo objects from deep inside the stack
357 # because it wants to make repo objects from deep inside the stack
348 # so we manually delay the circular imports to not break
358 # so we manually delay the circular imports to not break
349 # scripts that don't use our demand-loading
359 # scripts that don't use our demand-loading
350 global hg
360 global hg
351 from . import hg as h
361 from . import hg as h
352 hg = h
362 hg = h
353
363
354 pathutil.pathauditor(ctx.repo().root)(path)
364 pathutil.pathauditor(ctx.repo().root)(path)
355 state = ctx.substate[path]
365 state = ctx.substate[path]
356 if state[2] not in types:
366 if state[2] not in types:
357 raise error.Abort(_('unknown subrepo type %s') % state[2])
367 raise error.Abort(_('unknown subrepo type %s') % state[2])
358 if allowwdir:
368 if allowwdir:
359 state = (state[0], ctx.subrev(path), state[2])
369 state = (state[0], ctx.subrev(path), state[2])
360 return types[state[2]](ctx, path, state[:2], allowcreate)
370 return types[state[2]](ctx, path, state[:2], allowcreate)
361
371
362 def nullsubrepo(ctx, path, pctx):
372 def nullsubrepo(ctx, path, pctx):
363 """return an empty subrepo in pctx for the extant subrepo in ctx"""
373 """return an empty subrepo in pctx for the extant subrepo in ctx"""
364 # subrepo inherently violates our import layering rules
374 # subrepo inherently violates our import layering rules
365 # because it wants to make repo objects from deep inside the stack
375 # because it wants to make repo objects from deep inside the stack
366 # so we manually delay the circular imports to not break
376 # so we manually delay the circular imports to not break
367 # scripts that don't use our demand-loading
377 # scripts that don't use our demand-loading
368 global hg
378 global hg
369 from . import hg as h
379 from . import hg as h
370 hg = h
380 hg = h
371
381
372 pathutil.pathauditor(ctx.repo().root)(path)
382 pathutil.pathauditor(ctx.repo().root)(path)
373 state = ctx.substate[path]
383 state = ctx.substate[path]
374 if state[2] not in types:
384 if state[2] not in types:
375 raise error.Abort(_('unknown subrepo type %s') % state[2])
385 raise error.Abort(_('unknown subrepo type %s') % state[2])
376 subrev = ''
386 subrev = ''
377 if state[2] == 'hg':
387 if state[2] == 'hg':
378 subrev = "0" * 40
388 subrev = "0" * 40
379 return types[state[2]](pctx, path, (state[0], subrev), True)
389 return types[state[2]](pctx, path, (state[0], subrev), True)
380
390
381 def newcommitphase(ui, ctx):
391 def newcommitphase(ui, ctx):
382 commitphase = phases.newcommitphase(ui)
392 commitphase = phases.newcommitphase(ui)
383 substate = getattr(ctx, "substate", None)
393 substate = getattr(ctx, "substate", None)
384 if not substate:
394 if not substate:
385 return commitphase
395 return commitphase
386 check = ui.config('phases', 'checksubrepos', 'follow')
396 check = ui.config('phases', 'checksubrepos', 'follow')
387 if check not in ('ignore', 'follow', 'abort'):
397 if check not in ('ignore', 'follow', 'abort'):
388 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
398 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
389 % (check))
399 % (check))
390 if check == 'ignore':
400 if check == 'ignore':
391 return commitphase
401 return commitphase
392 maxphase = phases.public
402 maxphase = phases.public
393 maxsub = None
403 maxsub = None
394 for s in sorted(substate):
404 for s in sorted(substate):
395 sub = ctx.sub(s)
405 sub = ctx.sub(s)
396 subphase = sub.phase(substate[s][1])
406 subphase = sub.phase(substate[s][1])
397 if maxphase < subphase:
407 if maxphase < subphase:
398 maxphase = subphase
408 maxphase = subphase
399 maxsub = s
409 maxsub = s
400 if commitphase < maxphase:
410 if commitphase < maxphase:
401 if check == 'abort':
411 if check == 'abort':
402 raise error.Abort(_("can't commit in %s phase"
412 raise error.Abort(_("can't commit in %s phase"
403 " conflicting %s from subrepository %s") %
413 " conflicting %s from subrepository %s") %
404 (phases.phasenames[commitphase],
414 (phases.phasenames[commitphase],
405 phases.phasenames[maxphase], maxsub))
415 phases.phasenames[maxphase], maxsub))
406 ui.warn(_("warning: changes are committed in"
416 ui.warn(_("warning: changes are committed in"
407 " %s phase from subrepository %s\n") %
417 " %s phase from subrepository %s\n") %
408 (phases.phasenames[maxphase], maxsub))
418 (phases.phasenames[maxphase], maxsub))
409 return maxphase
419 return maxphase
410 return commitphase
420 return commitphase
411
421
412 # subrepo classes need to implement the following abstract class:
422 # subrepo classes need to implement the following abstract class:
413
423
414 class abstractsubrepo(object):
424 class abstractsubrepo(object):
415
425
416 def __init__(self, ctx, path):
426 def __init__(self, ctx, path):
417 """Initialize abstractsubrepo part
427 """Initialize abstractsubrepo part
418
428
419 ``ctx`` is the context referring this subrepository in the
429 ``ctx`` is the context referring this subrepository in the
420 parent repository.
430 parent repository.
421
431
422 ``path`` is the path to this subrepository as seen from
432 ``path`` is the path to this subrepository as seen from
423 innermost repository.
433 innermost repository.
424 """
434 """
425 self.ui = ctx.repo().ui
435 self.ui = ctx.repo().ui
426 self._ctx = ctx
436 self._ctx = ctx
427 self._path = path
437 self._path = path
428
438
429 def storeclean(self, path):
439 def storeclean(self, path):
430 """
440 """
431 returns true if the repository has not changed since it was last
441 returns true if the repository has not changed since it was last
432 cloned from or pushed to a given repository.
442 cloned from or pushed to a given repository.
433 """
443 """
434 return False
444 return False
435
445
436 def dirty(self, ignoreupdate=False):
446 def dirty(self, ignoreupdate=False):
437 """returns true if the dirstate of the subrepo is dirty or does not
447 """returns true if the dirstate of the subrepo is dirty or does not
438 match current stored state. If ignoreupdate is true, only check
448 match current stored state. If ignoreupdate is true, only check
439 whether the subrepo has uncommitted changes in its dirstate.
449 whether the subrepo has uncommitted changes in its dirstate.
440 """
450 """
441 raise NotImplementedError
451 raise NotImplementedError
442
452
443 def dirtyreason(self, ignoreupdate=False):
453 def dirtyreason(self, ignoreupdate=False):
444 """return reason string if it is ``dirty()``
454 """return reason string if it is ``dirty()``
445
455
446 Returned string should have enough information for the message
456 Returned string should have enough information for the message
447 of exception.
457 of exception.
448
458
449 This returns None, otherwise.
459 This returns None, otherwise.
450 """
460 """
451 if self.dirty(ignoreupdate=ignoreupdate):
461 if self.dirty(ignoreupdate=ignoreupdate):
452 return _("uncommitted changes in subrepository '%s'"
462 return _("uncommitted changes in subrepository '%s'"
453 ) % subrelpath(self)
463 ) % subrelpath(self)
454
464
455 def bailifchanged(self, ignoreupdate=False):
465 def bailifchanged(self, ignoreupdate=False):
456 """raise Abort if subrepository is ``dirty()``
466 """raise Abort if subrepository is ``dirty()``
457 """
467 """
458 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
468 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
459 if dirtyreason:
469 if dirtyreason:
460 raise error.Abort(dirtyreason)
470 raise error.Abort(dirtyreason)
461
471
462 def basestate(self):
472 def basestate(self):
463 """current working directory base state, disregarding .hgsubstate
473 """current working directory base state, disregarding .hgsubstate
464 state and working directory modifications"""
474 state and working directory modifications"""
465 raise NotImplementedError
475 raise NotImplementedError
466
476
467 def checknested(self, path):
477 def checknested(self, path):
468 """check if path is a subrepository within this repository"""
478 """check if path is a subrepository within this repository"""
469 return False
479 return False
470
480
471 def commit(self, text, user, date):
481 def commit(self, text, user, date):
472 """commit the current changes to the subrepo with the given
482 """commit the current changes to the subrepo with the given
473 log message. Use given user and date if possible. Return the
483 log message. Use given user and date if possible. Return the
474 new state of the subrepo.
484 new state of the subrepo.
475 """
485 """
476 raise NotImplementedError
486 raise NotImplementedError
477
487
478 def phase(self, state):
488 def phase(self, state):
479 """returns phase of specified state in the subrepository.
489 """returns phase of specified state in the subrepository.
480 """
490 """
481 return phases.public
491 return phases.public
482
492
483 def remove(self):
493 def remove(self):
484 """remove the subrepo
494 """remove the subrepo
485
495
486 (should verify the dirstate is not dirty first)
496 (should verify the dirstate is not dirty first)
487 """
497 """
488 raise NotImplementedError
498 raise NotImplementedError
489
499
490 def get(self, state, overwrite=False):
500 def get(self, state, overwrite=False):
491 """run whatever commands are needed to put the subrepo into
501 """run whatever commands are needed to put the subrepo into
492 this state
502 this state
493 """
503 """
494 raise NotImplementedError
504 raise NotImplementedError
495
505
496 def merge(self, state):
506 def merge(self, state):
497 """merge currently-saved state with the new state."""
507 """merge currently-saved state with the new state."""
498 raise NotImplementedError
508 raise NotImplementedError
499
509
500 def push(self, opts):
510 def push(self, opts):
501 """perform whatever action is analogous to 'hg push'
511 """perform whatever action is analogous to 'hg push'
502
512
503 This may be a no-op on some systems.
513 This may be a no-op on some systems.
504 """
514 """
505 raise NotImplementedError
515 raise NotImplementedError
506
516
507 def add(self, ui, match, prefix, explicitonly, **opts):
517 def add(self, ui, match, prefix, explicitonly, **opts):
508 return []
518 return []
509
519
510 def addremove(self, matcher, prefix, opts, dry_run, similarity):
520 def addremove(self, matcher, prefix, opts, dry_run, similarity):
511 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
521 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
512 return 1
522 return 1
513
523
514 def cat(self, match, prefix, **opts):
524 def cat(self, match, prefix, **opts):
515 return 1
525 return 1
516
526
517 def status(self, rev2, **opts):
527 def status(self, rev2, **opts):
518 return scmutil.status([], [], [], [], [], [], [])
528 return scmutil.status([], [], [], [], [], [], [])
519
529
520 def diff(self, ui, diffopts, node2, match, prefix, **opts):
530 def diff(self, ui, diffopts, node2, match, prefix, **opts):
521 pass
531 pass
522
532
523 def outgoing(self, ui, dest, opts):
533 def outgoing(self, ui, dest, opts):
524 return 1
534 return 1
525
535
526 def incoming(self, ui, source, opts):
536 def incoming(self, ui, source, opts):
527 return 1
537 return 1
528
538
529 def files(self):
539 def files(self):
530 """return filename iterator"""
540 """return filename iterator"""
531 raise NotImplementedError
541 raise NotImplementedError
532
542
533 def filedata(self, name):
543 def filedata(self, name):
534 """return file data"""
544 """return file data"""
535 raise NotImplementedError
545 raise NotImplementedError
536
546
537 def fileflags(self, name):
547 def fileflags(self, name):
538 """return file flags"""
548 """return file flags"""
539 return ''
549 return ''
540
550
541 def getfileset(self, expr):
551 def getfileset(self, expr):
542 """Resolve the fileset expression for this repo"""
552 """Resolve the fileset expression for this repo"""
543 return set()
553 return set()
544
554
545 def printfiles(self, ui, m, fm, fmt, subrepos):
555 def printfiles(self, ui, m, fm, fmt, subrepos):
546 """handle the files command for this subrepo"""
556 """handle the files command for this subrepo"""
547 return 1
557 return 1
548
558
549 def archive(self, archiver, prefix, match=None):
559 def archive(self, archiver, prefix, match=None):
550 if match is not None:
560 if match is not None:
551 files = [f for f in self.files() if match(f)]
561 files = [f for f in self.files() if match(f)]
552 else:
562 else:
553 files = self.files()
563 files = self.files()
554 total = len(files)
564 total = len(files)
555 relpath = subrelpath(self)
565 relpath = subrelpath(self)
556 self.ui.progress(_('archiving (%s)') % relpath, 0,
566 self.ui.progress(_('archiving (%s)') % relpath, 0,
557 unit=_('files'), total=total)
567 unit=_('files'), total=total)
558 for i, name in enumerate(files):
568 for i, name in enumerate(files):
559 flags = self.fileflags(name)
569 flags = self.fileflags(name)
560 mode = 'x' in flags and 0o755 or 0o644
570 mode = 'x' in flags and 0o755 or 0o644
561 symlink = 'l' in flags
571 symlink = 'l' in flags
562 archiver.addfile(prefix + self._path + '/' + name,
572 archiver.addfile(prefix + self._path + '/' + name,
563 mode, symlink, self.filedata(name))
573 mode, symlink, self.filedata(name))
564 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
574 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
565 unit=_('files'), total=total)
575 unit=_('files'), total=total)
566 self.ui.progress(_('archiving (%s)') % relpath, None)
576 self.ui.progress(_('archiving (%s)') % relpath, None)
567 return total
577 return total
568
578
569 def walk(self, match):
579 def walk(self, match):
570 '''
580 '''
571 walk recursively through the directory tree, finding all files
581 walk recursively through the directory tree, finding all files
572 matched by the match function
582 matched by the match function
573 '''
583 '''
574 pass
584 pass
575
585
576 def forget(self, match, prefix):
586 def forget(self, match, prefix):
577 return ([], [])
587 return ([], [])
578
588
579 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
589 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
580 """remove the matched files from the subrepository and the filesystem,
590 """remove the matched files from the subrepository and the filesystem,
581 possibly by force and/or after the file has been removed from the
591 possibly by force and/or after the file has been removed from the
582 filesystem. Return 0 on success, 1 on any warning.
592 filesystem. Return 0 on success, 1 on any warning.
583 """
593 """
584 warnings.append(_("warning: removefiles not implemented (%s)")
594 warnings.append(_("warning: removefiles not implemented (%s)")
585 % self._path)
595 % self._path)
586 return 1
596 return 1
587
597
588 def revert(self, substate, *pats, **opts):
598 def revert(self, substate, *pats, **opts):
589 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
599 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
590 % (substate[0], substate[2]))
600 % (substate[0], substate[2]))
591 return []
601 return []
592
602
593 def shortid(self, revid):
603 def shortid(self, revid):
594 return revid
604 return revid
595
605
596 def verify(self):
606 def verify(self):
597 '''verify the integrity of the repository. Return 0 on success or
607 '''verify the integrity of the repository. Return 0 on success or
598 warning, 1 on any error.
608 warning, 1 on any error.
599 '''
609 '''
600 return 0
610 return 0
601
611
602 @propertycache
612 @propertycache
603 def wvfs(self):
613 def wvfs(self):
604 """return vfs to access the working directory of this subrepository
614 """return vfs to access the working directory of this subrepository
605 """
615 """
606 return scmutil.vfs(self._ctx.repo().wvfs.join(self._path))
616 return scmutil.vfs(self._ctx.repo().wvfs.join(self._path))
607
617
608 @propertycache
618 @propertycache
609 def _relpath(self):
619 def _relpath(self):
610 """return path to this subrepository as seen from outermost repository
620 """return path to this subrepository as seen from outermost repository
611 """
621 """
612 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
622 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
613
623
614 class hgsubrepo(abstractsubrepo):
624 class hgsubrepo(abstractsubrepo):
615 def __init__(self, ctx, path, state, allowcreate):
625 def __init__(self, ctx, path, state, allowcreate):
616 super(hgsubrepo, self).__init__(ctx, path)
626 super(hgsubrepo, self).__init__(ctx, path)
617 self._state = state
627 self._state = state
618 r = ctx.repo()
628 r = ctx.repo()
619 root = r.wjoin(path)
629 root = r.wjoin(path)
620 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
630 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
621 self._repo = hg.repository(r.baseui, root, create=create)
631 self._repo = hg.repository(r.baseui, root, create=create)
622
632
623 # Propagate the parent's --hidden option
633 # Propagate the parent's --hidden option
624 if r is r.unfiltered():
634 if r is r.unfiltered():
625 self._repo = self._repo.unfiltered()
635 self._repo = self._repo.unfiltered()
626
636
627 self.ui = self._repo.ui
637 self.ui = self._repo.ui
628 for s, k in [('ui', 'commitsubrepos')]:
638 for s, k in [('ui', 'commitsubrepos')]:
629 v = r.ui.config(s, k)
639 v = r.ui.config(s, k)
630 if v:
640 if v:
631 self.ui.setconfig(s, k, v, 'subrepo')
641 self.ui.setconfig(s, k, v, 'subrepo')
632 # internal config: ui._usedassubrepo
642 # internal config: ui._usedassubrepo
633 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
643 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
634 self._initrepo(r, state[0], create)
644 self._initrepo(r, state[0], create)
635
645
636 def storeclean(self, path):
646 def storeclean(self, path):
637 with self._repo.lock():
647 with self._repo.lock():
638 return self._storeclean(path)
648 return self._storeclean(path)
639
649
640 def _storeclean(self, path):
650 def _storeclean(self, path):
641 clean = True
651 clean = True
642 itercache = self._calcstorehash(path)
652 itercache = self._calcstorehash(path)
643 for filehash in self._readstorehashcache(path):
653 for filehash in self._readstorehashcache(path):
644 if filehash != next(itercache, None):
654 if filehash != next(itercache, None):
645 clean = False
655 clean = False
646 break
656 break
647 if clean:
657 if clean:
648 # if not empty:
658 # if not empty:
649 # the cached and current pull states have a different size
659 # the cached and current pull states have a different size
650 clean = next(itercache, None) is None
660 clean = next(itercache, None) is None
651 return clean
661 return clean
652
662
653 def _calcstorehash(self, remotepath):
663 def _calcstorehash(self, remotepath):
654 '''calculate a unique "store hash"
664 '''calculate a unique "store hash"
655
665
656 This method is used to to detect when there are changes that may
666 This method is used to to detect when there are changes that may
657 require a push to a given remote path.'''
667 require a push to a given remote path.'''
658 # sort the files that will be hashed in increasing (likely) file size
668 # sort the files that will be hashed in increasing (likely) file size
659 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
669 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
660 yield '# %s\n' % _expandedabspath(remotepath)
670 yield '# %s\n' % _expandedabspath(remotepath)
661 vfs = self._repo.vfs
671 vfs = self._repo.vfs
662 for relname in filelist:
672 for relname in filelist:
663 filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest()
673 filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest()
664 yield '%s = %s\n' % (relname, filehash)
674 yield '%s = %s\n' % (relname, filehash)
665
675
666 @propertycache
676 @propertycache
667 def _cachestorehashvfs(self):
677 def _cachestorehashvfs(self):
668 return scmutil.vfs(self._repo.join('cache/storehash'))
678 return scmutil.vfs(self._repo.join('cache/storehash'))
669
679
670 def _readstorehashcache(self, remotepath):
680 def _readstorehashcache(self, remotepath):
671 '''read the store hash cache for a given remote repository'''
681 '''read the store hash cache for a given remote repository'''
672 cachefile = _getstorehashcachename(remotepath)
682 cachefile = _getstorehashcachename(remotepath)
673 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
683 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
674
684
675 def _cachestorehash(self, remotepath):
685 def _cachestorehash(self, remotepath):
676 '''cache the current store hash
686 '''cache the current store hash
677
687
678 Each remote repo requires its own store hash cache, because a subrepo
688 Each remote repo requires its own store hash cache, because a subrepo
679 store may be "clean" versus a given remote repo, but not versus another
689 store may be "clean" versus a given remote repo, but not versus another
680 '''
690 '''
681 cachefile = _getstorehashcachename(remotepath)
691 cachefile = _getstorehashcachename(remotepath)
682 with self._repo.lock():
692 with self._repo.lock():
683 storehash = list(self._calcstorehash(remotepath))
693 storehash = list(self._calcstorehash(remotepath))
684 vfs = self._cachestorehashvfs
694 vfs = self._cachestorehashvfs
685 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
695 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
686
696
687 def _getctx(self):
697 def _getctx(self):
688 '''fetch the context for this subrepo revision, possibly a workingctx
698 '''fetch the context for this subrepo revision, possibly a workingctx
689 '''
699 '''
690 if self._ctx.rev() is None:
700 if self._ctx.rev() is None:
691 return self._repo[None] # workingctx if parent is workingctx
701 return self._repo[None] # workingctx if parent is workingctx
692 else:
702 else:
693 rev = self._state[1]
703 rev = self._state[1]
694 return self._repo[rev]
704 return self._repo[rev]
695
705
696 @annotatesubrepoerror
706 @annotatesubrepoerror
697 def _initrepo(self, parentrepo, source, create):
707 def _initrepo(self, parentrepo, source, create):
698 self._repo._subparent = parentrepo
708 self._repo._subparent = parentrepo
699 self._repo._subsource = source
709 self._repo._subsource = source
700
710
701 if create:
711 if create:
702 lines = ['[paths]\n']
712 lines = ['[paths]\n']
703
713
704 def addpathconfig(key, value):
714 def addpathconfig(key, value):
705 if value:
715 if value:
706 lines.append('%s = %s\n' % (key, value))
716 lines.append('%s = %s\n' % (key, value))
707 self.ui.setconfig('paths', key, value, 'subrepo')
717 self.ui.setconfig('paths', key, value, 'subrepo')
708
718
709 defpath = _abssource(self._repo, abort=False)
719 defpath = _abssource(self._repo, abort=False)
710 defpushpath = _abssource(self._repo, True, abort=False)
720 defpushpath = _abssource(self._repo, True, abort=False)
711 addpathconfig('default', defpath)
721 addpathconfig('default', defpath)
712 if defpath != defpushpath:
722 if defpath != defpushpath:
713 addpathconfig('default-push', defpushpath)
723 addpathconfig('default-push', defpushpath)
714
724
715 fp = self._repo.vfs("hgrc", "w", text=True)
725 fp = self._repo.vfs("hgrc", "w", text=True)
716 try:
726 try:
717 fp.write(''.join(lines))
727 fp.write(''.join(lines))
718 finally:
728 finally:
719 fp.close()
729 fp.close()
720
730
721 @annotatesubrepoerror
731 @annotatesubrepoerror
722 def add(self, ui, match, prefix, explicitonly, **opts):
732 def add(self, ui, match, prefix, explicitonly, **opts):
723 return cmdutil.add(ui, self._repo, match,
733 return cmdutil.add(ui, self._repo, match,
724 self.wvfs.reljoin(prefix, self._path),
734 self.wvfs.reljoin(prefix, self._path),
725 explicitonly, **opts)
735 explicitonly, **opts)
726
736
727 @annotatesubrepoerror
737 @annotatesubrepoerror
728 def addremove(self, m, prefix, opts, dry_run, similarity):
738 def addremove(self, m, prefix, opts, dry_run, similarity):
729 # In the same way as sub directories are processed, once in a subrepo,
739 # In the same way as sub directories are processed, once in a subrepo,
730 # always entry any of its subrepos. Don't corrupt the options that will
740 # always entry any of its subrepos. Don't corrupt the options that will
731 # be used to process sibling subrepos however.
741 # be used to process sibling subrepos however.
732 opts = copy.copy(opts)
742 opts = copy.copy(opts)
733 opts['subrepos'] = True
743 opts['subrepos'] = True
734 return scmutil.addremove(self._repo, m,
744 return scmutil.addremove(self._repo, m,
735 self.wvfs.reljoin(prefix, self._path), opts,
745 self.wvfs.reljoin(prefix, self._path), opts,
736 dry_run, similarity)
746 dry_run, similarity)
737
747
738 @annotatesubrepoerror
748 @annotatesubrepoerror
739 def cat(self, match, prefix, **opts):
749 def cat(self, match, prefix, **opts):
740 rev = self._state[1]
750 rev = self._state[1]
741 ctx = self._repo[rev]
751 ctx = self._repo[rev]
742 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
752 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
743
753
744 @annotatesubrepoerror
754 @annotatesubrepoerror
745 def status(self, rev2, **opts):
755 def status(self, rev2, **opts):
746 try:
756 try:
747 rev1 = self._state[1]
757 rev1 = self._state[1]
748 ctx1 = self._repo[rev1]
758 ctx1 = self._repo[rev1]
749 ctx2 = self._repo[rev2]
759 ctx2 = self._repo[rev2]
750 return self._repo.status(ctx1, ctx2, **opts)
760 return self._repo.status(ctx1, ctx2, **opts)
751 except error.RepoLookupError as inst:
761 except error.RepoLookupError as inst:
752 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
762 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
753 % (inst, subrelpath(self)))
763 % (inst, subrelpath(self)))
754 return scmutil.status([], [], [], [], [], [], [])
764 return scmutil.status([], [], [], [], [], [], [])
755
765
756 @annotatesubrepoerror
766 @annotatesubrepoerror
757 def diff(self, ui, diffopts, node2, match, prefix, **opts):
767 def diff(self, ui, diffopts, node2, match, prefix, **opts):
758 try:
768 try:
759 node1 = node.bin(self._state[1])
769 node1 = node.bin(self._state[1])
760 # We currently expect node2 to come from substate and be
770 # We currently expect node2 to come from substate and be
761 # in hex format
771 # in hex format
762 if node2 is not None:
772 if node2 is not None:
763 node2 = node.bin(node2)
773 node2 = node.bin(node2)
764 cmdutil.diffordiffstat(ui, self._repo, diffopts,
774 cmdutil.diffordiffstat(ui, self._repo, diffopts,
765 node1, node2, match,
775 node1, node2, match,
766 prefix=posixpath.join(prefix, self._path),
776 prefix=posixpath.join(prefix, self._path),
767 listsubrepos=True, **opts)
777 listsubrepos=True, **opts)
768 except error.RepoLookupError as inst:
778 except error.RepoLookupError as inst:
769 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
779 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
770 % (inst, subrelpath(self)))
780 % (inst, subrelpath(self)))
771
781
772 @annotatesubrepoerror
782 @annotatesubrepoerror
773 def archive(self, archiver, prefix, match=None):
783 def archive(self, archiver, prefix, match=None):
774 self._get(self._state + ('hg',))
784 self._get(self._state + ('hg',))
775 total = abstractsubrepo.archive(self, archiver, prefix, match)
785 total = abstractsubrepo.archive(self, archiver, prefix, match)
776 rev = self._state[1]
786 rev = self._state[1]
777 ctx = self._repo[rev]
787 ctx = self._repo[rev]
778 for subpath in ctx.substate:
788 for subpath in ctx.substate:
779 s = subrepo(ctx, subpath, True)
789 s = subrepo(ctx, subpath, True)
780 submatch = matchmod.subdirmatcher(subpath, match)
790 submatch = matchmod.subdirmatcher(subpath, match)
781 total += s.archive(archiver, prefix + self._path + '/', submatch)
791 total += s.archive(archiver, prefix + self._path + '/', submatch)
782 return total
792 return total
783
793
784 @annotatesubrepoerror
794 @annotatesubrepoerror
785 def dirty(self, ignoreupdate=False):
795 def dirty(self, ignoreupdate=False):
786 r = self._state[1]
796 r = self._state[1]
787 if r == '' and not ignoreupdate: # no state recorded
797 if r == '' and not ignoreupdate: # no state recorded
788 return True
798 return True
789 w = self._repo[None]
799 w = self._repo[None]
790 if r != w.p1().hex() and not ignoreupdate:
800 if r != w.p1().hex() and not ignoreupdate:
791 # different version checked out
801 # different version checked out
792 return True
802 return True
793 return w.dirty() # working directory changed
803 return w.dirty() # working directory changed
794
804
795 def basestate(self):
805 def basestate(self):
796 return self._repo['.'].hex()
806 return self._repo['.'].hex()
797
807
798 def checknested(self, path):
808 def checknested(self, path):
799 return self._repo._checknested(self._repo.wjoin(path))
809 return self._repo._checknested(self._repo.wjoin(path))
800
810
801 @annotatesubrepoerror
811 @annotatesubrepoerror
802 def commit(self, text, user, date):
812 def commit(self, text, user, date):
803 # don't bother committing in the subrepo if it's only been
813 # don't bother committing in the subrepo if it's only been
804 # updated
814 # updated
805 if not self.dirty(True):
815 if not self.dirty(True):
806 return self._repo['.'].hex()
816 return self._repo['.'].hex()
807 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
817 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
808 n = self._repo.commit(text, user, date)
818 n = self._repo.commit(text, user, date)
809 if not n:
819 if not n:
810 return self._repo['.'].hex() # different version checked out
820 return self._repo['.'].hex() # different version checked out
811 return node.hex(n)
821 return node.hex(n)
812
822
813 @annotatesubrepoerror
823 @annotatesubrepoerror
814 def phase(self, state):
824 def phase(self, state):
815 return self._repo[state].phase()
825 return self._repo[state].phase()
816
826
817 @annotatesubrepoerror
827 @annotatesubrepoerror
818 def remove(self):
828 def remove(self):
819 # we can't fully delete the repository as it may contain
829 # we can't fully delete the repository as it may contain
820 # local-only history
830 # local-only history
821 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
831 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
822 hg.clean(self._repo, node.nullid, False)
832 hg.clean(self._repo, node.nullid, False)
823
833
824 def _get(self, state):
834 def _get(self, state):
825 source, revision, kind = state
835 source, revision, kind = state
826 if revision in self._repo.unfiltered():
836 if revision in self._repo.unfiltered():
827 return True
837 return True
828 self._repo._subsource = source
838 self._repo._subsource = source
829 srcurl = _abssource(self._repo)
839 srcurl = _abssource(self._repo)
830 other = hg.peer(self._repo, {}, srcurl)
840 other = hg.peer(self._repo, {}, srcurl)
831 if len(self._repo) == 0:
841 if len(self._repo) == 0:
832 self.ui.status(_('cloning subrepo %s from %s\n')
842 self.ui.status(_('cloning subrepo %s from %s\n')
833 % (subrelpath(self), srcurl))
843 % (subrelpath(self), srcurl))
834 parentrepo = self._repo._subparent
844 parentrepo = self._repo._subparent
835 # use self._repo.vfs instead of self.wvfs to remove .hg only
845 # use self._repo.vfs instead of self.wvfs to remove .hg only
836 self._repo.vfs.rmtree()
846 self._repo.vfs.rmtree()
837 other, cloned = hg.clone(self._repo._subparent.baseui, {},
847 other, cloned = hg.clone(self._repo._subparent.baseui, {},
838 other, self._repo.root,
848 other, self._repo.root,
839 update=False)
849 update=False)
840 self._repo = cloned.local()
850 self._repo = cloned.local()
841 self._initrepo(parentrepo, source, create=True)
851 self._initrepo(parentrepo, source, create=True)
842 self._cachestorehash(srcurl)
852 self._cachestorehash(srcurl)
843 else:
853 else:
844 self.ui.status(_('pulling subrepo %s from %s\n')
854 self.ui.status(_('pulling subrepo %s from %s\n')
845 % (subrelpath(self), srcurl))
855 % (subrelpath(self), srcurl))
846 cleansub = self.storeclean(srcurl)
856 cleansub = self.storeclean(srcurl)
847 exchange.pull(self._repo, other)
857 exchange.pull(self._repo, other)
848 if cleansub:
858 if cleansub:
849 # keep the repo clean after pull
859 # keep the repo clean after pull
850 self._cachestorehash(srcurl)
860 self._cachestorehash(srcurl)
851 return False
861 return False
852
862
853 @annotatesubrepoerror
863 @annotatesubrepoerror
854 def get(self, state, overwrite=False):
864 def get(self, state, overwrite=False):
855 inrepo = self._get(state)
865 inrepo = self._get(state)
856 source, revision, kind = state
866 source, revision, kind = state
857 repo = self._repo
867 repo = self._repo
858 repo.ui.debug("getting subrepo %s\n" % self._path)
868 repo.ui.debug("getting subrepo %s\n" % self._path)
859 if inrepo:
869 if inrepo:
860 urepo = repo.unfiltered()
870 urepo = repo.unfiltered()
861 ctx = urepo[revision]
871 ctx = urepo[revision]
862 if ctx.hidden():
872 if ctx.hidden():
863 urepo.ui.warn(
873 urepo.ui.warn(
864 _('revision %s in subrepo %s is hidden\n') \
874 _('revision %s in subrepo %s is hidden\n') \
865 % (revision[0:12], self._path))
875 % (revision[0:12], self._path))
866 repo = urepo
876 repo = urepo
867 hg.updaterepo(repo, revision, overwrite)
877 hg.updaterepo(repo, revision, overwrite)
868
878
869 @annotatesubrepoerror
879 @annotatesubrepoerror
870 def merge(self, state):
880 def merge(self, state):
871 self._get(state)
881 self._get(state)
872 cur = self._repo['.']
882 cur = self._repo['.']
873 dst = self._repo[state[1]]
883 dst = self._repo[state[1]]
874 anc = dst.ancestor(cur)
884 anc = dst.ancestor(cur)
875
885
876 def mergefunc():
886 def mergefunc():
877 if anc == cur and dst.branch() == cur.branch():
887 if anc == cur and dst.branch() == cur.branch():
878 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
888 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
879 hg.update(self._repo, state[1])
889 hg.update(self._repo, state[1])
880 elif anc == dst:
890 elif anc == dst:
881 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
891 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
882 else:
892 else:
883 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
893 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
884 hg.merge(self._repo, state[1], remind=False)
894 hg.merge(self._repo, state[1], remind=False)
885
895
886 wctx = self._repo[None]
896 wctx = self._repo[None]
887 if self.dirty():
897 if self.dirty():
888 if anc != dst:
898 if anc != dst:
889 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
899 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
890 mergefunc()
900 mergefunc()
891 else:
901 else:
892 mergefunc()
902 mergefunc()
893 else:
903 else:
894 mergefunc()
904 mergefunc()
895
905
896 @annotatesubrepoerror
906 @annotatesubrepoerror
897 def push(self, opts):
907 def push(self, opts):
898 force = opts.get('force')
908 force = opts.get('force')
899 newbranch = opts.get('new_branch')
909 newbranch = opts.get('new_branch')
900 ssh = opts.get('ssh')
910 ssh = opts.get('ssh')
901
911
902 # push subrepos depth-first for coherent ordering
912 # push subrepos depth-first for coherent ordering
903 c = self._repo['']
913 c = self._repo['']
904 subs = c.substate # only repos that are committed
914 subs = c.substate # only repos that are committed
905 for s in sorted(subs):
915 for s in sorted(subs):
906 if c.sub(s).push(opts) == 0:
916 if c.sub(s).push(opts) == 0:
907 return False
917 return False
908
918
909 dsturl = _abssource(self._repo, True)
919 dsturl = _abssource(self._repo, True)
910 if not force:
920 if not force:
911 if self.storeclean(dsturl):
921 if self.storeclean(dsturl):
912 self.ui.status(
922 self.ui.status(
913 _('no changes made to subrepo %s since last push to %s\n')
923 _('no changes made to subrepo %s since last push to %s\n')
914 % (subrelpath(self), dsturl))
924 % (subrelpath(self), dsturl))
915 return None
925 return None
916 self.ui.status(_('pushing subrepo %s to %s\n') %
926 self.ui.status(_('pushing subrepo %s to %s\n') %
917 (subrelpath(self), dsturl))
927 (subrelpath(self), dsturl))
918 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
928 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
919 res = exchange.push(self._repo, other, force, newbranch=newbranch)
929 res = exchange.push(self._repo, other, force, newbranch=newbranch)
920
930
921 # the repo is now clean
931 # the repo is now clean
922 self._cachestorehash(dsturl)
932 self._cachestorehash(dsturl)
923 return res.cgresult
933 return res.cgresult
924
934
925 @annotatesubrepoerror
935 @annotatesubrepoerror
926 def outgoing(self, ui, dest, opts):
936 def outgoing(self, ui, dest, opts):
927 if 'rev' in opts or 'branch' in opts:
937 if 'rev' in opts or 'branch' in opts:
928 opts = copy.copy(opts)
938 opts = copy.copy(opts)
929 opts.pop('rev', None)
939 opts.pop('rev', None)
930 opts.pop('branch', None)
940 opts.pop('branch', None)
931 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
941 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
932
942
933 @annotatesubrepoerror
943 @annotatesubrepoerror
934 def incoming(self, ui, source, opts):
944 def incoming(self, ui, source, opts):
935 if 'rev' in opts or 'branch' in opts:
945 if 'rev' in opts or 'branch' in opts:
936 opts = copy.copy(opts)
946 opts = copy.copy(opts)
937 opts.pop('rev', None)
947 opts.pop('rev', None)
938 opts.pop('branch', None)
948 opts.pop('branch', None)
939 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
949 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
940
950
941 @annotatesubrepoerror
951 @annotatesubrepoerror
942 def files(self):
952 def files(self):
943 rev = self._state[1]
953 rev = self._state[1]
944 ctx = self._repo[rev]
954 ctx = self._repo[rev]
945 return ctx.manifest().keys()
955 return ctx.manifest().keys()
946
956
947 def filedata(self, name):
957 def filedata(self, name):
948 rev = self._state[1]
958 rev = self._state[1]
949 return self._repo[rev][name].data()
959 return self._repo[rev][name].data()
950
960
951 def fileflags(self, name):
961 def fileflags(self, name):
952 rev = self._state[1]
962 rev = self._state[1]
953 ctx = self._repo[rev]
963 ctx = self._repo[rev]
954 return ctx.flags(name)
964 return ctx.flags(name)
955
965
956 @annotatesubrepoerror
966 @annotatesubrepoerror
957 def printfiles(self, ui, m, fm, fmt, subrepos):
967 def printfiles(self, ui, m, fm, fmt, subrepos):
958 # If the parent context is a workingctx, use the workingctx here for
968 # If the parent context is a workingctx, use the workingctx here for
959 # consistency.
969 # consistency.
960 if self._ctx.rev() is None:
970 if self._ctx.rev() is None:
961 ctx = self._repo[None]
971 ctx = self._repo[None]
962 else:
972 else:
963 rev = self._state[1]
973 rev = self._state[1]
964 ctx = self._repo[rev]
974 ctx = self._repo[rev]
965 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
975 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
966
976
967 @annotatesubrepoerror
977 @annotatesubrepoerror
968 def getfileset(self, expr):
978 def getfileset(self, expr):
969 if self._ctx.rev() is None:
979 if self._ctx.rev() is None:
970 ctx = self._repo[None]
980 ctx = self._repo[None]
971 else:
981 else:
972 rev = self._state[1]
982 rev = self._state[1]
973 ctx = self._repo[rev]
983 ctx = self._repo[rev]
974
984
975 files = ctx.getfileset(expr)
985 files = ctx.getfileset(expr)
976
986
977 for subpath in ctx.substate:
987 for subpath in ctx.substate:
978 sub = ctx.sub(subpath)
988 sub = ctx.sub(subpath)
979
989
980 try:
990 try:
981 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
991 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
982 except error.LookupError:
992 except error.LookupError:
983 self.ui.status(_("skipping missing subrepository: %s\n")
993 self.ui.status(_("skipping missing subrepository: %s\n")
984 % self.wvfs.reljoin(reporelpath(self), subpath))
994 % self.wvfs.reljoin(reporelpath(self), subpath))
985 return files
995 return files
986
996
987 def walk(self, match):
997 def walk(self, match):
988 ctx = self._repo[None]
998 ctx = self._repo[None]
989 return ctx.walk(match)
999 return ctx.walk(match)
990
1000
991 @annotatesubrepoerror
1001 @annotatesubrepoerror
992 def forget(self, match, prefix):
1002 def forget(self, match, prefix):
993 return cmdutil.forget(self.ui, self._repo, match,
1003 return cmdutil.forget(self.ui, self._repo, match,
994 self.wvfs.reljoin(prefix, self._path), True)
1004 self.wvfs.reljoin(prefix, self._path), True)
995
1005
996 @annotatesubrepoerror
1006 @annotatesubrepoerror
997 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
1007 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
998 return cmdutil.remove(self.ui, self._repo, matcher,
1008 return cmdutil.remove(self.ui, self._repo, matcher,
999 self.wvfs.reljoin(prefix, self._path),
1009 self.wvfs.reljoin(prefix, self._path),
1000 after, force, subrepos)
1010 after, force, subrepos)
1001
1011
1002 @annotatesubrepoerror
1012 @annotatesubrepoerror
1003 def revert(self, substate, *pats, **opts):
1013 def revert(self, substate, *pats, **opts):
1004 # reverting a subrepo is a 2 step process:
1014 # reverting a subrepo is a 2 step process:
1005 # 1. if the no_backup is not set, revert all modified
1015 # 1. if the no_backup is not set, revert all modified
1006 # files inside the subrepo
1016 # files inside the subrepo
1007 # 2. update the subrepo to the revision specified in
1017 # 2. update the subrepo to the revision specified in
1008 # the corresponding substate dictionary
1018 # the corresponding substate dictionary
1009 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1019 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1010 if not opts.get('no_backup'):
1020 if not opts.get('no_backup'):
1011 # Revert all files on the subrepo, creating backups
1021 # Revert all files on the subrepo, creating backups
1012 # Note that this will not recursively revert subrepos
1022 # Note that this will not recursively revert subrepos
1013 # We could do it if there was a set:subrepos() predicate
1023 # We could do it if there was a set:subrepos() predicate
1014 opts = opts.copy()
1024 opts = opts.copy()
1015 opts['date'] = None
1025 opts['date'] = None
1016 opts['rev'] = substate[1]
1026 opts['rev'] = substate[1]
1017
1027
1018 self.filerevert(*pats, **opts)
1028 self.filerevert(*pats, **opts)
1019
1029
1020 # Update the repo to the revision specified in the given substate
1030 # Update the repo to the revision specified in the given substate
1021 if not opts.get('dry_run'):
1031 if not opts.get('dry_run'):
1022 self.get(substate, overwrite=True)
1032 self.get(substate, overwrite=True)
1023
1033
1024 def filerevert(self, *pats, **opts):
1034 def filerevert(self, *pats, **opts):
1025 ctx = self._repo[opts['rev']]
1035 ctx = self._repo[opts['rev']]
1026 parents = self._repo.dirstate.parents()
1036 parents = self._repo.dirstate.parents()
1027 if opts.get('all'):
1037 if opts.get('all'):
1028 pats = ['set:modified()']
1038 pats = ['set:modified()']
1029 else:
1039 else:
1030 pats = []
1040 pats = []
1031 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
1041 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
1032
1042
1033 def shortid(self, revid):
1043 def shortid(self, revid):
1034 return revid[:12]
1044 return revid[:12]
1035
1045
1036 def verify(self):
1046 def verify(self):
1037 try:
1047 try:
1038 rev = self._state[1]
1048 rev = self._state[1]
1039 ctx = self._repo.unfiltered()[rev]
1049 ctx = self._repo.unfiltered()[rev]
1040 if ctx.hidden():
1050 if ctx.hidden():
1041 # Since hidden revisions aren't pushed/pulled, it seems worth an
1051 # Since hidden revisions aren't pushed/pulled, it seems worth an
1042 # explicit warning.
1052 # explicit warning.
1043 ui = self._repo.ui
1053 ui = self._repo.ui
1044 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
1054 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
1045 (self._relpath, node.short(self._ctx.node())))
1055 (self._relpath, node.short(self._ctx.node())))
1046 return 0
1056 return 0
1047 except error.RepoLookupError:
1057 except error.RepoLookupError:
1048 # A missing subrepo revision may be a case of needing to pull it, so
1058 # A missing subrepo revision may be a case of needing to pull it, so
1049 # don't treat this as an error.
1059 # don't treat this as an error.
1050 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
1060 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
1051 (self._relpath, node.short(self._ctx.node())))
1061 (self._relpath, node.short(self._ctx.node())))
1052 return 0
1062 return 0
1053
1063
1054 @propertycache
1064 @propertycache
1055 def wvfs(self):
1065 def wvfs(self):
1056 """return own wvfs for efficiency and consistency
1066 """return own wvfs for efficiency and consistency
1057 """
1067 """
1058 return self._repo.wvfs
1068 return self._repo.wvfs
1059
1069
1060 @propertycache
1070 @propertycache
1061 def _relpath(self):
1071 def _relpath(self):
1062 """return path to this subrepository as seen from outermost repository
1072 """return path to this subrepository as seen from outermost repository
1063 """
1073 """
1064 # Keep consistent dir separators by avoiding vfs.join(self._path)
1074 # Keep consistent dir separators by avoiding vfs.join(self._path)
1065 return reporelpath(self._repo)
1075 return reporelpath(self._repo)
1066
1076
1067 class svnsubrepo(abstractsubrepo):
1077 class svnsubrepo(abstractsubrepo):
1068 def __init__(self, ctx, path, state, allowcreate):
1078 def __init__(self, ctx, path, state, allowcreate):
1069 super(svnsubrepo, self).__init__(ctx, path)
1079 super(svnsubrepo, self).__init__(ctx, path)
1070 self._state = state
1080 self._state = state
1071 self._exe = util.findexe('svn')
1081 self._exe = util.findexe('svn')
1072 if not self._exe:
1082 if not self._exe:
1073 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1083 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1074 % self._path)
1084 % self._path)
1075
1085
1076 def _svncommand(self, commands, filename='', failok=False):
1086 def _svncommand(self, commands, filename='', failok=False):
1077 cmd = [self._exe]
1087 cmd = [self._exe]
1078 extrakw = {}
1088 extrakw = {}
1079 if not self.ui.interactive():
1089 if not self.ui.interactive():
1080 # Making stdin be a pipe should prevent svn from behaving
1090 # Making stdin be a pipe should prevent svn from behaving
1081 # interactively even if we can't pass --non-interactive.
1091 # interactively even if we can't pass --non-interactive.
1082 extrakw['stdin'] = subprocess.PIPE
1092 extrakw['stdin'] = subprocess.PIPE
1083 # Starting in svn 1.5 --non-interactive is a global flag
1093 # Starting in svn 1.5 --non-interactive is a global flag
1084 # instead of being per-command, but we need to support 1.4 so
1094 # instead of being per-command, but we need to support 1.4 so
1085 # we have to be intelligent about what commands take
1095 # we have to be intelligent about what commands take
1086 # --non-interactive.
1096 # --non-interactive.
1087 if commands[0] in ('update', 'checkout', 'commit'):
1097 if commands[0] in ('update', 'checkout', 'commit'):
1088 cmd.append('--non-interactive')
1098 cmd.append('--non-interactive')
1089 cmd.extend(commands)
1099 cmd.extend(commands)
1090 if filename is not None:
1100 if filename is not None:
1091 path = self.wvfs.reljoin(self._ctx.repo().origroot,
1101 path = self.wvfs.reljoin(self._ctx.repo().origroot,
1092 self._path, filename)
1102 self._path, filename)
1093 cmd.append(path)
1103 cmd.append(path)
1094 env = dict(os.environ)
1104 env = dict(os.environ)
1095 # Avoid localized output, preserve current locale for everything else.
1105 # Avoid localized output, preserve current locale for everything else.
1096 lc_all = env.get('LC_ALL')
1106 lc_all = env.get('LC_ALL')
1097 if lc_all:
1107 if lc_all:
1098 env['LANG'] = lc_all
1108 env['LANG'] = lc_all
1099 del env['LC_ALL']
1109 del env['LC_ALL']
1100 env['LC_MESSAGES'] = 'C'
1110 env['LC_MESSAGES'] = 'C'
1101 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
1111 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
1102 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1112 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1103 universal_newlines=True, env=env, **extrakw)
1113 universal_newlines=True, env=env, **extrakw)
1104 stdout, stderr = p.communicate()
1114 stdout, stderr = p.communicate()
1105 stderr = stderr.strip()
1115 stderr = stderr.strip()
1106 if not failok:
1116 if not failok:
1107 if p.returncode:
1117 if p.returncode:
1108 raise error.Abort(stderr or 'exited with code %d'
1118 raise error.Abort(stderr or 'exited with code %d'
1109 % p.returncode)
1119 % p.returncode)
1110 if stderr:
1120 if stderr:
1111 self.ui.warn(stderr + '\n')
1121 self.ui.warn(stderr + '\n')
1112 return stdout, stderr
1122 return stdout, stderr
1113
1123
1114 @propertycache
1124 @propertycache
1115 def _svnversion(self):
1125 def _svnversion(self):
1116 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1126 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1117 m = re.search(r'^(\d+)\.(\d+)', output)
1127 m = re.search(r'^(\d+)\.(\d+)', output)
1118 if not m:
1128 if not m:
1119 raise error.Abort(_('cannot retrieve svn tool version'))
1129 raise error.Abort(_('cannot retrieve svn tool version'))
1120 return (int(m.group(1)), int(m.group(2)))
1130 return (int(m.group(1)), int(m.group(2)))
1121
1131
1122 def _wcrevs(self):
1132 def _wcrevs(self):
1123 # Get the working directory revision as well as the last
1133 # Get the working directory revision as well as the last
1124 # commit revision so we can compare the subrepo state with
1134 # commit revision so we can compare the subrepo state with
1125 # both. We used to store the working directory one.
1135 # both. We used to store the working directory one.
1126 output, err = self._svncommand(['info', '--xml'])
1136 output, err = self._svncommand(['info', '--xml'])
1127 doc = xml.dom.minidom.parseString(output)
1137 doc = xml.dom.minidom.parseString(output)
1128 entries = doc.getElementsByTagName('entry')
1138 entries = doc.getElementsByTagName('entry')
1129 lastrev, rev = '0', '0'
1139 lastrev, rev = '0', '0'
1130 if entries:
1140 if entries:
1131 rev = str(entries[0].getAttribute('revision')) or '0'
1141 rev = str(entries[0].getAttribute('revision')) or '0'
1132 commits = entries[0].getElementsByTagName('commit')
1142 commits = entries[0].getElementsByTagName('commit')
1133 if commits:
1143 if commits:
1134 lastrev = str(commits[0].getAttribute('revision')) or '0'
1144 lastrev = str(commits[0].getAttribute('revision')) or '0'
1135 return (lastrev, rev)
1145 return (lastrev, rev)
1136
1146
1137 def _wcrev(self):
1147 def _wcrev(self):
1138 return self._wcrevs()[0]
1148 return self._wcrevs()[0]
1139
1149
1140 def _wcchanged(self):
1150 def _wcchanged(self):
1141 """Return (changes, extchanges, missing) where changes is True
1151 """Return (changes, extchanges, missing) where changes is True
1142 if the working directory was changed, extchanges is
1152 if the working directory was changed, extchanges is
1143 True if any of these changes concern an external entry and missing
1153 True if any of these changes concern an external entry and missing
1144 is True if any change is a missing entry.
1154 is True if any change is a missing entry.
1145 """
1155 """
1146 output, err = self._svncommand(['status', '--xml'])
1156 output, err = self._svncommand(['status', '--xml'])
1147 externals, changes, missing = [], [], []
1157 externals, changes, missing = [], [], []
1148 doc = xml.dom.minidom.parseString(output)
1158 doc = xml.dom.minidom.parseString(output)
1149 for e in doc.getElementsByTagName('entry'):
1159 for e in doc.getElementsByTagName('entry'):
1150 s = e.getElementsByTagName('wc-status')
1160 s = e.getElementsByTagName('wc-status')
1151 if not s:
1161 if not s:
1152 continue
1162 continue
1153 item = s[0].getAttribute('item')
1163 item = s[0].getAttribute('item')
1154 props = s[0].getAttribute('props')
1164 props = s[0].getAttribute('props')
1155 path = e.getAttribute('path')
1165 path = e.getAttribute('path')
1156 if item == 'external':
1166 if item == 'external':
1157 externals.append(path)
1167 externals.append(path)
1158 elif item == 'missing':
1168 elif item == 'missing':
1159 missing.append(path)
1169 missing.append(path)
1160 if (item not in ('', 'normal', 'unversioned', 'external')
1170 if (item not in ('', 'normal', 'unversioned', 'external')
1161 or props not in ('', 'none', 'normal')):
1171 or props not in ('', 'none', 'normal')):
1162 changes.append(path)
1172 changes.append(path)
1163 for path in changes:
1173 for path in changes:
1164 for ext in externals:
1174 for ext in externals:
1165 if path == ext or path.startswith(ext + os.sep):
1175 if path == ext or path.startswith(ext + os.sep):
1166 return True, True, bool(missing)
1176 return True, True, bool(missing)
1167 return bool(changes), False, bool(missing)
1177 return bool(changes), False, bool(missing)
1168
1178
1169 def dirty(self, ignoreupdate=False):
1179 def dirty(self, ignoreupdate=False):
1170 if not self._wcchanged()[0]:
1180 if not self._wcchanged()[0]:
1171 if self._state[1] in self._wcrevs() or ignoreupdate:
1181 if self._state[1] in self._wcrevs() or ignoreupdate:
1172 return False
1182 return False
1173 return True
1183 return True
1174
1184
1175 def basestate(self):
1185 def basestate(self):
1176 lastrev, rev = self._wcrevs()
1186 lastrev, rev = self._wcrevs()
1177 if lastrev != rev:
1187 if lastrev != rev:
1178 # Last committed rev is not the same than rev. We would
1188 # Last committed rev is not the same than rev. We would
1179 # like to take lastrev but we do not know if the subrepo
1189 # like to take lastrev but we do not know if the subrepo
1180 # URL exists at lastrev. Test it and fallback to rev it
1190 # URL exists at lastrev. Test it and fallback to rev it
1181 # is not there.
1191 # is not there.
1182 try:
1192 try:
1183 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1193 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1184 return lastrev
1194 return lastrev
1185 except error.Abort:
1195 except error.Abort:
1186 pass
1196 pass
1187 return rev
1197 return rev
1188
1198
1189 @annotatesubrepoerror
1199 @annotatesubrepoerror
1190 def commit(self, text, user, date):
1200 def commit(self, text, user, date):
1191 # user and date are out of our hands since svn is centralized
1201 # user and date are out of our hands since svn is centralized
1192 changed, extchanged, missing = self._wcchanged()
1202 changed, extchanged, missing = self._wcchanged()
1193 if not changed:
1203 if not changed:
1194 return self.basestate()
1204 return self.basestate()
1195 if extchanged:
1205 if extchanged:
1196 # Do not try to commit externals
1206 # Do not try to commit externals
1197 raise error.Abort(_('cannot commit svn externals'))
1207 raise error.Abort(_('cannot commit svn externals'))
1198 if missing:
1208 if missing:
1199 # svn can commit with missing entries but aborting like hg
1209 # svn can commit with missing entries but aborting like hg
1200 # seems a better approach.
1210 # seems a better approach.
1201 raise error.Abort(_('cannot commit missing svn entries'))
1211 raise error.Abort(_('cannot commit missing svn entries'))
1202 commitinfo, err = self._svncommand(['commit', '-m', text])
1212 commitinfo, err = self._svncommand(['commit', '-m', text])
1203 self.ui.status(commitinfo)
1213 self.ui.status(commitinfo)
1204 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1214 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1205 if not newrev:
1215 if not newrev:
1206 if not commitinfo.strip():
1216 if not commitinfo.strip():
1207 # Sometimes, our definition of "changed" differs from
1217 # Sometimes, our definition of "changed" differs from
1208 # svn one. For instance, svn ignores missing files
1218 # svn one. For instance, svn ignores missing files
1209 # when committing. If there are only missing files, no
1219 # when committing. If there are only missing files, no
1210 # commit is made, no output and no error code.
1220 # commit is made, no output and no error code.
1211 raise error.Abort(_('failed to commit svn changes'))
1221 raise error.Abort(_('failed to commit svn changes'))
1212 raise error.Abort(commitinfo.splitlines()[-1])
1222 raise error.Abort(commitinfo.splitlines()[-1])
1213 newrev = newrev.groups()[0]
1223 newrev = newrev.groups()[0]
1214 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1224 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1215 return newrev
1225 return newrev
1216
1226
1217 @annotatesubrepoerror
1227 @annotatesubrepoerror
1218 def remove(self):
1228 def remove(self):
1219 if self.dirty():
1229 if self.dirty():
1220 self.ui.warn(_('not removing repo %s because '
1230 self.ui.warn(_('not removing repo %s because '
1221 'it has changes.\n') % self._path)
1231 'it has changes.\n') % self._path)
1222 return
1232 return
1223 self.ui.note(_('removing subrepo %s\n') % self._path)
1233 self.ui.note(_('removing subrepo %s\n') % self._path)
1224
1234
1225 self.wvfs.rmtree(forcibly=True)
1235 self.wvfs.rmtree(forcibly=True)
1226 try:
1236 try:
1227 pwvfs = self._ctx.repo().wvfs
1237 pwvfs = self._ctx.repo().wvfs
1228 pwvfs.removedirs(pwvfs.dirname(self._path))
1238 pwvfs.removedirs(pwvfs.dirname(self._path))
1229 except OSError:
1239 except OSError:
1230 pass
1240 pass
1231
1241
1232 @annotatesubrepoerror
1242 @annotatesubrepoerror
1233 def get(self, state, overwrite=False):
1243 def get(self, state, overwrite=False):
1234 if overwrite:
1244 if overwrite:
1235 self._svncommand(['revert', '--recursive'])
1245 self._svncommand(['revert', '--recursive'])
1236 args = ['checkout']
1246 args = ['checkout']
1237 if self._svnversion >= (1, 5):
1247 if self._svnversion >= (1, 5):
1238 args.append('--force')
1248 args.append('--force')
1239 # The revision must be specified at the end of the URL to properly
1249 # The revision must be specified at the end of the URL to properly
1240 # update to a directory which has since been deleted and recreated.
1250 # update to a directory which has since been deleted and recreated.
1241 args.append('%s@%s' % (state[0], state[1]))
1251 args.append('%s@%s' % (state[0], state[1]))
1242 status, err = self._svncommand(args, failok=True)
1252 status, err = self._svncommand(args, failok=True)
1243 _sanitize(self.ui, self.wvfs, '.svn')
1253 _sanitize(self.ui, self.wvfs, '.svn')
1244 if not re.search('Checked out revision [0-9]+.', status):
1254 if not re.search('Checked out revision [0-9]+.', status):
1245 if ('is already a working copy for a different URL' in err
1255 if ('is already a working copy for a different URL' in err
1246 and (self._wcchanged()[:2] == (False, False))):
1256 and (self._wcchanged()[:2] == (False, False))):
1247 # obstructed but clean working copy, so just blow it away.
1257 # obstructed but clean working copy, so just blow it away.
1248 self.remove()
1258 self.remove()
1249 self.get(state, overwrite=False)
1259 self.get(state, overwrite=False)
1250 return
1260 return
1251 raise error.Abort((status or err).splitlines()[-1])
1261 raise error.Abort((status or err).splitlines()[-1])
1252 self.ui.status(status)
1262 self.ui.status(status)
1253
1263
1254 @annotatesubrepoerror
1264 @annotatesubrepoerror
1255 def merge(self, state):
1265 def merge(self, state):
1256 old = self._state[1]
1266 old = self._state[1]
1257 new = state[1]
1267 new = state[1]
1258 wcrev = self._wcrev()
1268 wcrev = self._wcrev()
1259 if new != wcrev:
1269 if new != wcrev:
1260 dirty = old == wcrev or self._wcchanged()[0]
1270 dirty = old == wcrev or self._wcchanged()[0]
1261 if _updateprompt(self.ui, self, dirty, wcrev, new):
1271 if _updateprompt(self.ui, self, dirty, wcrev, new):
1262 self.get(state, False)
1272 self.get(state, False)
1263
1273
1264 def push(self, opts):
1274 def push(self, opts):
1265 # push is a no-op for SVN
1275 # push is a no-op for SVN
1266 return True
1276 return True
1267
1277
1268 @annotatesubrepoerror
1278 @annotatesubrepoerror
1269 def files(self):
1279 def files(self):
1270 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1280 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1271 doc = xml.dom.minidom.parseString(output)
1281 doc = xml.dom.minidom.parseString(output)
1272 paths = []
1282 paths = []
1273 for e in doc.getElementsByTagName('entry'):
1283 for e in doc.getElementsByTagName('entry'):
1274 kind = str(e.getAttribute('kind'))
1284 kind = str(e.getAttribute('kind'))
1275 if kind != 'file':
1285 if kind != 'file':
1276 continue
1286 continue
1277 name = ''.join(c.data for c
1287 name = ''.join(c.data for c
1278 in e.getElementsByTagName('name')[0].childNodes
1288 in e.getElementsByTagName('name')[0].childNodes
1279 if c.nodeType == c.TEXT_NODE)
1289 if c.nodeType == c.TEXT_NODE)
1280 paths.append(name.encode('utf-8'))
1290 paths.append(name.encode('utf-8'))
1281 return paths
1291 return paths
1282
1292
1283 def filedata(self, name):
1293 def filedata(self, name):
1284 return self._svncommand(['cat'], name)[0]
1294 return self._svncommand(['cat'], name)[0]
1285
1295
1286
1296
1287 class gitsubrepo(abstractsubrepo):
1297 class gitsubrepo(abstractsubrepo):
1288 def __init__(self, ctx, path, state, allowcreate):
1298 def __init__(self, ctx, path, state, allowcreate):
1289 super(gitsubrepo, self).__init__(ctx, path)
1299 super(gitsubrepo, self).__init__(ctx, path)
1290 self._state = state
1300 self._state = state
1291 self._abspath = ctx.repo().wjoin(path)
1301 self._abspath = ctx.repo().wjoin(path)
1292 self._subparent = ctx.repo()
1302 self._subparent = ctx.repo()
1293 self._ensuregit()
1303 self._ensuregit()
1294
1304
1295 def _ensuregit(self):
1305 def _ensuregit(self):
1296 try:
1306 try:
1297 self._gitexecutable = 'git'
1307 self._gitexecutable = 'git'
1298 out, err = self._gitnodir(['--version'])
1308 out, err = self._gitnodir(['--version'])
1299 except OSError as e:
1309 except OSError as e:
1300 genericerror = _("error executing git for subrepo '%s': %s")
1310 genericerror = _("error executing git for subrepo '%s': %s")
1301 notfoundhint = _("check git is installed and in your PATH")
1311 notfoundhint = _("check git is installed and in your PATH")
1302 if e.errno != errno.ENOENT:
1312 if e.errno != errno.ENOENT:
1303 raise error.Abort(genericerror % (self._path, e.strerror))
1313 raise error.Abort(genericerror % (self._path, e.strerror))
1304 elif os.name == 'nt':
1314 elif os.name == 'nt':
1305 try:
1315 try:
1306 self._gitexecutable = 'git.cmd'
1316 self._gitexecutable = 'git.cmd'
1307 out, err = self._gitnodir(['--version'])
1317 out, err = self._gitnodir(['--version'])
1308 except OSError as e2:
1318 except OSError as e2:
1309 if e2.errno == errno.ENOENT:
1319 if e2.errno == errno.ENOENT:
1310 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1320 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1311 " for subrepo '%s'") % self._path,
1321 " for subrepo '%s'") % self._path,
1312 hint=notfoundhint)
1322 hint=notfoundhint)
1313 else:
1323 else:
1314 raise error.Abort(genericerror % (self._path,
1324 raise error.Abort(genericerror % (self._path,
1315 e2.strerror))
1325 e2.strerror))
1316 else:
1326 else:
1317 raise error.Abort(_("couldn't find git for subrepo '%s'")
1327 raise error.Abort(_("couldn't find git for subrepo '%s'")
1318 % self._path, hint=notfoundhint)
1328 % self._path, hint=notfoundhint)
1319 versionstatus = self._checkversion(out)
1329 versionstatus = self._checkversion(out)
1320 if versionstatus == 'unknown':
1330 if versionstatus == 'unknown':
1321 self.ui.warn(_('cannot retrieve git version\n'))
1331 self.ui.warn(_('cannot retrieve git version\n'))
1322 elif versionstatus == 'abort':
1332 elif versionstatus == 'abort':
1323 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1333 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1324 elif versionstatus == 'warning':
1334 elif versionstatus == 'warning':
1325 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1335 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1326
1336
1327 @staticmethod
1337 @staticmethod
1328 def _gitversion(out):
1338 def _gitversion(out):
1329 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1339 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1330 if m:
1340 if m:
1331 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1341 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1332
1342
1333 m = re.search(r'^git version (\d+)\.(\d+)', out)
1343 m = re.search(r'^git version (\d+)\.(\d+)', out)
1334 if m:
1344 if m:
1335 return (int(m.group(1)), int(m.group(2)), 0)
1345 return (int(m.group(1)), int(m.group(2)), 0)
1336
1346
1337 return -1
1347 return -1
1338
1348
1339 @staticmethod
1349 @staticmethod
1340 def _checkversion(out):
1350 def _checkversion(out):
1341 '''ensure git version is new enough
1351 '''ensure git version is new enough
1342
1352
1343 >>> _checkversion = gitsubrepo._checkversion
1353 >>> _checkversion = gitsubrepo._checkversion
1344 >>> _checkversion('git version 1.6.0')
1354 >>> _checkversion('git version 1.6.0')
1345 'ok'
1355 'ok'
1346 >>> _checkversion('git version 1.8.5')
1356 >>> _checkversion('git version 1.8.5')
1347 'ok'
1357 'ok'
1348 >>> _checkversion('git version 1.4.0')
1358 >>> _checkversion('git version 1.4.0')
1349 'abort'
1359 'abort'
1350 >>> _checkversion('git version 1.5.0')
1360 >>> _checkversion('git version 1.5.0')
1351 'warning'
1361 'warning'
1352 >>> _checkversion('git version 1.9-rc0')
1362 >>> _checkversion('git version 1.9-rc0')
1353 'ok'
1363 'ok'
1354 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1364 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1355 'ok'
1365 'ok'
1356 >>> _checkversion('git version 1.9.0.GIT')
1366 >>> _checkversion('git version 1.9.0.GIT')
1357 'ok'
1367 'ok'
1358 >>> _checkversion('git version 12345')
1368 >>> _checkversion('git version 12345')
1359 'unknown'
1369 'unknown'
1360 >>> _checkversion('no')
1370 >>> _checkversion('no')
1361 'unknown'
1371 'unknown'
1362 '''
1372 '''
1363 version = gitsubrepo._gitversion(out)
1373 version = gitsubrepo._gitversion(out)
1364 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1374 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1365 # despite the docstring comment. For now, error on 1.4.0, warn on
1375 # despite the docstring comment. For now, error on 1.4.0, warn on
1366 # 1.5.0 but attempt to continue.
1376 # 1.5.0 but attempt to continue.
1367 if version == -1:
1377 if version == -1:
1368 return 'unknown'
1378 return 'unknown'
1369 if version < (1, 5, 0):
1379 if version < (1, 5, 0):
1370 return 'abort'
1380 return 'abort'
1371 elif version < (1, 6, 0):
1381 elif version < (1, 6, 0):
1372 return 'warning'
1382 return 'warning'
1373 return 'ok'
1383 return 'ok'
1374
1384
1375 def _gitcommand(self, commands, env=None, stream=False):
1385 def _gitcommand(self, commands, env=None, stream=False):
1376 return self._gitdir(commands, env=env, stream=stream)[0]
1386 return self._gitdir(commands, env=env, stream=stream)[0]
1377
1387
1378 def _gitdir(self, commands, env=None, stream=False):
1388 def _gitdir(self, commands, env=None, stream=False):
1379 return self._gitnodir(commands, env=env, stream=stream,
1389 return self._gitnodir(commands, env=env, stream=stream,
1380 cwd=self._abspath)
1390 cwd=self._abspath)
1381
1391
1382 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1392 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1383 """Calls the git command
1393 """Calls the git command
1384
1394
1385 The methods tries to call the git command. versions prior to 1.6.0
1395 The methods tries to call the git command. versions prior to 1.6.0
1386 are not supported and very probably fail.
1396 are not supported and very probably fail.
1387 """
1397 """
1388 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1398 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1389 if env is None:
1399 if env is None:
1390 env = os.environ.copy()
1400 env = os.environ.copy()
1391 # disable localization for Git output (issue5176)
1401 # disable localization for Git output (issue5176)
1392 env['LC_ALL'] = 'C'
1402 env['LC_ALL'] = 'C'
1393 # fix for Git CVE-2015-7545
1403 # fix for Git CVE-2015-7545
1394 if 'GIT_ALLOW_PROTOCOL' not in env:
1404 if 'GIT_ALLOW_PROTOCOL' not in env:
1395 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1405 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1396 # unless ui.quiet is set, print git's stderr,
1406 # unless ui.quiet is set, print git's stderr,
1397 # which is mostly progress and useful info
1407 # which is mostly progress and useful info
1398 errpipe = None
1408 errpipe = None
1399 if self.ui.quiet:
1409 if self.ui.quiet:
1400 errpipe = open(os.devnull, 'w')
1410 errpipe = open(os.devnull, 'w')
1401 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1411 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1402 cwd=cwd, env=env, close_fds=util.closefds,
1412 cwd=cwd, env=env, close_fds=util.closefds,
1403 stdout=subprocess.PIPE, stderr=errpipe)
1413 stdout=subprocess.PIPE, stderr=errpipe)
1404 if stream:
1414 if stream:
1405 return p.stdout, None
1415 return p.stdout, None
1406
1416
1407 retdata = p.stdout.read().strip()
1417 retdata = p.stdout.read().strip()
1408 # wait for the child to exit to avoid race condition.
1418 # wait for the child to exit to avoid race condition.
1409 p.wait()
1419 p.wait()
1410
1420
1411 if p.returncode != 0 and p.returncode != 1:
1421 if p.returncode != 0 and p.returncode != 1:
1412 # there are certain error codes that are ok
1422 # there are certain error codes that are ok
1413 command = commands[0]
1423 command = commands[0]
1414 if command in ('cat-file', 'symbolic-ref'):
1424 if command in ('cat-file', 'symbolic-ref'):
1415 return retdata, p.returncode
1425 return retdata, p.returncode
1416 # for all others, abort
1426 # for all others, abort
1417 raise error.Abort(_('git %s error %d in %s') %
1427 raise error.Abort(_('git %s error %d in %s') %
1418 (command, p.returncode, self._relpath))
1428 (command, p.returncode, self._relpath))
1419
1429
1420 return retdata, p.returncode
1430 return retdata, p.returncode
1421
1431
1422 def _gitmissing(self):
1432 def _gitmissing(self):
1423 return not self.wvfs.exists('.git')
1433 return not self.wvfs.exists('.git')
1424
1434
1425 def _gitstate(self):
1435 def _gitstate(self):
1426 return self._gitcommand(['rev-parse', 'HEAD'])
1436 return self._gitcommand(['rev-parse', 'HEAD'])
1427
1437
1428 def _gitcurrentbranch(self):
1438 def _gitcurrentbranch(self):
1429 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1439 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1430 if err:
1440 if err:
1431 current = None
1441 current = None
1432 return current
1442 return current
1433
1443
1434 def _gitremote(self, remote):
1444 def _gitremote(self, remote):
1435 out = self._gitcommand(['remote', 'show', '-n', remote])
1445 out = self._gitcommand(['remote', 'show', '-n', remote])
1436 line = out.split('\n')[1]
1446 line = out.split('\n')[1]
1437 i = line.index('URL: ') + len('URL: ')
1447 i = line.index('URL: ') + len('URL: ')
1438 return line[i:]
1448 return line[i:]
1439
1449
1440 def _githavelocally(self, revision):
1450 def _githavelocally(self, revision):
1441 out, code = self._gitdir(['cat-file', '-e', revision])
1451 out, code = self._gitdir(['cat-file', '-e', revision])
1442 return code == 0
1452 return code == 0
1443
1453
1444 def _gitisancestor(self, r1, r2):
1454 def _gitisancestor(self, r1, r2):
1445 base = self._gitcommand(['merge-base', r1, r2])
1455 base = self._gitcommand(['merge-base', r1, r2])
1446 return base == r1
1456 return base == r1
1447
1457
1448 def _gitisbare(self):
1458 def _gitisbare(self):
1449 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1459 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1450
1460
1451 def _gitupdatestat(self):
1461 def _gitupdatestat(self):
1452 """This must be run before git diff-index.
1462 """This must be run before git diff-index.
1453 diff-index only looks at changes to file stat;
1463 diff-index only looks at changes to file stat;
1454 this command looks at file contents and updates the stat."""
1464 this command looks at file contents and updates the stat."""
1455 self._gitcommand(['update-index', '-q', '--refresh'])
1465 self._gitcommand(['update-index', '-q', '--refresh'])
1456
1466
1457 def _gitbranchmap(self):
1467 def _gitbranchmap(self):
1458 '''returns 2 things:
1468 '''returns 2 things:
1459 a map from git branch to revision
1469 a map from git branch to revision
1460 a map from revision to branches'''
1470 a map from revision to branches'''
1461 branch2rev = {}
1471 branch2rev = {}
1462 rev2branch = {}
1472 rev2branch = {}
1463
1473
1464 out = self._gitcommand(['for-each-ref', '--format',
1474 out = self._gitcommand(['for-each-ref', '--format',
1465 '%(objectname) %(refname)'])
1475 '%(objectname) %(refname)'])
1466 for line in out.split('\n'):
1476 for line in out.split('\n'):
1467 revision, ref = line.split(' ')
1477 revision, ref = line.split(' ')
1468 if (not ref.startswith('refs/heads/') and
1478 if (not ref.startswith('refs/heads/') and
1469 not ref.startswith('refs/remotes/')):
1479 not ref.startswith('refs/remotes/')):
1470 continue
1480 continue
1471 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1481 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1472 continue # ignore remote/HEAD redirects
1482 continue # ignore remote/HEAD redirects
1473 branch2rev[ref] = revision
1483 branch2rev[ref] = revision
1474 rev2branch.setdefault(revision, []).append(ref)
1484 rev2branch.setdefault(revision, []).append(ref)
1475 return branch2rev, rev2branch
1485 return branch2rev, rev2branch
1476
1486
1477 def _gittracking(self, branches):
1487 def _gittracking(self, branches):
1478 'return map of remote branch to local tracking branch'
1488 'return map of remote branch to local tracking branch'
1479 # assumes no more than one local tracking branch for each remote
1489 # assumes no more than one local tracking branch for each remote
1480 tracking = {}
1490 tracking = {}
1481 for b in branches:
1491 for b in branches:
1482 if b.startswith('refs/remotes/'):
1492 if b.startswith('refs/remotes/'):
1483 continue
1493 continue
1484 bname = b.split('/', 2)[2]
1494 bname = b.split('/', 2)[2]
1485 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1495 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1486 if remote:
1496 if remote:
1487 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1497 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1488 tracking['refs/remotes/%s/%s' %
1498 tracking['refs/remotes/%s/%s' %
1489 (remote, ref.split('/', 2)[2])] = b
1499 (remote, ref.split('/', 2)[2])] = b
1490 return tracking
1500 return tracking
1491
1501
1492 def _abssource(self, source):
1502 def _abssource(self, source):
1493 if '://' not in source:
1503 if '://' not in source:
1494 # recognize the scp syntax as an absolute source
1504 # recognize the scp syntax as an absolute source
1495 colon = source.find(':')
1505 colon = source.find(':')
1496 if colon != -1 and '/' not in source[:colon]:
1506 if colon != -1 and '/' not in source[:colon]:
1497 return source
1507 return source
1498 self._subsource = source
1508 self._subsource = source
1499 return _abssource(self)
1509 return _abssource(self)
1500
1510
1501 def _fetch(self, source, revision):
1511 def _fetch(self, source, revision):
1502 if self._gitmissing():
1512 if self._gitmissing():
1503 source = self._abssource(source)
1513 source = self._abssource(source)
1504 self.ui.status(_('cloning subrepo %s from %s\n') %
1514 self.ui.status(_('cloning subrepo %s from %s\n') %
1505 (self._relpath, source))
1515 (self._relpath, source))
1506 self._gitnodir(['clone', source, self._abspath])
1516 self._gitnodir(['clone', source, self._abspath])
1507 if self._githavelocally(revision):
1517 if self._githavelocally(revision):
1508 return
1518 return
1509 self.ui.status(_('pulling subrepo %s from %s\n') %
1519 self.ui.status(_('pulling subrepo %s from %s\n') %
1510 (self._relpath, self._gitremote('origin')))
1520 (self._relpath, self._gitremote('origin')))
1511 # try only origin: the originally cloned repo
1521 # try only origin: the originally cloned repo
1512 self._gitcommand(['fetch'])
1522 self._gitcommand(['fetch'])
1513 if not self._githavelocally(revision):
1523 if not self._githavelocally(revision):
1514 raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
1524 raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
1515 (revision, self._relpath))
1525 (revision, self._relpath))
1516
1526
1517 @annotatesubrepoerror
1527 @annotatesubrepoerror
1518 def dirty(self, ignoreupdate=False):
1528 def dirty(self, ignoreupdate=False):
1519 if self._gitmissing():
1529 if self._gitmissing():
1520 return self._state[1] != ''
1530 return self._state[1] != ''
1521 if self._gitisbare():
1531 if self._gitisbare():
1522 return True
1532 return True
1523 if not ignoreupdate and self._state[1] != self._gitstate():
1533 if not ignoreupdate and self._state[1] != self._gitstate():
1524 # different version checked out
1534 # different version checked out
1525 return True
1535 return True
1526 # check for staged changes or modified files; ignore untracked files
1536 # check for staged changes or modified files; ignore untracked files
1527 self._gitupdatestat()
1537 self._gitupdatestat()
1528 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1538 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1529 return code == 1
1539 return code == 1
1530
1540
1531 def basestate(self):
1541 def basestate(self):
1532 return self._gitstate()
1542 return self._gitstate()
1533
1543
1534 @annotatesubrepoerror
1544 @annotatesubrepoerror
1535 def get(self, state, overwrite=False):
1545 def get(self, state, overwrite=False):
1536 source, revision, kind = state
1546 source, revision, kind = state
1537 if not revision:
1547 if not revision:
1538 self.remove()
1548 self.remove()
1539 return
1549 return
1540 self._fetch(source, revision)
1550 self._fetch(source, revision)
1541 # if the repo was set to be bare, unbare it
1551 # if the repo was set to be bare, unbare it
1542 if self._gitisbare():
1552 if self._gitisbare():
1543 self._gitcommand(['config', 'core.bare', 'false'])
1553 self._gitcommand(['config', 'core.bare', 'false'])
1544 if self._gitstate() == revision:
1554 if self._gitstate() == revision:
1545 self._gitcommand(['reset', '--hard', 'HEAD'])
1555 self._gitcommand(['reset', '--hard', 'HEAD'])
1546 return
1556 return
1547 elif self._gitstate() == revision:
1557 elif self._gitstate() == revision:
1548 if overwrite:
1558 if overwrite:
1549 # first reset the index to unmark new files for commit, because
1559 # first reset the index to unmark new files for commit, because
1550 # reset --hard will otherwise throw away files added for commit,
1560 # reset --hard will otherwise throw away files added for commit,
1551 # not just unmark them.
1561 # not just unmark them.
1552 self._gitcommand(['reset', 'HEAD'])
1562 self._gitcommand(['reset', 'HEAD'])
1553 self._gitcommand(['reset', '--hard', 'HEAD'])
1563 self._gitcommand(['reset', '--hard', 'HEAD'])
1554 return
1564 return
1555 branch2rev, rev2branch = self._gitbranchmap()
1565 branch2rev, rev2branch = self._gitbranchmap()
1556
1566
1557 def checkout(args):
1567 def checkout(args):
1558 cmd = ['checkout']
1568 cmd = ['checkout']
1559 if overwrite:
1569 if overwrite:
1560 # first reset the index to unmark new files for commit, because
1570 # first reset the index to unmark new files for commit, because
1561 # the -f option will otherwise throw away files added for
1571 # the -f option will otherwise throw away files added for
1562 # commit, not just unmark them.
1572 # commit, not just unmark them.
1563 self._gitcommand(['reset', 'HEAD'])
1573 self._gitcommand(['reset', 'HEAD'])
1564 cmd.append('-f')
1574 cmd.append('-f')
1565 self._gitcommand(cmd + args)
1575 self._gitcommand(cmd + args)
1566 _sanitize(self.ui, self.wvfs, '.git')
1576 _sanitize(self.ui, self.wvfs, '.git')
1567
1577
1568 def rawcheckout():
1578 def rawcheckout():
1569 # no branch to checkout, check it out with no branch
1579 # no branch to checkout, check it out with no branch
1570 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1580 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1571 self._relpath)
1581 self._relpath)
1572 self.ui.warn(_('check out a git branch if you intend '
1582 self.ui.warn(_('check out a git branch if you intend '
1573 'to make changes\n'))
1583 'to make changes\n'))
1574 checkout(['-q', revision])
1584 checkout(['-q', revision])
1575
1585
1576 if revision not in rev2branch:
1586 if revision not in rev2branch:
1577 rawcheckout()
1587 rawcheckout()
1578 return
1588 return
1579 branches = rev2branch[revision]
1589 branches = rev2branch[revision]
1580 firstlocalbranch = None
1590 firstlocalbranch = None
1581 for b in branches:
1591 for b in branches:
1582 if b == 'refs/heads/master':
1592 if b == 'refs/heads/master':
1583 # master trumps all other branches
1593 # master trumps all other branches
1584 checkout(['refs/heads/master'])
1594 checkout(['refs/heads/master'])
1585 return
1595 return
1586 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1596 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1587 firstlocalbranch = b
1597 firstlocalbranch = b
1588 if firstlocalbranch:
1598 if firstlocalbranch:
1589 checkout([firstlocalbranch])
1599 checkout([firstlocalbranch])
1590 return
1600 return
1591
1601
1592 tracking = self._gittracking(branch2rev.keys())
1602 tracking = self._gittracking(branch2rev.keys())
1593 # choose a remote branch already tracked if possible
1603 # choose a remote branch already tracked if possible
1594 remote = branches[0]
1604 remote = branches[0]
1595 if remote not in tracking:
1605 if remote not in tracking:
1596 for b in branches:
1606 for b in branches:
1597 if b in tracking:
1607 if b in tracking:
1598 remote = b
1608 remote = b
1599 break
1609 break
1600
1610
1601 if remote not in tracking:
1611 if remote not in tracking:
1602 # create a new local tracking branch
1612 # create a new local tracking branch
1603 local = remote.split('/', 3)[3]
1613 local = remote.split('/', 3)[3]
1604 checkout(['-b', local, remote])
1614 checkout(['-b', local, remote])
1605 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1615 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1606 # When updating to a tracked remote branch,
1616 # When updating to a tracked remote branch,
1607 # if the local tracking branch is downstream of it,
1617 # if the local tracking branch is downstream of it,
1608 # a normal `git pull` would have performed a "fast-forward merge"
1618 # a normal `git pull` would have performed a "fast-forward merge"
1609 # which is equivalent to updating the local branch to the remote.
1619 # which is equivalent to updating the local branch to the remote.
1610 # Since we are only looking at branching at update, we need to
1620 # Since we are only looking at branching at update, we need to
1611 # detect this situation and perform this action lazily.
1621 # detect this situation and perform this action lazily.
1612 if tracking[remote] != self._gitcurrentbranch():
1622 if tracking[remote] != self._gitcurrentbranch():
1613 checkout([tracking[remote]])
1623 checkout([tracking[remote]])
1614 self._gitcommand(['merge', '--ff', remote])
1624 self._gitcommand(['merge', '--ff', remote])
1615 _sanitize(self.ui, self.wvfs, '.git')
1625 _sanitize(self.ui, self.wvfs, '.git')
1616 else:
1626 else:
1617 # a real merge would be required, just checkout the revision
1627 # a real merge would be required, just checkout the revision
1618 rawcheckout()
1628 rawcheckout()
1619
1629
1620 @annotatesubrepoerror
1630 @annotatesubrepoerror
1621 def commit(self, text, user, date):
1631 def commit(self, text, user, date):
1622 if self._gitmissing():
1632 if self._gitmissing():
1623 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1633 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1624 cmd = ['commit', '-a', '-m', text]
1634 cmd = ['commit', '-a', '-m', text]
1625 env = os.environ.copy()
1635 env = os.environ.copy()
1626 if user:
1636 if user:
1627 cmd += ['--author', user]
1637 cmd += ['--author', user]
1628 if date:
1638 if date:
1629 # git's date parser silently ignores when seconds < 1e9
1639 # git's date parser silently ignores when seconds < 1e9
1630 # convert to ISO8601
1640 # convert to ISO8601
1631 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1641 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1632 '%Y-%m-%dT%H:%M:%S %1%2')
1642 '%Y-%m-%dT%H:%M:%S %1%2')
1633 self._gitcommand(cmd, env=env)
1643 self._gitcommand(cmd, env=env)
1634 # make sure commit works otherwise HEAD might not exist under certain
1644 # make sure commit works otherwise HEAD might not exist under certain
1635 # circumstances
1645 # circumstances
1636 return self._gitstate()
1646 return self._gitstate()
1637
1647
1638 @annotatesubrepoerror
1648 @annotatesubrepoerror
1639 def merge(self, state):
1649 def merge(self, state):
1640 source, revision, kind = state
1650 source, revision, kind = state
1641 self._fetch(source, revision)
1651 self._fetch(source, revision)
1642 base = self._gitcommand(['merge-base', revision, self._state[1]])
1652 base = self._gitcommand(['merge-base', revision, self._state[1]])
1643 self._gitupdatestat()
1653 self._gitupdatestat()
1644 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1654 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1645
1655
1646 def mergefunc():
1656 def mergefunc():
1647 if base == revision:
1657 if base == revision:
1648 self.get(state) # fast forward merge
1658 self.get(state) # fast forward merge
1649 elif base != self._state[1]:
1659 elif base != self._state[1]:
1650 self._gitcommand(['merge', '--no-commit', revision])
1660 self._gitcommand(['merge', '--no-commit', revision])
1651 _sanitize(self.ui, self.wvfs, '.git')
1661 _sanitize(self.ui, self.wvfs, '.git')
1652
1662
1653 if self.dirty():
1663 if self.dirty():
1654 if self._gitstate() != revision:
1664 if self._gitstate() != revision:
1655 dirty = self._gitstate() == self._state[1] or code != 0
1665 dirty = self._gitstate() == self._state[1] or code != 0
1656 if _updateprompt(self.ui, self, dirty,
1666 if _updateprompt(self.ui, self, dirty,
1657 self._state[1][:7], revision[:7]):
1667 self._state[1][:7], revision[:7]):
1658 mergefunc()
1668 mergefunc()
1659 else:
1669 else:
1660 mergefunc()
1670 mergefunc()
1661
1671
1662 @annotatesubrepoerror
1672 @annotatesubrepoerror
1663 def push(self, opts):
1673 def push(self, opts):
1664 force = opts.get('force')
1674 force = opts.get('force')
1665
1675
1666 if not self._state[1]:
1676 if not self._state[1]:
1667 return True
1677 return True
1668 if self._gitmissing():
1678 if self._gitmissing():
1669 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1679 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1670 # if a branch in origin contains the revision, nothing to do
1680 # if a branch in origin contains the revision, nothing to do
1671 branch2rev, rev2branch = self._gitbranchmap()
1681 branch2rev, rev2branch = self._gitbranchmap()
1672 if self._state[1] in rev2branch:
1682 if self._state[1] in rev2branch:
1673 for b in rev2branch[self._state[1]]:
1683 for b in rev2branch[self._state[1]]:
1674 if b.startswith('refs/remotes/origin/'):
1684 if b.startswith('refs/remotes/origin/'):
1675 return True
1685 return True
1676 for b, revision in branch2rev.iteritems():
1686 for b, revision in branch2rev.iteritems():
1677 if b.startswith('refs/remotes/origin/'):
1687 if b.startswith('refs/remotes/origin/'):
1678 if self._gitisancestor(self._state[1], revision):
1688 if self._gitisancestor(self._state[1], revision):
1679 return True
1689 return True
1680 # otherwise, try to push the currently checked out branch
1690 # otherwise, try to push the currently checked out branch
1681 cmd = ['push']
1691 cmd = ['push']
1682 if force:
1692 if force:
1683 cmd.append('--force')
1693 cmd.append('--force')
1684
1694
1685 current = self._gitcurrentbranch()
1695 current = self._gitcurrentbranch()
1686 if current:
1696 if current:
1687 # determine if the current branch is even useful
1697 # determine if the current branch is even useful
1688 if not self._gitisancestor(self._state[1], current):
1698 if not self._gitisancestor(self._state[1], current):
1689 self.ui.warn(_('unrelated git branch checked out '
1699 self.ui.warn(_('unrelated git branch checked out '
1690 'in subrepo %s\n') % self._relpath)
1700 'in subrepo %s\n') % self._relpath)
1691 return False
1701 return False
1692 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1702 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1693 (current.split('/', 2)[2], self._relpath))
1703 (current.split('/', 2)[2], self._relpath))
1694 ret = self._gitdir(cmd + ['origin', current])
1704 ret = self._gitdir(cmd + ['origin', current])
1695 return ret[1] == 0
1705 return ret[1] == 0
1696 else:
1706 else:
1697 self.ui.warn(_('no branch checked out in subrepo %s\n'
1707 self.ui.warn(_('no branch checked out in subrepo %s\n'
1698 'cannot push revision %s\n') %
1708 'cannot push revision %s\n') %
1699 (self._relpath, self._state[1]))
1709 (self._relpath, self._state[1]))
1700 return False
1710 return False
1701
1711
1702 @annotatesubrepoerror
1712 @annotatesubrepoerror
1703 def add(self, ui, match, prefix, explicitonly, **opts):
1713 def add(self, ui, match, prefix, explicitonly, **opts):
1704 if self._gitmissing():
1714 if self._gitmissing():
1705 return []
1715 return []
1706
1716
1707 (modified, added, removed,
1717 (modified, added, removed,
1708 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1718 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1709 clean=True)
1719 clean=True)
1710
1720
1711 tracked = set()
1721 tracked = set()
1712 # dirstates 'amn' warn, 'r' is added again
1722 # dirstates 'amn' warn, 'r' is added again
1713 for l in (modified, added, deleted, clean):
1723 for l in (modified, added, deleted, clean):
1714 tracked.update(l)
1724 tracked.update(l)
1715
1725
1716 # Unknown files not of interest will be rejected by the matcher
1726 # Unknown files not of interest will be rejected by the matcher
1717 files = unknown
1727 files = unknown
1718 files.extend(match.files())
1728 files.extend(match.files())
1719
1729
1720 rejected = []
1730 rejected = []
1721
1731
1722 files = [f for f in sorted(set(files)) if match(f)]
1732 files = [f for f in sorted(set(files)) if match(f)]
1723 for f in files:
1733 for f in files:
1724 exact = match.exact(f)
1734 exact = match.exact(f)
1725 command = ["add"]
1735 command = ["add"]
1726 if exact:
1736 if exact:
1727 command.append("-f") #should be added, even if ignored
1737 command.append("-f") #should be added, even if ignored
1728 if ui.verbose or not exact:
1738 if ui.verbose or not exact:
1729 ui.status(_('adding %s\n') % match.rel(f))
1739 ui.status(_('adding %s\n') % match.rel(f))
1730
1740
1731 if f in tracked: # hg prints 'adding' even if already tracked
1741 if f in tracked: # hg prints 'adding' even if already tracked
1732 if exact:
1742 if exact:
1733 rejected.append(f)
1743 rejected.append(f)
1734 continue
1744 continue
1735 if not opts.get('dry_run'):
1745 if not opts.get('dry_run'):
1736 self._gitcommand(command + [f])
1746 self._gitcommand(command + [f])
1737
1747
1738 for f in rejected:
1748 for f in rejected:
1739 ui.warn(_("%s already tracked!\n") % match.abs(f))
1749 ui.warn(_("%s already tracked!\n") % match.abs(f))
1740
1750
1741 return rejected
1751 return rejected
1742
1752
1743 @annotatesubrepoerror
1753 @annotatesubrepoerror
1744 def remove(self):
1754 def remove(self):
1745 if self._gitmissing():
1755 if self._gitmissing():
1746 return
1756 return
1747 if self.dirty():
1757 if self.dirty():
1748 self.ui.warn(_('not removing repo %s because '
1758 self.ui.warn(_('not removing repo %s because '
1749 'it has changes.\n') % self._relpath)
1759 'it has changes.\n') % self._relpath)
1750 return
1760 return
1751 # we can't fully delete the repository as it may contain
1761 # we can't fully delete the repository as it may contain
1752 # local-only history
1762 # local-only history
1753 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1763 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1754 self._gitcommand(['config', 'core.bare', 'true'])
1764 self._gitcommand(['config', 'core.bare', 'true'])
1755 for f, kind in self.wvfs.readdir():
1765 for f, kind in self.wvfs.readdir():
1756 if f == '.git':
1766 if f == '.git':
1757 continue
1767 continue
1758 if kind == stat.S_IFDIR:
1768 if kind == stat.S_IFDIR:
1759 self.wvfs.rmtree(f)
1769 self.wvfs.rmtree(f)
1760 else:
1770 else:
1761 self.wvfs.unlink(f)
1771 self.wvfs.unlink(f)
1762
1772
1763 def archive(self, archiver, prefix, match=None):
1773 def archive(self, archiver, prefix, match=None):
1764 total = 0
1774 total = 0
1765 source, revision = self._state
1775 source, revision = self._state
1766 if not revision:
1776 if not revision:
1767 return total
1777 return total
1768 self._fetch(source, revision)
1778 self._fetch(source, revision)
1769
1779
1770 # Parse git's native archive command.
1780 # Parse git's native archive command.
1771 # This should be much faster than manually traversing the trees
1781 # This should be much faster than manually traversing the trees
1772 # and objects with many subprocess calls.
1782 # and objects with many subprocess calls.
1773 tarstream = self._gitcommand(['archive', revision], stream=True)
1783 tarstream = self._gitcommand(['archive', revision], stream=True)
1774 tar = tarfile.open(fileobj=tarstream, mode='r|')
1784 tar = tarfile.open(fileobj=tarstream, mode='r|')
1775 relpath = subrelpath(self)
1785 relpath = subrelpath(self)
1776 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1786 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1777 for i, info in enumerate(tar):
1787 for i, info in enumerate(tar):
1778 if info.isdir():
1788 if info.isdir():
1779 continue
1789 continue
1780 if match and not match(info.name):
1790 if match and not match(info.name):
1781 continue
1791 continue
1782 if info.issym():
1792 if info.issym():
1783 data = info.linkname
1793 data = info.linkname
1784 else:
1794 else:
1785 data = tar.extractfile(info).read()
1795 data = tar.extractfile(info).read()
1786 archiver.addfile(prefix + self._path + '/' + info.name,
1796 archiver.addfile(prefix + self._path + '/' + info.name,
1787 info.mode, info.issym(), data)
1797 info.mode, info.issym(), data)
1788 total += 1
1798 total += 1
1789 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1799 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1790 unit=_('files'))
1800 unit=_('files'))
1791 self.ui.progress(_('archiving (%s)') % relpath, None)
1801 self.ui.progress(_('archiving (%s)') % relpath, None)
1792 return total
1802 return total
1793
1803
1794
1804
1795 @annotatesubrepoerror
1805 @annotatesubrepoerror
1796 def cat(self, match, prefix, **opts):
1806 def cat(self, match, prefix, **opts):
1797 rev = self._state[1]
1807 rev = self._state[1]
1798 if match.anypats():
1808 if match.anypats():
1799 return 1 #No support for include/exclude yet
1809 return 1 #No support for include/exclude yet
1800
1810
1801 if not match.files():
1811 if not match.files():
1802 return 1
1812 return 1
1803
1813
1804 for f in match.files():
1814 for f in match.files():
1805 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1815 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1806 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1816 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1807 self._ctx.node(),
1817 self._ctx.node(),
1808 pathname=self.wvfs.reljoin(prefix, f))
1818 pathname=self.wvfs.reljoin(prefix, f))
1809 fp.write(output)
1819 fp.write(output)
1810 fp.close()
1820 fp.close()
1811 return 0
1821 return 0
1812
1822
1813
1823
1814 @annotatesubrepoerror
1824 @annotatesubrepoerror
1815 def status(self, rev2, **opts):
1825 def status(self, rev2, **opts):
1816 rev1 = self._state[1]
1826 rev1 = self._state[1]
1817 if self._gitmissing() or not rev1:
1827 if self._gitmissing() or not rev1:
1818 # if the repo is missing, return no results
1828 # if the repo is missing, return no results
1819 return scmutil.status([], [], [], [], [], [], [])
1829 return scmutil.status([], [], [], [], [], [], [])
1820 modified, added, removed = [], [], []
1830 modified, added, removed = [], [], []
1821 self._gitupdatestat()
1831 self._gitupdatestat()
1822 if rev2:
1832 if rev2:
1823 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1833 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1824 else:
1834 else:
1825 command = ['diff-index', '--no-renames', rev1]
1835 command = ['diff-index', '--no-renames', rev1]
1826 out = self._gitcommand(command)
1836 out = self._gitcommand(command)
1827 for line in out.split('\n'):
1837 for line in out.split('\n'):
1828 tab = line.find('\t')
1838 tab = line.find('\t')
1829 if tab == -1:
1839 if tab == -1:
1830 continue
1840 continue
1831 status, f = line[tab - 1], line[tab + 1:]
1841 status, f = line[tab - 1], line[tab + 1:]
1832 if status == 'M':
1842 if status == 'M':
1833 modified.append(f)
1843 modified.append(f)
1834 elif status == 'A':
1844 elif status == 'A':
1835 added.append(f)
1845 added.append(f)
1836 elif status == 'D':
1846 elif status == 'D':
1837 removed.append(f)
1847 removed.append(f)
1838
1848
1839 deleted, unknown, ignored, clean = [], [], [], []
1849 deleted, unknown, ignored, clean = [], [], [], []
1840
1850
1841 command = ['status', '--porcelain', '-z']
1851 command = ['status', '--porcelain', '-z']
1842 if opts.get('unknown'):
1852 if opts.get('unknown'):
1843 command += ['--untracked-files=all']
1853 command += ['--untracked-files=all']
1844 if opts.get('ignored'):
1854 if opts.get('ignored'):
1845 command += ['--ignored']
1855 command += ['--ignored']
1846 out = self._gitcommand(command)
1856 out = self._gitcommand(command)
1847
1857
1848 changedfiles = set()
1858 changedfiles = set()
1849 changedfiles.update(modified)
1859 changedfiles.update(modified)
1850 changedfiles.update(added)
1860 changedfiles.update(added)
1851 changedfiles.update(removed)
1861 changedfiles.update(removed)
1852 for line in out.split('\0'):
1862 for line in out.split('\0'):
1853 if not line:
1863 if not line:
1854 continue
1864 continue
1855 st = line[0:2]
1865 st = line[0:2]
1856 #moves and copies show 2 files on one line
1866 #moves and copies show 2 files on one line
1857 if line.find('\0') >= 0:
1867 if line.find('\0') >= 0:
1858 filename1, filename2 = line[3:].split('\0')
1868 filename1, filename2 = line[3:].split('\0')
1859 else:
1869 else:
1860 filename1 = line[3:]
1870 filename1 = line[3:]
1861 filename2 = None
1871 filename2 = None
1862
1872
1863 changedfiles.add(filename1)
1873 changedfiles.add(filename1)
1864 if filename2:
1874 if filename2:
1865 changedfiles.add(filename2)
1875 changedfiles.add(filename2)
1866
1876
1867 if st == '??':
1877 if st == '??':
1868 unknown.append(filename1)
1878 unknown.append(filename1)
1869 elif st == '!!':
1879 elif st == '!!':
1870 ignored.append(filename1)
1880 ignored.append(filename1)
1871
1881
1872 if opts.get('clean'):
1882 if opts.get('clean'):
1873 out = self._gitcommand(['ls-files'])
1883 out = self._gitcommand(['ls-files'])
1874 for f in out.split('\n'):
1884 for f in out.split('\n'):
1875 if not f in changedfiles:
1885 if not f in changedfiles:
1876 clean.append(f)
1886 clean.append(f)
1877
1887
1878 return scmutil.status(modified, added, removed, deleted,
1888 return scmutil.status(modified, added, removed, deleted,
1879 unknown, ignored, clean)
1889 unknown, ignored, clean)
1880
1890
1881 @annotatesubrepoerror
1891 @annotatesubrepoerror
1882 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1892 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1883 node1 = self._state[1]
1893 node1 = self._state[1]
1884 cmd = ['diff', '--no-renames']
1894 cmd = ['diff', '--no-renames']
1885 if opts['stat']:
1895 if opts['stat']:
1886 cmd.append('--stat')
1896 cmd.append('--stat')
1887 else:
1897 else:
1888 # for Git, this also implies '-p'
1898 # for Git, this also implies '-p'
1889 cmd.append('-U%d' % diffopts.context)
1899 cmd.append('-U%d' % diffopts.context)
1890
1900
1891 gitprefix = self.wvfs.reljoin(prefix, self._path)
1901 gitprefix = self.wvfs.reljoin(prefix, self._path)
1892
1902
1893 if diffopts.noprefix:
1903 if diffopts.noprefix:
1894 cmd.extend(['--src-prefix=%s/' % gitprefix,
1904 cmd.extend(['--src-prefix=%s/' % gitprefix,
1895 '--dst-prefix=%s/' % gitprefix])
1905 '--dst-prefix=%s/' % gitprefix])
1896 else:
1906 else:
1897 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1907 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1898 '--dst-prefix=b/%s/' % gitprefix])
1908 '--dst-prefix=b/%s/' % gitprefix])
1899
1909
1900 if diffopts.ignorews:
1910 if diffopts.ignorews:
1901 cmd.append('--ignore-all-space')
1911 cmd.append('--ignore-all-space')
1902 if diffopts.ignorewsamount:
1912 if diffopts.ignorewsamount:
1903 cmd.append('--ignore-space-change')
1913 cmd.append('--ignore-space-change')
1904 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1914 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1905 and diffopts.ignoreblanklines:
1915 and diffopts.ignoreblanklines:
1906 cmd.append('--ignore-blank-lines')
1916 cmd.append('--ignore-blank-lines')
1907
1917
1908 cmd.append(node1)
1918 cmd.append(node1)
1909 if node2:
1919 if node2:
1910 cmd.append(node2)
1920 cmd.append(node2)
1911
1921
1912 output = ""
1922 output = ""
1913 if match.always():
1923 if match.always():
1914 output += self._gitcommand(cmd) + '\n'
1924 output += self._gitcommand(cmd) + '\n'
1915 else:
1925 else:
1916 st = self.status(node2)[:3]
1926 st = self.status(node2)[:3]
1917 files = [f for sublist in st for f in sublist]
1927 files = [f for sublist in st for f in sublist]
1918 for f in files:
1928 for f in files:
1919 if match(f):
1929 if match(f):
1920 output += self._gitcommand(cmd + ['--', f]) + '\n'
1930 output += self._gitcommand(cmd + ['--', f]) + '\n'
1921
1931
1922 if output.strip():
1932 if output.strip():
1923 ui.write(output)
1933 ui.write(output)
1924
1934
1925 @annotatesubrepoerror
1935 @annotatesubrepoerror
1926 def revert(self, substate, *pats, **opts):
1936 def revert(self, substate, *pats, **opts):
1927 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1937 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1928 if not opts.get('no_backup'):
1938 if not opts.get('no_backup'):
1929 status = self.status(None)
1939 status = self.status(None)
1930 names = status.modified
1940 names = status.modified
1931 for name in names:
1941 for name in names:
1932 bakname = scmutil.origpath(self.ui, self._subparent, name)
1942 bakname = scmutil.origpath(self.ui, self._subparent, name)
1933 self.ui.note(_('saving current version of %s as %s\n') %
1943 self.ui.note(_('saving current version of %s as %s\n') %
1934 (name, bakname))
1944 (name, bakname))
1935 self.wvfs.rename(name, bakname)
1945 self.wvfs.rename(name, bakname)
1936
1946
1937 if not opts.get('dry_run'):
1947 if not opts.get('dry_run'):
1938 self.get(substate, overwrite=True)
1948 self.get(substate, overwrite=True)
1939 return []
1949 return []
1940
1950
1941 def shortid(self, revid):
1951 def shortid(self, revid):
1942 return revid[:7]
1952 return revid[:7]
1943
1953
1944 types = {
1954 types = {
1945 'hg': hgsubrepo,
1955 'hg': hgsubrepo,
1946 'svn': svnsubrepo,
1956 'svn': svnsubrepo,
1947 'git': gitsubrepo,
1957 'git': gitsubrepo,
1948 }
1958 }
@@ -1,744 +1,744 b''
1 This file focuses mainly on updating largefiles in the working
1 This file focuses mainly on updating largefiles in the working
2 directory (and ".hg/largefiles/dirstate")
2 directory (and ".hg/largefiles/dirstate")
3
3
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [ui]
5 > [ui]
6 > merge = internal:fail
6 > merge = internal:fail
7 > [extensions]
7 > [extensions]
8 > largefiles =
8 > largefiles =
9 > [extdiff]
9 > [extdiff]
10 > # for portability:
10 > # for portability:
11 > pdiff = sh "$RUNTESTDIR/pdiff"
11 > pdiff = sh "$RUNTESTDIR/pdiff"
12 > EOF
12 > EOF
13
13
14 $ hg init repo
14 $ hg init repo
15 $ cd repo
15 $ cd repo
16
16
17 $ echo large1 > large1
17 $ echo large1 > large1
18 $ echo large2 > large2
18 $ echo large2 > large2
19 $ hg add --large large1 large2
19 $ hg add --large large1 large2
20 $ echo normal1 > normal1
20 $ echo normal1 > normal1
21 $ hg add normal1
21 $ hg add normal1
22 $ hg commit -m '#0'
22 $ hg commit -m '#0'
23 $ echo 'large1 in #1' > large1
23 $ echo 'large1 in #1' > large1
24 $ echo 'normal1 in #1' > normal1
24 $ echo 'normal1 in #1' > normal1
25 $ hg commit -m '#1'
25 $ hg commit -m '#1'
26 $ hg pdiff -r '.^' --config extensions.extdiff=
26 $ hg pdiff -r '.^' --config extensions.extdiff=
27 diff -Nru repo.0d9d9b8dc9a3/.hglf/large1 repo/.hglf/large1
27 diff -Nru repo.0d9d9b8dc9a3/.hglf/large1 repo/.hglf/large1
28 --- repo.0d9d9b8dc9a3/.hglf/large1 * (glob)
28 --- repo.0d9d9b8dc9a3/.hglf/large1 * (glob)
29 +++ repo/.hglf/large1 * (glob)
29 +++ repo/.hglf/large1 * (glob)
30 @@ -1* +1* @@ (glob)
30 @@ -1* +1* @@ (glob)
31 -4669e532d5b2c093a78eca010077e708a071bb64
31 -4669e532d5b2c093a78eca010077e708a071bb64
32 +58e24f733a964da346e2407a2bee99d9001184f5
32 +58e24f733a964da346e2407a2bee99d9001184f5
33 diff -Nru repo.0d9d9b8dc9a3/normal1 repo/normal1
33 diff -Nru repo.0d9d9b8dc9a3/normal1 repo/normal1
34 --- repo.0d9d9b8dc9a3/normal1 * (glob)
34 --- repo.0d9d9b8dc9a3/normal1 * (glob)
35 +++ repo/normal1 * (glob)
35 +++ repo/normal1 * (glob)
36 @@ -1* +1* @@ (glob)
36 @@ -1* +1* @@ (glob)
37 -normal1
37 -normal1
38 +normal1 in #1
38 +normal1 in #1
39 [1]
39 [1]
40 $ hg update -q -C 0
40 $ hg update -q -C 0
41 $ echo 'large2 in #2' > large2
41 $ echo 'large2 in #2' > large2
42 $ hg commit -m '#2'
42 $ hg commit -m '#2'
43 created new head
43 created new head
44
44
45 Test that update also updates the lfdirstate of 'unsure' largefiles after
45 Test that update also updates the lfdirstate of 'unsure' largefiles after
46 hashing them:
46 hashing them:
47
47
48 The previous operations will usually have left us with largefiles with a mtime
48 The previous operations will usually have left us with largefiles with a mtime
49 within the same second as the dirstate was written.
49 within the same second as the dirstate was written.
50 The lfdirstate entries will thus have been written with an invalidated/unset
50 The lfdirstate entries will thus have been written with an invalidated/unset
51 mtime to make sure further changes within the same second is detected.
51 mtime to make sure further changes within the same second is detected.
52 We will however occasionally be "lucky" and get a tick between writing
52 We will however occasionally be "lucky" and get a tick between writing
53 largefiles and writing dirstate so we get valid lfdirstate timestamps. The
53 largefiles and writing dirstate so we get valid lfdirstate timestamps. The
54 following verification is thus disabled but can be verified manually.
54 following verification is thus disabled but can be verified manually.
55
55
56 #if false
56 #if false
57 $ hg debugdirstate --large --nodate
57 $ hg debugdirstate --large --nodate
58 n 644 7 unset large1
58 n 644 7 unset large1
59 n 644 13 unset large2
59 n 644 13 unset large2
60 #endif
60 #endif
61
61
62 Wait to make sure we get a tick so the mtime of the largefiles become valid.
62 Wait to make sure we get a tick so the mtime of the largefiles become valid.
63
63
64 $ sleep 1
64 $ sleep 1
65
65
66 A linear merge will update standins before performing the actual merge. It will
66 A linear merge will update standins before performing the actual merge. It will
67 do a lfdirstate status walk and find 'unset'/'unsure' files, hash them, and
67 do a lfdirstate status walk and find 'unset'/'unsure' files, hash them, and
68 update the corresponding standins.
68 update the corresponding standins.
69 Verify that it actually marks the clean files as clean in lfdirstate so
69 Verify that it actually marks the clean files as clean in lfdirstate so
70 we don't have to hash them again next time we update.
70 we don't have to hash them again next time we update.
71
71
72 $ hg up
72 $ hg up
73 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
73 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 1 other heads for branch "default"
74 1 other heads for branch "default"
75 $ hg debugdirstate --large --nodate
75 $ hg debugdirstate --large --nodate
76 n 644 7 set large1
76 n 644 7 set large1
77 n 644 13 set large2
77 n 644 13 set large2
78
78
79 Test that lfdirstate keeps track of last modification of largefiles and
79 Test that lfdirstate keeps track of last modification of largefiles and
80 prevents unnecessary hashing of content - also after linear/noop update
80 prevents unnecessary hashing of content - also after linear/noop update
81
81
82 $ sleep 1
82 $ sleep 1
83 $ hg st
83 $ hg st
84 $ hg debugdirstate --large --nodate
84 $ hg debugdirstate --large --nodate
85 n 644 7 set large1
85 n 644 7 set large1
86 n 644 13 set large2
86 n 644 13 set large2
87 $ hg up
87 $ hg up
88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 1 other heads for branch "default"
89 1 other heads for branch "default"
90 $ hg debugdirstate --large --nodate
90 $ hg debugdirstate --large --nodate
91 n 644 7 set large1
91 n 644 7 set large1
92 n 644 13 set large2
92 n 644 13 set large2
93
93
94 Test that "hg merge" updates largefiles from "other" correctly
94 Test that "hg merge" updates largefiles from "other" correctly
95
95
96 (getting largefiles from "other" normally)
96 (getting largefiles from "other" normally)
97
97
98 $ hg status -A large1
98 $ hg status -A large1
99 C large1
99 C large1
100 $ cat large1
100 $ cat large1
101 large1
101 large1
102 $ cat .hglf/large1
102 $ cat .hglf/large1
103 4669e532d5b2c093a78eca010077e708a071bb64
103 4669e532d5b2c093a78eca010077e708a071bb64
104 $ hg merge --config debug.dirstate.delaywrite=2
104 $ hg merge --config debug.dirstate.delaywrite=2
105 getting changed largefiles
105 getting changed largefiles
106 1 largefiles updated, 0 removed
106 1 largefiles updated, 0 removed
107 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 (branch merge, don't forget to commit)
108 (branch merge, don't forget to commit)
109 $ hg status -A large1
109 $ hg status -A large1
110 M large1
110 M large1
111 $ cat large1
111 $ cat large1
112 large1 in #1
112 large1 in #1
113 $ cat .hglf/large1
113 $ cat .hglf/large1
114 58e24f733a964da346e2407a2bee99d9001184f5
114 58e24f733a964da346e2407a2bee99d9001184f5
115 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
115 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
116 -4669e532d5b2c093a78eca010077e708a071bb64
116 -4669e532d5b2c093a78eca010077e708a071bb64
117 +58e24f733a964da346e2407a2bee99d9001184f5
117 +58e24f733a964da346e2407a2bee99d9001184f5
118
118
119 (getting largefiles from "other" via conflict prompt)
119 (getting largefiles from "other" via conflict prompt)
120
120
121 $ hg update -q -C 2
121 $ hg update -q -C 2
122 $ echo 'large1 in #3' > large1
122 $ echo 'large1 in #3' > large1
123 $ echo 'normal1 in #3' > normal1
123 $ echo 'normal1 in #3' > normal1
124 $ hg commit -m '#3'
124 $ hg commit -m '#3'
125 $ cat .hglf/large1
125 $ cat .hglf/large1
126 e5bb990443d6a92aaf7223813720f7566c9dd05b
126 e5bb990443d6a92aaf7223813720f7566c9dd05b
127 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
127 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
128 > o
128 > o
129 > EOF
129 > EOF
130 largefile large1 has a merge conflict
130 largefile large1 has a merge conflict
131 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
131 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
132 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
132 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
133 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
133 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
134 merging normal1
134 merging normal1
135 warning: conflicts while merging normal1! (edit, then use 'hg resolve --mark')
135 warning: conflicts while merging normal1! (edit, then use 'hg resolve --mark')
136 getting changed largefiles
136 getting changed largefiles
137 1 largefiles updated, 0 removed
137 1 largefiles updated, 0 removed
138 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
138 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
139 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
139 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
140 [1]
140 [1]
141 $ hg status -A large1
141 $ hg status -A large1
142 M large1
142 M large1
143 $ cat large1
143 $ cat large1
144 large1 in #1
144 large1 in #1
145 $ cat .hglf/large1
145 $ cat .hglf/large1
146 58e24f733a964da346e2407a2bee99d9001184f5
146 58e24f733a964da346e2407a2bee99d9001184f5
147
147
148 (merge non-existing largefiles from "other" via conflict prompt -
148 (merge non-existing largefiles from "other" via conflict prompt -
149 make sure the following commit doesn't abort in a confusing way when trying to
149 make sure the following commit doesn't abort in a confusing way when trying to
150 mark the non-existing file as normal in lfdirstate)
150 mark the non-existing file as normal in lfdirstate)
151
151
152 $ mv .hg/largefiles/58e24f733a964da346e2407a2bee99d9001184f5 .
152 $ mv .hg/largefiles/58e24f733a964da346e2407a2bee99d9001184f5 .
153 $ hg update -q -C 3
153 $ hg update -q -C 3
154 $ hg merge --config largefiles.usercache=not --config debug.dirstate.delaywrite=2 --tool :local --config ui.interactive=True <<EOF
154 $ hg merge --config largefiles.usercache=not --config debug.dirstate.delaywrite=2 --tool :local --config ui.interactive=True <<EOF
155 > o
155 > o
156 > EOF
156 > EOF
157 largefile large1 has a merge conflict
157 largefile large1 has a merge conflict
158 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
158 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
159 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
159 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
160 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
160 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
161 getting changed largefiles
161 getting changed largefiles
162 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from file:/*/$TESTTMP/repo (glob)
162 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from file:/*/$TESTTMP/repo (glob)
163 0 largefiles updated, 0 removed
163 0 largefiles updated, 0 removed
164 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
164 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
165 (branch merge, don't forget to commit)
165 (branch merge, don't forget to commit)
166 $ hg commit -m '1-2-3 testing' --config largefiles.usercache=not
166 $ hg commit -m '1-2-3 testing' --config largefiles.usercache=not
167 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from local store
167 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from local store
168 $ hg up -C . --config largefiles.usercache=not
168 $ hg up -C . --config largefiles.usercache=not
169 getting changed largefiles
169 getting changed largefiles
170 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from file:/*/$TESTTMP/repo (glob)
170 large1: largefile 58e24f733a964da346e2407a2bee99d9001184f5 not available from file:/*/$TESTTMP/repo (glob)
171 0 largefiles updated, 0 removed
171 0 largefiles updated, 0 removed
172 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
172 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 $ hg st large1
173 $ hg st large1
174 ! large1
174 ! large1
175 $ hg rollback -q
175 $ hg rollback -q
176 $ mv 58e24f733a964da346e2407a2bee99d9001184f5 .hg/largefiles/
176 $ mv 58e24f733a964da346e2407a2bee99d9001184f5 .hg/largefiles/
177
177
178 Test that "hg revert -r REV" updates largefiles from "REV" correctly
178 Test that "hg revert -r REV" updates largefiles from "REV" correctly
179
179
180 $ hg update -q -C 3
180 $ hg update -q -C 3
181 $ hg status -A large1
181 $ hg status -A large1
182 C large1
182 C large1
183 $ cat large1
183 $ cat large1
184 large1 in #3
184 large1 in #3
185 $ cat .hglf/large1
185 $ cat .hglf/large1
186 e5bb990443d6a92aaf7223813720f7566c9dd05b
186 e5bb990443d6a92aaf7223813720f7566c9dd05b
187 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
187 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
188 -4669e532d5b2c093a78eca010077e708a071bb64
188 -4669e532d5b2c093a78eca010077e708a071bb64
189 +58e24f733a964da346e2407a2bee99d9001184f5
189 +58e24f733a964da346e2407a2bee99d9001184f5
190 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
190 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
191 $ hg status -A large1
191 $ hg status -A large1
192 M large1
192 M large1
193 $ cat large1
193 $ cat large1
194 large1 in #1
194 large1 in #1
195 $ cat .hglf/large1
195 $ cat .hglf/large1
196 58e24f733a964da346e2407a2bee99d9001184f5
196 58e24f733a964da346e2407a2bee99d9001184f5
197
197
198 Test that "hg rollback" restores status of largefiles correctly
198 Test that "hg rollback" restores status of largefiles correctly
199
199
200 $ hg update -C -q
200 $ hg update -C -q
201 $ hg remove large1
201 $ hg remove large1
202 $ test -f .hglf/large1
202 $ test -f .hglf/large1
203 [1]
203 [1]
204 $ hg forget large2
204 $ hg forget large2
205 $ test -f .hglf/large2
205 $ test -f .hglf/large2
206 [1]
206 [1]
207 $ echo largeX > largeX
207 $ echo largeX > largeX
208 $ hg add --large largeX
208 $ hg add --large largeX
209 $ cat .hglf/largeX
209 $ cat .hglf/largeX
210
210
211 $ hg commit -m 'will be rollback-ed soon'
211 $ hg commit -m 'will be rollback-ed soon'
212 $ echo largeY > largeY
212 $ echo largeY > largeY
213 $ hg add --large largeY
213 $ hg add --large largeY
214 #if windows
214 #if windows
215 $ hg status -A large1
215 $ hg status -A large1
216 large1: * (glob)
216 large1: * (glob)
217 #else
217 #else
218 $ hg status -A large1
218 $ hg status -A large1
219 large1: No such file or directory
219 large1: No such file or directory
220 #endif
220 #endif
221 $ hg status -A large2
221 $ hg status -A large2
222 ? large2
222 ? large2
223 $ hg status -A largeX
223 $ hg status -A largeX
224 C largeX
224 C largeX
225 $ hg status -A largeY
225 $ hg status -A largeY
226 A largeY
226 A largeY
227 $ hg rollback
227 $ hg rollback
228 repository tip rolled back to revision 3 (undo commit)
228 repository tip rolled back to revision 3 (undo commit)
229 working directory now based on revision 3
229 working directory now based on revision 3
230 $ hg status -A large1
230 $ hg status -A large1
231 R large1
231 R large1
232 $ test -f .hglf/large1
232 $ test -f .hglf/large1
233 [1]
233 [1]
234 $ hg status -A large2
234 $ hg status -A large2
235 R large2
235 R large2
236 $ test -f .hglf/large2
236 $ test -f .hglf/large2
237 [1]
237 [1]
238 $ hg status -A largeX
238 $ hg status -A largeX
239 A largeX
239 A largeX
240 $ cat .hglf/largeX
240 $ cat .hglf/largeX
241
241
242 $ hg status -A largeY
242 $ hg status -A largeY
243 ? largeY
243 ? largeY
244 $ test -f .hglf/largeY
244 $ test -f .hglf/largeY
245 [1]
245 [1]
246
246
247 Test that "hg rollback" restores standins correctly
247 Test that "hg rollback" restores standins correctly
248
248
249 $ hg commit -m 'will be rollback-ed soon'
249 $ hg commit -m 'will be rollback-ed soon'
250 $ hg update -q -C 2
250 $ hg update -q -C 2
251 $ cat large1
251 $ cat large1
252 large1
252 large1
253 $ cat .hglf/large1
253 $ cat .hglf/large1
254 4669e532d5b2c093a78eca010077e708a071bb64
254 4669e532d5b2c093a78eca010077e708a071bb64
255 $ cat large2
255 $ cat large2
256 large2 in #2
256 large2 in #2
257 $ cat .hglf/large2
257 $ cat .hglf/large2
258 3cfce6277e7668985707b6887ce56f9f62f6ccd9
258 3cfce6277e7668985707b6887ce56f9f62f6ccd9
259
259
260 $ hg rollback -q -f
260 $ hg rollback -q -f
261 $ cat large1
261 $ cat large1
262 large1
262 large1
263 $ cat .hglf/large1
263 $ cat .hglf/large1
264 4669e532d5b2c093a78eca010077e708a071bb64
264 4669e532d5b2c093a78eca010077e708a071bb64
265 $ cat large2
265 $ cat large2
266 large2 in #2
266 large2 in #2
267 $ cat .hglf/large2
267 $ cat .hglf/large2
268 3cfce6277e7668985707b6887ce56f9f62f6ccd9
268 3cfce6277e7668985707b6887ce56f9f62f6ccd9
269
269
270 (rollback the parent of the working directory, when the parent of it
270 (rollback the parent of the working directory, when the parent of it
271 is not branch-tip)
271 is not branch-tip)
272
272
273 $ hg update -q -C 1
273 $ hg update -q -C 1
274 $ cat .hglf/large1
274 $ cat .hglf/large1
275 58e24f733a964da346e2407a2bee99d9001184f5
275 58e24f733a964da346e2407a2bee99d9001184f5
276 $ cat .hglf/large2
276 $ cat .hglf/large2
277 1deebade43c8c498a3c8daddac0244dc55d1331d
277 1deebade43c8c498a3c8daddac0244dc55d1331d
278
278
279 $ echo normalX > normalX
279 $ echo normalX > normalX
280 $ hg add normalX
280 $ hg add normalX
281 $ hg commit -m 'will be rollback-ed soon'
281 $ hg commit -m 'will be rollback-ed soon'
282 $ hg rollback -q
282 $ hg rollback -q
283
283
284 $ cat .hglf/large1
284 $ cat .hglf/large1
285 58e24f733a964da346e2407a2bee99d9001184f5
285 58e24f733a964da346e2407a2bee99d9001184f5
286 $ cat .hglf/large2
286 $ cat .hglf/large2
287 1deebade43c8c498a3c8daddac0244dc55d1331d
287 1deebade43c8c498a3c8daddac0244dc55d1331d
288
288
289 Test that "hg status" shows status of largefiles correctly just after
289 Test that "hg status" shows status of largefiles correctly just after
290 automated commit like rebase/transplant
290 automated commit like rebase/transplant
291
291
292 $ cat >> .hg/hgrc <<EOF
292 $ cat >> .hg/hgrc <<EOF
293 > [extensions]
293 > [extensions]
294 > rebase =
294 > rebase =
295 > strip =
295 > strip =
296 > transplant =
296 > transplant =
297 > EOF
297 > EOF
298 $ hg update -q -C 1
298 $ hg update -q -C 1
299 $ hg remove large1
299 $ hg remove large1
300 $ echo largeX > largeX
300 $ echo largeX > largeX
301 $ hg add --large largeX
301 $ hg add --large largeX
302 $ hg commit -m '#4'
302 $ hg commit -m '#4'
303
303
304 $ hg rebase -s 1 -d 2 --keep
304 $ hg rebase -s 1 -d 2 --keep
305 rebasing 1:72518492caa6 "#1"
305 rebasing 1:72518492caa6 "#1"
306 rebasing 4:07d6153b5c04 "#4" (tip)
306 rebasing 4:07d6153b5c04 "#4" (tip)
307 #if windows
307 #if windows
308 $ hg status -A large1
308 $ hg status -A large1
309 large1: * (glob)
309 large1: * (glob)
310 #else
310 #else
311 $ hg status -A large1
311 $ hg status -A large1
312 large1: No such file or directory
312 large1: No such file or directory
313 #endif
313 #endif
314 $ hg status -A largeX
314 $ hg status -A largeX
315 C largeX
315 C largeX
316 $ hg strip -q 5
316 $ hg strip -q 5
317
317
318 $ hg update -q -C 2
318 $ hg update -q -C 2
319 $ hg transplant -q 1 4
319 $ hg transplant -q 1 4
320 #if windows
320 #if windows
321 $ hg status -A large1
321 $ hg status -A large1
322 large1: * (glob)
322 large1: * (glob)
323 #else
323 #else
324 $ hg status -A large1
324 $ hg status -A large1
325 large1: No such file or directory
325 large1: No such file or directory
326 #endif
326 #endif
327 $ hg status -A largeX
327 $ hg status -A largeX
328 C largeX
328 C largeX
329 $ hg strip -q 5
329 $ hg strip -q 5
330
330
331 $ hg update -q -C 2
331 $ hg update -q -C 2
332 $ hg transplant -q --merge 1 --merge 4
332 $ hg transplant -q --merge 1 --merge 4
333 #if windows
333 #if windows
334 $ hg status -A large1
334 $ hg status -A large1
335 large1: * (glob)
335 large1: * (glob)
336 #else
336 #else
337 $ hg status -A large1
337 $ hg status -A large1
338 large1: No such file or directory
338 large1: No such file or directory
339 #endif
339 #endif
340 $ hg status -A largeX
340 $ hg status -A largeX
341 C largeX
341 C largeX
342 $ hg strip -q 5
342 $ hg strip -q 5
343
343
344 Test that linear merge can detect modification (and conflict) correctly
344 Test that linear merge can detect modification (and conflict) correctly
345
345
346 (linear merge without conflict)
346 (linear merge without conflict)
347
347
348 $ echo 'large2 for linear merge (no conflict)' > large2
348 $ echo 'large2 for linear merge (no conflict)' > large2
349 $ hg update 3 --config debug.dirstate.delaywrite=2
349 $ hg update 3 --config debug.dirstate.delaywrite=2
350 getting changed largefiles
350 getting changed largefiles
351 1 largefiles updated, 0 removed
351 1 largefiles updated, 0 removed
352 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
352 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
353 $ hg status -A large2
353 $ hg status -A large2
354 M large2
354 M large2
355 $ cat large2
355 $ cat large2
356 large2 for linear merge (no conflict)
356 large2 for linear merge (no conflict)
357 $ cat .hglf/large2
357 $ cat .hglf/large2
358 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
358 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
359
359
360 (linear merge with conflict, choosing "other")
360 (linear merge with conflict, choosing "other")
361
361
362 $ hg update -q -C 2
362 $ hg update -q -C 2
363 $ echo 'large1 for linear merge (conflict)' > large1
363 $ echo 'large1 for linear merge (conflict)' > large1
364 $ hg update 3 --config ui.interactive=True <<EOF
364 $ hg update 3 --config ui.interactive=True <<EOF
365 > o
365 > o
366 > EOF
366 > EOF
367 largefile large1 has a merge conflict
367 largefile large1 has a merge conflict
368 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
368 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
369 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
369 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
370 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
370 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
371 getting changed largefiles
371 getting changed largefiles
372 1 largefiles updated, 0 removed
372 1 largefiles updated, 0 removed
373 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
373 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
374 $ hg status -A large1
374 $ hg status -A large1
375 C large1
375 C large1
376 $ cat large1
376 $ cat large1
377 large1 in #3
377 large1 in #3
378 $ cat .hglf/large1
378 $ cat .hglf/large1
379 e5bb990443d6a92aaf7223813720f7566c9dd05b
379 e5bb990443d6a92aaf7223813720f7566c9dd05b
380
380
381 (linear merge with conflict, choosing "local")
381 (linear merge with conflict, choosing "local")
382
382
383 $ hg update -q -C 2
383 $ hg update -q -C 2
384 $ echo 'large1 for linear merge (conflict)' > large1
384 $ echo 'large1 for linear merge (conflict)' > large1
385 $ hg update 3 --config debug.dirstate.delaywrite=2
385 $ hg update 3 --config debug.dirstate.delaywrite=2
386 largefile large1 has a merge conflict
386 largefile large1 has a merge conflict
387 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
387 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
388 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
388 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
389 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
389 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
390 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
390 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
391 $ hg status -A large1
391 $ hg status -A large1
392 M large1
392 M large1
393 $ cat large1
393 $ cat large1
394 large1 for linear merge (conflict)
394 large1 for linear merge (conflict)
395 $ cat .hglf/large1
395 $ cat .hglf/large1
396 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
396 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
397
397
398 Test a linear merge to a revision containing same-name normal file
398 Test a linear merge to a revision containing same-name normal file
399
399
400 $ hg update -q -C 3
400 $ hg update -q -C 3
401 $ hg remove large2
401 $ hg remove large2
402 $ echo 'large2 as normal file' > large2
402 $ echo 'large2 as normal file' > large2
403 $ hg add large2
403 $ hg add large2
404 $ echo 'large3 as normal file' > large3
404 $ echo 'large3 as normal file' > large3
405 $ hg add large3
405 $ hg add large3
406 $ hg commit -m '#5'
406 $ hg commit -m '#5'
407 $ hg manifest
407 $ hg manifest
408 .hglf/large1
408 .hglf/large1
409 large2
409 large2
410 large3
410 large3
411 normal1
411 normal1
412
412
413 (modified largefile is already switched to normal)
413 (modified largefile is already switched to normal)
414
414
415 $ hg update -q -C 2
415 $ hg update -q -C 2
416 $ echo 'modified large2 for linear merge' > large2
416 $ echo 'modified large2 for linear merge' > large2
417 $ hg update -q 5
417 $ hg update -q 5
418 remote turned local largefile large2 into a normal file
418 remote turned local largefile large2 into a normal file
419 keep (l)argefile or use (n)ormal file? l
419 keep (l)argefile or use (n)ormal file? l
420 $ hg debugdirstate --nodates | grep large2
420 $ hg debugdirstate --nodates | grep large2
421 a 0 -1 unset .hglf/large2
421 a 0 -1 unset .hglf/large2
422 r 0 0 set large2
422 r 0 0 set large2
423 $ hg status -A large2
423 $ hg status -A large2
424 A large2
424 A large2
425 $ cat large2
425 $ cat large2
426 modified large2 for linear merge
426 modified large2 for linear merge
427
427
428 (added largefile is already committed as normal)
428 (added largefile is already committed as normal)
429
429
430 $ hg update -q -C 2
430 $ hg update -q -C 2
431 $ echo 'large3 as large file for linear merge' > large3
431 $ echo 'large3 as large file for linear merge' > large3
432 $ hg add --large large3
432 $ hg add --large large3
433 $ hg update -q 5
433 $ hg update -q 5
434 remote turned local largefile large3 into a normal file
434 remote turned local largefile large3 into a normal file
435 keep (l)argefile or use (n)ormal file? l
435 keep (l)argefile or use (n)ormal file? l
436 $ hg debugdirstate --nodates | grep large3
436 $ hg debugdirstate --nodates | grep large3
437 a 0 -1 unset .hglf/large3
437 a 0 -1 unset .hglf/large3
438 r 0 0 set large3
438 r 0 0 set large3
439 $ hg status -A large3
439 $ hg status -A large3
440 A large3
440 A large3
441 $ cat large3
441 $ cat large3
442 large3 as large file for linear merge
442 large3 as large file for linear merge
443 $ rm -f large3 .hglf/large3
443 $ rm -f large3 .hglf/large3
444
444
445 Test that the internal linear merging works correctly
445 Test that the internal linear merging works correctly
446 (both heads are stripped to keep pairing of revision number and commit log)
446 (both heads are stripped to keep pairing of revision number and commit log)
447
447
448 $ hg update -q -C 2
448 $ hg update -q -C 2
449 $ hg strip 3 4
449 $ hg strip 3 4
450 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-2e7b195d-backup.hg (glob)
450 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-2e7b195d-backup.hg (glob)
451 $ mv .hg/strip-backup/9530e27857f7-2e7b195d-backup.hg $TESTTMP
451 $ mv .hg/strip-backup/9530e27857f7-2e7b195d-backup.hg $TESTTMP
452
452
453 (internal linear merging at "hg pull --update")
453 (internal linear merging at "hg pull --update")
454
454
455 $ echo 'large1 for linear merge (conflict)' > large1
455 $ echo 'large1 for linear merge (conflict)' > large1
456 $ echo 'large2 for linear merge (conflict with normal file)' > large2
456 $ echo 'large2 for linear merge (conflict with normal file)' > large2
457 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
457 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
458 pulling from $TESTTMP/9530e27857f7-2e7b195d-backup.hg (glob)
458 pulling from $TESTTMP/9530e27857f7-2e7b195d-backup.hg (glob)
459 searching for changes
459 searching for changes
460 adding changesets
460 adding changesets
461 adding manifests
461 adding manifests
462 adding file changes
462 adding file changes
463 added 3 changesets with 5 changes to 5 files
463 added 3 changesets with 5 changes to 5 files
464 remote turned local largefile large2 into a normal file
464 remote turned local largefile large2 into a normal file
465 keep (l)argefile or use (n)ormal file? l
465 keep (l)argefile or use (n)ormal file? l
466 largefile large1 has a merge conflict
466 largefile large1 has a merge conflict
467 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
467 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
468 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
468 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
469 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
469 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
470 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
470 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
471 1 other heads for branch "default"
471 1 other heads for branch "default"
472
472
473 $ hg status -A large1
473 $ hg status -A large1
474 M large1
474 M large1
475 $ cat large1
475 $ cat large1
476 large1 for linear merge (conflict)
476 large1 for linear merge (conflict)
477 $ cat .hglf/large1
477 $ cat .hglf/large1
478 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
478 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
479 $ hg status -A large2
479 $ hg status -A large2
480 A large2
480 A large2
481 $ cat large2
481 $ cat large2
482 large2 for linear merge (conflict with normal file)
482 large2 for linear merge (conflict with normal file)
483 $ cat .hglf/large2
483 $ cat .hglf/large2
484 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
484 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
485
485
486 (internal linear merging at "hg unbundle --update")
486 (internal linear merging at "hg unbundle --update")
487
487
488 $ hg update -q -C 2
488 $ hg update -q -C 2
489 $ hg rollback -q
489 $ hg rollback -q
490
490
491 $ echo 'large1 for linear merge (conflict)' > large1
491 $ echo 'large1 for linear merge (conflict)' > large1
492 $ echo 'large2 for linear merge (conflict with normal file)' > large2
492 $ echo 'large2 for linear merge (conflict with normal file)' > large2
493 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
493 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
494 adding changesets
494 adding changesets
495 adding manifests
495 adding manifests
496 adding file changes
496 adding file changes
497 added 3 changesets with 5 changes to 5 files
497 added 3 changesets with 5 changes to 5 files
498 remote turned local largefile large2 into a normal file
498 remote turned local largefile large2 into a normal file
499 keep (l)argefile or use (n)ormal file? l
499 keep (l)argefile or use (n)ormal file? l
500 largefile large1 has a merge conflict
500 largefile large1 has a merge conflict
501 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
501 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
502 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
502 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
503 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
503 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
504 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
504 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
505 1 other heads for branch "default"
505 1 other heads for branch "default"
506
506
507 $ hg status -A large1
507 $ hg status -A large1
508 M large1
508 M large1
509 $ cat large1
509 $ cat large1
510 large1 for linear merge (conflict)
510 large1 for linear merge (conflict)
511 $ cat .hglf/large1
511 $ cat .hglf/large1
512 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
512 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
513 $ hg status -A large2
513 $ hg status -A large2
514 A large2
514 A large2
515 $ cat large2
515 $ cat large2
516 large2 for linear merge (conflict with normal file)
516 large2 for linear merge (conflict with normal file)
517 $ cat .hglf/large2
517 $ cat .hglf/large2
518 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
518 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
519
519
520 (internal linear merging in subrepo at "hg update")
520 (internal linear merging in subrepo at "hg update")
521
521
522 $ cd ..
522 $ cd ..
523 $ hg init subparent
523 $ hg init subparent
524 $ cd subparent
524 $ cd subparent
525
525
526 $ hg clone -q -u 2 ../repo sub
526 $ hg clone -q -u 2 ../repo sub
527 $ cat > .hgsub <<EOF
527 $ cat > .hgsub <<EOF
528 > sub = sub
528 > sub = sub
529 > EOF
529 > EOF
530 $ hg add .hgsub
530 $ hg add .hgsub
531 $ hg commit -m '#0@parent'
531 $ hg commit -m '#0@parent'
532 $ cat .hgsubstate
532 $ cat .hgsubstate
533 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
533 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
534 $ hg -R sub update -q
534 $ hg -R sub update -q
535 $ hg commit -m '#1@parent'
535 $ hg commit -m '#1@parent'
536 $ cat .hgsubstate
536 $ cat .hgsubstate
537 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
537 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
538 $ hg update -q 0
538 $ hg update -q 0
539
539
540 $ echo 'large1 for linear merge (conflict)' > sub/large1
540 $ echo 'large1 for linear merge (conflict)' > sub/large1
541 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
541 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
542 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
542 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
543 > m
543 > m
544 > r
544 > r
545 > l
545 > l
546 > l
546 > l
547 > EOF
547 > EOF
548 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
548 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
549 (M)erge, keep (l)ocal or keep (r)emote? m
549 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
550 subrepository sources for sub differ (in checked out version)
550 subrepository sources for sub differ (in checked out version)
551 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
551 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
552 remote turned local largefile large2 into a normal file
552 remote turned local largefile large2 into a normal file
553 keep (l)argefile or use (n)ormal file? l
553 keep (l)argefile or use (n)ormal file? l
554 largefile large1 has a merge conflict
554 largefile large1 has a merge conflict
555 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
555 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
556 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
556 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
557 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
557 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
558 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
558 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
559 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
559 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
560
560
561 $ hg -R sub status -A sub/large1
561 $ hg -R sub status -A sub/large1
562 M sub/large1
562 M sub/large1
563 $ cat sub/large1
563 $ cat sub/large1
564 large1 for linear merge (conflict)
564 large1 for linear merge (conflict)
565 $ cat sub/.hglf/large1
565 $ cat sub/.hglf/large1
566 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
566 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
567 $ hg -R sub status -A sub/large2
567 $ hg -R sub status -A sub/large2
568 A sub/large2
568 A sub/large2
569 $ cat sub/large2
569 $ cat sub/large2
570 large2 for linear merge (conflict with normal file)
570 large2 for linear merge (conflict with normal file)
571 $ cat sub/.hglf/large2
571 $ cat sub/.hglf/large2
572 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
572 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
573
573
574 $ cd ..
574 $ cd ..
575 $ cd repo
575 $ cd repo
576
576
577 Test that rebase updates largefiles in the working directory even if
577 Test that rebase updates largefiles in the working directory even if
578 it is aborted by conflict.
578 it is aborted by conflict.
579
579
580 $ hg update -q -C 3
580 $ hg update -q -C 3
581 $ cat .hglf/large1
581 $ cat .hglf/large1
582 e5bb990443d6a92aaf7223813720f7566c9dd05b
582 e5bb990443d6a92aaf7223813720f7566c9dd05b
583 $ cat large1
583 $ cat large1
584 large1 in #3
584 large1 in #3
585 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
585 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
586 > o
586 > o
587 > EOF
587 > EOF
588 rebasing 1:72518492caa6 "#1"
588 rebasing 1:72518492caa6 "#1"
589 largefile large1 has a merge conflict
589 largefile large1 has a merge conflict
590 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
590 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
591 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
591 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
592 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
592 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
593 merging normal1
593 merging normal1
594 warning: conflicts while merging normal1! (edit, then use 'hg resolve --mark')
594 warning: conflicts while merging normal1! (edit, then use 'hg resolve --mark')
595 unresolved conflicts (see hg resolve, then hg rebase --continue)
595 unresolved conflicts (see hg resolve, then hg rebase --continue)
596 [1]
596 [1]
597 $ cat .hglf/large1
597 $ cat .hglf/large1
598 58e24f733a964da346e2407a2bee99d9001184f5
598 58e24f733a964da346e2407a2bee99d9001184f5
599 $ cat large1
599 $ cat large1
600 large1 in #1
600 large1 in #1
601
601
602 Test that rebase updates standins for manually modified largefiles at
602 Test that rebase updates standins for manually modified largefiles at
603 the 1st commit of resuming.
603 the 1st commit of resuming.
604
604
605 $ echo "manually modified before 'hg rebase --continue'" > large1
605 $ echo "manually modified before 'hg rebase --continue'" > large1
606 $ hg resolve -m normal1
606 $ hg resolve -m normal1
607 (no more unresolved files)
607 (no more unresolved files)
608 continue: hg rebase --continue
608 continue: hg rebase --continue
609 $ hg rebase --continue --config ui.interactive=True <<EOF
609 $ hg rebase --continue --config ui.interactive=True <<EOF
610 > c
610 > c
611 > EOF
611 > EOF
612 rebasing 1:72518492caa6 "#1"
612 rebasing 1:72518492caa6 "#1"
613 rebasing 4:07d6153b5c04 "#4"
613 rebasing 4:07d6153b5c04 "#4"
614 local [dest] changed .hglf/large1 which other [source] deleted
614 local [dest] changed .hglf/large1 which other [source] deleted
615 use (c)hanged version, (d)elete, or leave (u)nresolved? c
615 use (c)hanged version, (d)elete, or leave (u)nresolved? c
616
616
617 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
617 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
618 -e5bb990443d6a92aaf7223813720f7566c9dd05b
618 -e5bb990443d6a92aaf7223813720f7566c9dd05b
619 +8a4f783556e7dea21139ca0466eafce954c75c13
619 +8a4f783556e7dea21139ca0466eafce954c75c13
620 $ rm -f large1
620 $ rm -f large1
621 $ hg update -q -C tip
621 $ hg update -q -C tip
622 $ cat large1
622 $ cat large1
623 manually modified before 'hg rebase --continue'
623 manually modified before 'hg rebase --continue'
624
624
625 Test that transplant updates largefiles, of which standins are safely
625 Test that transplant updates largefiles, of which standins are safely
626 changed, even if it is aborted by conflict of other.
626 changed, even if it is aborted by conflict of other.
627
627
628 $ hg update -q -C 5
628 $ hg update -q -C 5
629 $ cat .hglf/large1
629 $ cat .hglf/large1
630 e5bb990443d6a92aaf7223813720f7566c9dd05b
630 e5bb990443d6a92aaf7223813720f7566c9dd05b
631 $ cat large1
631 $ cat large1
632 large1 in #3
632 large1 in #3
633 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
633 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
634 +fa44618ea25181aff4f48b70428294790cec9f61
634 +fa44618ea25181aff4f48b70428294790cec9f61
635 $ hg transplant 4
635 $ hg transplant 4
636 applying 07d6153b5c04
636 applying 07d6153b5c04
637 patching file .hglf/large1
637 patching file .hglf/large1
638 Hunk #1 FAILED at 0
638 Hunk #1 FAILED at 0
639 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
639 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
640 patch failed to apply
640 patch failed to apply
641 abort: fix up the working directory and run hg transplant --continue
641 abort: fix up the working directory and run hg transplant --continue
642 [255]
642 [255]
643 $ hg status -A large1
643 $ hg status -A large1
644 C large1
644 C large1
645 $ cat .hglf/large1
645 $ cat .hglf/large1
646 e5bb990443d6a92aaf7223813720f7566c9dd05b
646 e5bb990443d6a92aaf7223813720f7566c9dd05b
647 $ cat large1
647 $ cat large1
648 large1 in #3
648 large1 in #3
649 $ hg status -A largeX
649 $ hg status -A largeX
650 A largeX
650 A largeX
651 $ cat .hglf/largeX
651 $ cat .hglf/largeX
652 fa44618ea25181aff4f48b70428294790cec9f61
652 fa44618ea25181aff4f48b70428294790cec9f61
653 $ cat largeX
653 $ cat largeX
654 largeX
654 largeX
655
655
656 Test that transplant updates standins for manually modified largefiles
656 Test that transplant updates standins for manually modified largefiles
657 at the 1st commit of resuming.
657 at the 1st commit of resuming.
658
658
659 $ echo "manually modified before 'hg transplant --continue'" > large1
659 $ echo "manually modified before 'hg transplant --continue'" > large1
660 $ hg transplant --continue
660 $ hg transplant --continue
661 07d6153b5c04 transplanted as f1bf30eb88cc
661 07d6153b5c04 transplanted as f1bf30eb88cc
662 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
662 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
663 -e5bb990443d6a92aaf7223813720f7566c9dd05b
663 -e5bb990443d6a92aaf7223813720f7566c9dd05b
664 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
664 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
665 $ rm -f large1
665 $ rm -f large1
666 $ hg update -q -C tip
666 $ hg update -q -C tip
667 $ cat large1
667 $ cat large1
668 manually modified before 'hg transplant --continue'
668 manually modified before 'hg transplant --continue'
669
669
670 Test that "hg status" doesn't show removal of largefiles not managed
670 Test that "hg status" doesn't show removal of largefiles not managed
671 in the target context.
671 in the target context.
672
672
673 $ hg update -q -C 4
673 $ hg update -q -C 4
674 $ hg remove largeX
674 $ hg remove largeX
675 $ hg status -A largeX
675 $ hg status -A largeX
676 R largeX
676 R largeX
677 $ hg status -A --rev '.^1' largeX
677 $ hg status -A --rev '.^1' largeX
678
678
679 #if execbit
679 #if execbit
680
680
681 Test that "hg status" against revisions other than parent notices exec
681 Test that "hg status" against revisions other than parent notices exec
682 bit changes of largefiles.
682 bit changes of largefiles.
683
683
684 $ hg update -q -C 4
684 $ hg update -q -C 4
685
685
686 (the case that large2 doesn't have exec bit in the target context but
686 (the case that large2 doesn't have exec bit in the target context but
687 in the working context)
687 in the working context)
688
688
689 $ chmod +x large2
689 $ chmod +x large2
690 $ hg status -A --rev 0 large2
690 $ hg status -A --rev 0 large2
691 M large2
691 M large2
692 $ hg commit -m 'chmod +x large2'
692 $ hg commit -m 'chmod +x large2'
693
693
694 (the case that large2 has exec bit in the target context but not in
694 (the case that large2 has exec bit in the target context but not in
695 the working context)
695 the working context)
696
696
697 $ echo dummy > dummy
697 $ echo dummy > dummy
698 $ hg add dummy
698 $ hg add dummy
699 $ hg commit -m 'revision for separation'
699 $ hg commit -m 'revision for separation'
700 $ chmod -x large2
700 $ chmod -x large2
701 $ hg status -A --rev '.^1' large2
701 $ hg status -A --rev '.^1' large2
702 M large2
702 M large2
703
703
704 #else
704 #else
705
705
706 Test that "hg status" against revisions other than parent ignores exec
706 Test that "hg status" against revisions other than parent ignores exec
707 bit correctly on the platform being unaware of it.
707 bit correctly on the platform being unaware of it.
708
708
709 $ hg update -q -C 4
709 $ hg update -q -C 4
710
710
711 $ cat > exec-bit.patch <<EOF
711 $ cat > exec-bit.patch <<EOF
712 > # HG changeset patch
712 > # HG changeset patch
713 > # User test
713 > # User test
714 > # Date 0 0
714 > # Date 0 0
715 > # Thu Jan 01 00:00:00 1970 +0000
715 > # Thu Jan 01 00:00:00 1970 +0000
716 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
716 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
717 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
717 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
718 > chmod +x large2
718 > chmod +x large2
719 >
719 >
720 > diff --git a/.hglf/large2 b/.hglf/large2
720 > diff --git a/.hglf/large2 b/.hglf/large2
721 > old mode 100644
721 > old mode 100644
722 > new mode 100755
722 > new mode 100755
723 > EOF
723 > EOF
724 $ hg import --exact --bypass exec-bit.patch
724 $ hg import --exact --bypass exec-bit.patch
725 applying exec-bit.patch
725 applying exec-bit.patch
726 $ hg status -A --rev tip large2
726 $ hg status -A --rev tip large2
727 C large2
727 C large2
728
728
729 #endif
729 #endif
730
730
731 $ cd ..
731 $ cd ..
732
732
733 Test that "hg convert" avoids copying largefiles from the working
733 Test that "hg convert" avoids copying largefiles from the working
734 directory into store, because "hg convert" doesn't update largefiles
734 directory into store, because "hg convert" doesn't update largefiles
735 in the working directory (removing files under ".cache/largefiles"
735 in the working directory (removing files under ".cache/largefiles"
736 forces "hg convert" to copy corresponding largefiles)
736 forces "hg convert" to copy corresponding largefiles)
737
737
738 $ cat >> $HGRCPATH <<EOF
738 $ cat >> $HGRCPATH <<EOF
739 > [extensions]
739 > [extensions]
740 > convert =
740 > convert =
741 > EOF
741 > EOF
742
742
743 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
743 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
744 $ hg convert -q repo repo.converted
744 $ hg convert -q repo repo.converted
@@ -1,1175 +1,1175 b''
1 #require git
1 #require git
2
2
3 make git commits repeatable
3 make git commits repeatable
4
4
5 $ echo "[core]" >> $HOME/.gitconfig
5 $ echo "[core]" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
7 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
7 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
8 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
8 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
9 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
9 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
10 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
10 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
11 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
11 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
12 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
12 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
13 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
13 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
14
14
15 root hg repo
15 root hg repo
16
16
17 $ hg init t
17 $ hg init t
18 $ cd t
18 $ cd t
19 $ echo a > a
19 $ echo a > a
20 $ hg add a
20 $ hg add a
21 $ hg commit -m a
21 $ hg commit -m a
22 $ cd ..
22 $ cd ..
23
23
24 new external git repo
24 new external git repo
25
25
26 $ mkdir gitroot
26 $ mkdir gitroot
27 $ cd gitroot
27 $ cd gitroot
28 $ git init -q
28 $ git init -q
29 $ echo g > g
29 $ echo g > g
30 $ git add g
30 $ git add g
31 $ git commit -q -m g
31 $ git commit -q -m g
32
32
33 add subrepo clone
33 add subrepo clone
34
34
35 $ cd ../t
35 $ cd ../t
36 $ echo 's = [git]../gitroot' > .hgsub
36 $ echo 's = [git]../gitroot' > .hgsub
37 $ git clone -q ../gitroot s
37 $ git clone -q ../gitroot s
38 $ hg add .hgsub
38 $ hg add .hgsub
39 $ hg commit -m 'new git subrepo'
39 $ hg commit -m 'new git subrepo'
40 $ hg debugsub
40 $ hg debugsub
41 path s
41 path s
42 source ../gitroot
42 source ../gitroot
43 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
43 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
44
44
45 record a new commit from upstream from a different branch
45 record a new commit from upstream from a different branch
46
46
47 $ cd ../gitroot
47 $ cd ../gitroot
48 $ git checkout -q -b testing
48 $ git checkout -q -b testing
49 $ echo gg >> g
49 $ echo gg >> g
50 $ git commit -q -a -m gg
50 $ git commit -q -a -m gg
51
51
52 $ cd ../t/s
52 $ cd ../t/s
53 $ git pull -q >/dev/null 2>/dev/null
53 $ git pull -q >/dev/null 2>/dev/null
54 $ git checkout -q -b testing origin/testing >/dev/null
54 $ git checkout -q -b testing origin/testing >/dev/null
55
55
56 $ cd ..
56 $ cd ..
57 $ hg status --subrepos
57 $ hg status --subrepos
58 M s/g
58 M s/g
59 $ hg commit -m 'update git subrepo'
59 $ hg commit -m 'update git subrepo'
60 $ hg debugsub
60 $ hg debugsub
61 path s
61 path s
62 source ../gitroot
62 source ../gitroot
63 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
63 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
64
64
65 make $GITROOT pushable, by replacing it with a clone with nothing checked out
65 make $GITROOT pushable, by replacing it with a clone with nothing checked out
66
66
67 $ cd ..
67 $ cd ..
68 $ git clone gitroot gitrootbare --bare -q
68 $ git clone gitroot gitrootbare --bare -q
69 $ rm -rf gitroot
69 $ rm -rf gitroot
70 $ mv gitrootbare gitroot
70 $ mv gitrootbare gitroot
71
71
72 clone root
72 clone root
73
73
74 $ cd t
74 $ cd t
75 $ hg clone . ../tc 2> /dev/null
75 $ hg clone . ../tc 2> /dev/null
76 updating to branch default
76 updating to branch default
77 cloning subrepo s from $TESTTMP/gitroot
77 cloning subrepo s from $TESTTMP/gitroot
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 $ cd ../tc
79 $ cd ../tc
80 $ hg debugsub
80 $ hg debugsub
81 path s
81 path s
82 source ../gitroot
82 source ../gitroot
83 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
83 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
84
84
85 update to previous substate
85 update to previous substate
86
86
87 $ hg update 1 -q
87 $ hg update 1 -q
88 $ cat s/g
88 $ cat s/g
89 g
89 g
90 $ hg debugsub
90 $ hg debugsub
91 path s
91 path s
92 source ../gitroot
92 source ../gitroot
93 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
93 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
94
94
95 clone root, make local change
95 clone root, make local change
96
96
97 $ cd ../t
97 $ cd ../t
98 $ hg clone . ../ta 2> /dev/null
98 $ hg clone . ../ta 2> /dev/null
99 updating to branch default
99 updating to branch default
100 cloning subrepo s from $TESTTMP/gitroot
100 cloning subrepo s from $TESTTMP/gitroot
101 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
102
102
103 $ cd ../ta
103 $ cd ../ta
104 $ echo ggg >> s/g
104 $ echo ggg >> s/g
105 $ hg status --subrepos
105 $ hg status --subrepos
106 M s/g
106 M s/g
107 $ hg diff --subrepos
107 $ hg diff --subrepos
108 diff --git a/s/g b/s/g
108 diff --git a/s/g b/s/g
109 index 089258f..85341ee 100644
109 index 089258f..85341ee 100644
110 --- a/s/g
110 --- a/s/g
111 +++ b/s/g
111 +++ b/s/g
112 @@ -1,2 +1,3 @@
112 @@ -1,2 +1,3 @@
113 g
113 g
114 gg
114 gg
115 +ggg
115 +ggg
116 $ hg commit --subrepos -m ggg
116 $ hg commit --subrepos -m ggg
117 committing subrepository s
117 committing subrepository s
118 $ hg debugsub
118 $ hg debugsub
119 path s
119 path s
120 source ../gitroot
120 source ../gitroot
121 revision 79695940086840c99328513acbe35f90fcd55e57
121 revision 79695940086840c99328513acbe35f90fcd55e57
122
122
123 clone root separately, make different local change
123 clone root separately, make different local change
124
124
125 $ cd ../t
125 $ cd ../t
126 $ hg clone . ../tb 2> /dev/null
126 $ hg clone . ../tb 2> /dev/null
127 updating to branch default
127 updating to branch default
128 cloning subrepo s from $TESTTMP/gitroot
128 cloning subrepo s from $TESTTMP/gitroot
129 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
130
130
131 $ cd ../tb/s
131 $ cd ../tb/s
132 $ hg status --subrepos
132 $ hg status --subrepos
133 $ echo f > f
133 $ echo f > f
134 $ hg status --subrepos
134 $ hg status --subrepos
135 ? s/f
135 ? s/f
136 $ hg add .
136 $ hg add .
137 adding f
137 adding f
138 $ git add f
138 $ git add f
139 $ cd ..
139 $ cd ..
140
140
141 $ hg status --subrepos
141 $ hg status --subrepos
142 A s/f
142 A s/f
143 $ hg commit --subrepos -m f
143 $ hg commit --subrepos -m f
144 committing subrepository s
144 committing subrepository s
145 $ hg debugsub
145 $ hg debugsub
146 path s
146 path s
147 source ../gitroot
147 source ../gitroot
148 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
148 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
149
149
150 user b push changes
150 user b push changes
151
151
152 $ hg push 2>/dev/null
152 $ hg push 2>/dev/null
153 pushing to $TESTTMP/t (glob)
153 pushing to $TESTTMP/t (glob)
154 pushing branch testing of subrepo s
154 pushing branch testing of subrepo s
155 searching for changes
155 searching for changes
156 adding changesets
156 adding changesets
157 adding manifests
157 adding manifests
158 adding file changes
158 adding file changes
159 added 1 changesets with 1 changes to 1 files
159 added 1 changesets with 1 changes to 1 files
160
160
161 user a pulls, merges, commits
161 user a pulls, merges, commits
162
162
163 $ cd ../ta
163 $ cd ../ta
164 $ hg pull
164 $ hg pull
165 pulling from $TESTTMP/t (glob)
165 pulling from $TESTTMP/t (glob)
166 searching for changes
166 searching for changes
167 adding changesets
167 adding changesets
168 adding manifests
168 adding manifests
169 adding file changes
169 adding file changes
170 added 1 changesets with 1 changes to 1 files (+1 heads)
170 added 1 changesets with 1 changes to 1 files (+1 heads)
171 (run 'hg heads' to see heads, 'hg merge' to merge)
171 (run 'hg heads' to see heads, 'hg merge' to merge)
172 $ hg merge 2>/dev/null
172 $ hg merge 2>/dev/null
173 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
173 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
174 (M)erge, keep (l)ocal or keep (r)emote? m
174 (M)erge, keep (l)ocal or keep (r)emote? m
175 pulling subrepo s from $TESTTMP/gitroot
175 pulling subrepo s from $TESTTMP/gitroot
176 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 (branch merge, don't forget to commit)
177 (branch merge, don't forget to commit)
178 $ hg st --subrepos s
178 $ hg st --subrepos s
179 A s/f
179 A s/f
180 $ cat s/f
180 $ cat s/f
181 f
181 f
182 $ cat s/g
182 $ cat s/g
183 g
183 g
184 gg
184 gg
185 ggg
185 ggg
186 $ hg commit --subrepos -m 'merge'
186 $ hg commit --subrepos -m 'merge'
187 committing subrepository s
187 committing subrepository s
188 $ hg status --subrepos --rev 1:5
188 $ hg status --subrepos --rev 1:5
189 M .hgsubstate
189 M .hgsubstate
190 M s/g
190 M s/g
191 A s/f
191 A s/f
192 $ hg debugsub
192 $ hg debugsub
193 path s
193 path s
194 source ../gitroot
194 source ../gitroot
195 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
195 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
196 $ hg push 2>/dev/null
196 $ hg push 2>/dev/null
197 pushing to $TESTTMP/t (glob)
197 pushing to $TESTTMP/t (glob)
198 pushing branch testing of subrepo s
198 pushing branch testing of subrepo s
199 searching for changes
199 searching for changes
200 adding changesets
200 adding changesets
201 adding manifests
201 adding manifests
202 adding file changes
202 adding file changes
203 added 2 changesets with 2 changes to 1 files
203 added 2 changesets with 2 changes to 1 files
204
204
205 make upstream git changes
205 make upstream git changes
206
206
207 $ cd ..
207 $ cd ..
208 $ git clone -q gitroot gitclone
208 $ git clone -q gitroot gitclone
209 $ cd gitclone
209 $ cd gitclone
210 $ echo ff >> f
210 $ echo ff >> f
211 $ git commit -q -a -m ff
211 $ git commit -q -a -m ff
212 $ echo fff >> f
212 $ echo fff >> f
213 $ git commit -q -a -m fff
213 $ git commit -q -a -m fff
214 $ git push origin testing 2>/dev/null
214 $ git push origin testing 2>/dev/null
215
215
216 make and push changes to hg without updating the subrepo
216 make and push changes to hg without updating the subrepo
217
217
218 $ cd ../t
218 $ cd ../t
219 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
219 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
220 updating to branch default
220 updating to branch default
221 cloning subrepo s from $TESTTMP/gitroot
221 cloning subrepo s from $TESTTMP/gitroot
222 checking out detached HEAD in subrepo s
222 checking out detached HEAD in subrepo s
223 check out a git branch if you intend to make changes
223 check out a git branch if you intend to make changes
224 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
225 $ cd ../td
225 $ cd ../td
226 $ echo aa >> a
226 $ echo aa >> a
227 $ hg commit -m aa
227 $ hg commit -m aa
228 $ hg push
228 $ hg push
229 pushing to $TESTTMP/t (glob)
229 pushing to $TESTTMP/t (glob)
230 searching for changes
230 searching for changes
231 adding changesets
231 adding changesets
232 adding manifests
232 adding manifests
233 adding file changes
233 adding file changes
234 added 1 changesets with 1 changes to 1 files
234 added 1 changesets with 1 changes to 1 files
235
235
236 sync to upstream git, distribute changes
236 sync to upstream git, distribute changes
237
237
238 $ cd ../ta
238 $ cd ../ta
239 $ hg pull -u -q
239 $ hg pull -u -q
240 $ cd s
240 $ cd s
241 $ git pull -q >/dev/null 2>/dev/null
241 $ git pull -q >/dev/null 2>/dev/null
242 $ cd ..
242 $ cd ..
243 $ hg commit -m 'git upstream sync'
243 $ hg commit -m 'git upstream sync'
244 $ hg debugsub
244 $ hg debugsub
245 path s
245 path s
246 source ../gitroot
246 source ../gitroot
247 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
247 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
248 $ hg push -q
248 $ hg push -q
249
249
250 $ cd ../tb
250 $ cd ../tb
251 $ hg pull -q
251 $ hg pull -q
252 $ hg update 2>/dev/null
252 $ hg update 2>/dev/null
253 pulling subrepo s from $TESTTMP/gitroot
253 pulling subrepo s from $TESTTMP/gitroot
254 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 $ hg debugsub
255 $ hg debugsub
256 path s
256 path s
257 source ../gitroot
257 source ../gitroot
258 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
258 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
259
259
260 create a new git branch
260 create a new git branch
261
261
262 $ cd s
262 $ cd s
263 $ git checkout -b b2
263 $ git checkout -b b2
264 Switched to a new branch 'b2'
264 Switched to a new branch 'b2'
265 $ echo a>a
265 $ echo a>a
266 $ git add a
266 $ git add a
267 $ git commit -qm 'add a'
267 $ git commit -qm 'add a'
268 $ cd ..
268 $ cd ..
269 $ hg commit -m 'add branch in s'
269 $ hg commit -m 'add branch in s'
270
270
271 pulling new git branch should not create tracking branch named 'origin/b2'
271 pulling new git branch should not create tracking branch named 'origin/b2'
272 (issue3870)
272 (issue3870)
273 $ cd ../td/s
273 $ cd ../td/s
274 $ git remote set-url origin $TESTTMP/tb/s
274 $ git remote set-url origin $TESTTMP/tb/s
275 $ git branch --no-track oldtesting
275 $ git branch --no-track oldtesting
276 $ cd ..
276 $ cd ..
277 $ hg pull -q ../tb
277 $ hg pull -q ../tb
278 $ hg up
278 $ hg up
279 From $TESTTMP/tb/s
279 From $TESTTMP/tb/s
280 * [new branch] b2 -> origin/b2
280 * [new branch] b2 -> origin/b2
281 Previous HEAD position was f47b465... merge
281 Previous HEAD position was f47b465... merge
282 Switched to a new branch 'b2'
282 Switched to a new branch 'b2'
283 pulling subrepo s from $TESTTMP/tb/s
283 pulling subrepo s from $TESTTMP/tb/s
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
285
285
286 update to a revision without the subrepo, keeping the local git repository
286 update to a revision without the subrepo, keeping the local git repository
287
287
288 $ cd ../t
288 $ cd ../t
289 $ hg up 0
289 $ hg up 0
290 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
290 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
291 $ ls -a s
291 $ ls -a s
292 .
292 .
293 ..
293 ..
294 .git
294 .git
295
295
296 $ hg up 2
296 $ hg up 2
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 $ ls -a s
298 $ ls -a s
299 .
299 .
300 ..
300 ..
301 .git
301 .git
302 g
302 g
303
303
304 archive subrepos
304 archive subrepos
305
305
306 $ cd ../tc
306 $ cd ../tc
307 $ hg pull -q
307 $ hg pull -q
308 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
308 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
309 pulling subrepo s from $TESTTMP/gitroot
309 pulling subrepo s from $TESTTMP/gitroot
310 $ cd ../archive
310 $ cd ../archive
311 $ cat s/f
311 $ cat s/f
312 f
312 f
313 $ cat s/g
313 $ cat s/g
314 g
314 g
315 gg
315 gg
316 ggg
316 ggg
317
317
318 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
318 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
319 $ find ../archive_x | sort | grep -v pax_global_header
319 $ find ../archive_x | sort | grep -v pax_global_header
320 ../archive_x
320 ../archive_x
321 ../archive_x/.hg_archival.txt
321 ../archive_x/.hg_archival.txt
322 ../archive_x/.hgsub
322 ../archive_x/.hgsub
323 ../archive_x/.hgsubstate
323 ../archive_x/.hgsubstate
324 ../archive_x/a
324 ../archive_x/a
325 ../archive_x/s
325 ../archive_x/s
326 ../archive_x/s/g
326 ../archive_x/s/g
327
327
328 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
328 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
329 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
329 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
330 .hg_archival.txt
330 .hg_archival.txt
331 .hgsub
331 .hgsub
332 .hgsubstate
332 .hgsubstate
333 a
333 a
334 s/g
334 s/g
335
335
336 create nested repo
336 create nested repo
337
337
338 $ cd ..
338 $ cd ..
339 $ hg init outer
339 $ hg init outer
340 $ cd outer
340 $ cd outer
341 $ echo b>b
341 $ echo b>b
342 $ hg add b
342 $ hg add b
343 $ hg commit -m b
343 $ hg commit -m b
344
344
345 $ hg clone ../t inner 2> /dev/null
345 $ hg clone ../t inner 2> /dev/null
346 updating to branch default
346 updating to branch default
347 cloning subrepo s from $TESTTMP/gitroot
347 cloning subrepo s from $TESTTMP/gitroot
348 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 $ echo inner = inner > .hgsub
349 $ echo inner = inner > .hgsub
350 $ hg add .hgsub
350 $ hg add .hgsub
351 $ hg commit -m 'nested sub'
351 $ hg commit -m 'nested sub'
352
352
353 nested commit
353 nested commit
354
354
355 $ echo ffff >> inner/s/f
355 $ echo ffff >> inner/s/f
356 $ hg status --subrepos
356 $ hg status --subrepos
357 M inner/s/f
357 M inner/s/f
358 $ hg commit --subrepos -m nested
358 $ hg commit --subrepos -m nested
359 committing subrepository inner
359 committing subrepository inner
360 committing subrepository inner/s (glob)
360 committing subrepository inner/s (glob)
361
361
362 nested archive
362 nested archive
363
363
364 $ hg archive --subrepos ../narchive
364 $ hg archive --subrepos ../narchive
365 $ ls ../narchive/inner/s | grep -v pax_global_header
365 $ ls ../narchive/inner/s | grep -v pax_global_header
366 f
366 f
367 g
367 g
368
368
369 relative source expansion
369 relative source expansion
370
370
371 $ cd ..
371 $ cd ..
372 $ mkdir d
372 $ mkdir d
373 $ hg clone t d/t 2> /dev/null
373 $ hg clone t d/t 2> /dev/null
374 updating to branch default
374 updating to branch default
375 cloning subrepo s from $TESTTMP/gitroot
375 cloning subrepo s from $TESTTMP/gitroot
376 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
377
377
378 Don't crash if the subrepo is missing
378 Don't crash if the subrepo is missing
379
379
380 $ hg clone t missing -q
380 $ hg clone t missing -q
381 $ cd missing
381 $ cd missing
382 $ rm -rf s
382 $ rm -rf s
383 $ hg status -S
383 $ hg status -S
384 $ hg sum | grep commit
384 $ hg sum | grep commit
385 commit: 1 subrepos
385 commit: 1 subrepos
386 $ hg push -q
386 $ hg push -q
387 abort: subrepo s is missing (in subrepo s)
387 abort: subrepo s is missing (in subrepo s)
388 [255]
388 [255]
389 $ hg commit --subrepos -qm missing
389 $ hg commit --subrepos -qm missing
390 abort: subrepo s is missing (in subrepo s)
390 abort: subrepo s is missing (in subrepo s)
391 [255]
391 [255]
392
392
393 #if symlink
393 #if symlink
394 Don't crash if subrepo is a broken symlink
394 Don't crash if subrepo is a broken symlink
395 $ ln -s broken s
395 $ ln -s broken s
396 $ hg status -S
396 $ hg status -S
397 $ hg push -q
397 $ hg push -q
398 abort: subrepo s is missing (in subrepo s)
398 abort: subrepo s is missing (in subrepo s)
399 [255]
399 [255]
400 $ hg commit --subrepos -qm missing
400 $ hg commit --subrepos -qm missing
401 abort: subrepo s is missing (in subrepo s)
401 abort: subrepo s is missing (in subrepo s)
402 [255]
402 [255]
403 $ rm s
403 $ rm s
404 #endif
404 #endif
405
405
406 $ hg update -C 2> /dev/null
406 $ hg update -C 2> /dev/null
407 cloning subrepo s from $TESTTMP/gitroot
407 cloning subrepo s from $TESTTMP/gitroot
408 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
408 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 $ hg sum | grep commit
409 $ hg sum | grep commit
410 commit: (clean)
410 commit: (clean)
411
411
412 Don't crash if the .hgsubstate entry is missing
412 Don't crash if the .hgsubstate entry is missing
413
413
414 $ hg update 1 -q
414 $ hg update 1 -q
415 $ hg rm .hgsubstate
415 $ hg rm .hgsubstate
416 $ hg commit .hgsubstate -m 'no substate'
416 $ hg commit .hgsubstate -m 'no substate'
417 nothing changed
417 nothing changed
418 [1]
418 [1]
419 $ hg tag -l nosubstate
419 $ hg tag -l nosubstate
420 $ hg manifest
420 $ hg manifest
421 .hgsub
421 .hgsub
422 .hgsubstate
422 .hgsubstate
423 a
423 a
424
424
425 $ hg status -S
425 $ hg status -S
426 R .hgsubstate
426 R .hgsubstate
427 $ hg sum | grep commit
427 $ hg sum | grep commit
428 commit: 1 removed, 1 subrepos (new branch head)
428 commit: 1 removed, 1 subrepos (new branch head)
429
429
430 $ hg commit -m 'restore substate'
430 $ hg commit -m 'restore substate'
431 nothing changed
431 nothing changed
432 [1]
432 [1]
433 $ hg manifest
433 $ hg manifest
434 .hgsub
434 .hgsub
435 .hgsubstate
435 .hgsubstate
436 a
436 a
437 $ hg sum | grep commit
437 $ hg sum | grep commit
438 commit: 1 removed, 1 subrepos (new branch head)
438 commit: 1 removed, 1 subrepos (new branch head)
439
439
440 $ hg update -qC nosubstate
440 $ hg update -qC nosubstate
441 $ ls s
441 $ ls s
442 g
442 g
443
443
444 issue3109: false positives in git diff-index
444 issue3109: false positives in git diff-index
445
445
446 $ hg update -q
446 $ hg update -q
447 $ touch -t 200001010000 s/g
447 $ touch -t 200001010000 s/g
448 $ hg status --subrepos
448 $ hg status --subrepos
449 $ touch -t 200001010000 s/g
449 $ touch -t 200001010000 s/g
450 $ hg sum | grep commit
450 $ hg sum | grep commit
451 commit: (clean)
451 commit: (clean)
452
452
453 Check hg update --clean
453 Check hg update --clean
454 $ cd $TESTTMP/ta
454 $ cd $TESTTMP/ta
455 $ echo > s/g
455 $ echo > s/g
456 $ cd s
456 $ cd s
457 $ echo c1 > f1
457 $ echo c1 > f1
458 $ echo c1 > f2
458 $ echo c1 > f2
459 $ git add f1
459 $ git add f1
460 $ cd ..
460 $ cd ..
461 $ hg status -S
461 $ hg status -S
462 M s/g
462 M s/g
463 A s/f1
463 A s/f1
464 ? s/f2
464 ? s/f2
465 $ ls s
465 $ ls s
466 f
466 f
467 f1
467 f1
468 f2
468 f2
469 g
469 g
470 $ hg update --clean
470 $ hg update --clean
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 $ hg status -S
472 $ hg status -S
473 ? s/f1
473 ? s/f1
474 ? s/f2
474 ? s/f2
475 $ ls s
475 $ ls s
476 f
476 f
477 f1
477 f1
478 f2
478 f2
479 g
479 g
480
480
481 Sticky subrepositories, no changes
481 Sticky subrepositories, no changes
482 $ cd $TESTTMP/ta
482 $ cd $TESTTMP/ta
483 $ hg id -n
483 $ hg id -n
484 7
484 7
485 $ cd s
485 $ cd s
486 $ git rev-parse HEAD
486 $ git rev-parse HEAD
487 32a343883b74769118bb1d3b4b1fbf9156f4dddc
487 32a343883b74769118bb1d3b4b1fbf9156f4dddc
488 $ cd ..
488 $ cd ..
489 $ hg update 1 > /dev/null 2>&1
489 $ hg update 1 > /dev/null 2>&1
490 $ hg id -n
490 $ hg id -n
491 1
491 1
492 $ cd s
492 $ cd s
493 $ git rev-parse HEAD
493 $ git rev-parse HEAD
494 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
494 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
495 $ cd ..
495 $ cd ..
496
496
497 Sticky subrepositories, file changes
497 Sticky subrepositories, file changes
498 $ touch s/f1
498 $ touch s/f1
499 $ cd s
499 $ cd s
500 $ git add f1
500 $ git add f1
501 $ cd ..
501 $ cd ..
502 $ hg id -n
502 $ hg id -n
503 1+
503 1+
504 $ cd s
504 $ cd s
505 $ git rev-parse HEAD
505 $ git rev-parse HEAD
506 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
506 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
507 $ cd ..
507 $ cd ..
508 $ hg update 4
508 $ hg update 4
509 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
509 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
510 (M)erge, keep (l)ocal or keep (r)emote? m
510 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
511 subrepository sources for s differ
511 subrepository sources for s differ
512 use (l)ocal source (da5f5b1) or (r)emote source (aa84837)? l
512 use (l)ocal source (da5f5b1) or (r)emote source (aa84837)? l
513 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 $ hg id -n
514 $ hg id -n
515 4+
515 4+
516 $ cd s
516 $ cd s
517 $ git rev-parse HEAD
517 $ git rev-parse HEAD
518 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
518 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
519 $ cd ..
519 $ cd ..
520 $ hg update --clean tip > /dev/null 2>&1
520 $ hg update --clean tip > /dev/null 2>&1
521
521
522 Sticky subrepository, revision updates
522 Sticky subrepository, revision updates
523 $ hg id -n
523 $ hg id -n
524 7
524 7
525 $ cd s
525 $ cd s
526 $ git rev-parse HEAD
526 $ git rev-parse HEAD
527 32a343883b74769118bb1d3b4b1fbf9156f4dddc
527 32a343883b74769118bb1d3b4b1fbf9156f4dddc
528 $ cd ..
528 $ cd ..
529 $ cd s
529 $ cd s
530 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
530 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
531 Previous HEAD position was 32a3438... fff
531 Previous HEAD position was 32a3438... fff
532 HEAD is now at aa84837... f
532 HEAD is now at aa84837... f
533 $ cd ..
533 $ cd ..
534 $ hg update 1
534 $ hg update 1
535 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
535 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
536 (M)erge, keep (l)ocal or keep (r)emote? m
536 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
537 subrepository sources for s differ (in checked out version)
537 subrepository sources for s differ (in checked out version)
538 use (l)ocal source (32a3438) or (r)emote source (da5f5b1)? l
538 use (l)ocal source (32a3438) or (r)emote source (da5f5b1)? l
539 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
539 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
540 $ hg id -n
540 $ hg id -n
541 1+
541 1+
542 $ cd s
542 $ cd s
543 $ git rev-parse HEAD
543 $ git rev-parse HEAD
544 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
544 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
545 $ cd ..
545 $ cd ..
546
546
547 Sticky subrepository, file changes and revision updates
547 Sticky subrepository, file changes and revision updates
548 $ touch s/f1
548 $ touch s/f1
549 $ cd s
549 $ cd s
550 $ git add f1
550 $ git add f1
551 $ git rev-parse HEAD
551 $ git rev-parse HEAD
552 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
552 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
553 $ cd ..
553 $ cd ..
554 $ hg id -n
554 $ hg id -n
555 1+
555 1+
556 $ hg update 7
556 $ hg update 7
557 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
557 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
558 (M)erge, keep (l)ocal or keep (r)emote? m
558 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
559 subrepository sources for s differ
559 subrepository sources for s differ
560 use (l)ocal source (32a3438) or (r)emote source (32a3438)? l
560 use (l)ocal source (32a3438) or (r)emote source (32a3438)? l
561 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 $ hg id -n
562 $ hg id -n
563 7+
563 7+
564 $ cd s
564 $ cd s
565 $ git rev-parse HEAD
565 $ git rev-parse HEAD
566 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
566 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
567 $ cd ..
567 $ cd ..
568
568
569 Sticky repository, update --clean
569 Sticky repository, update --clean
570 $ hg update --clean tip 2>/dev/null
570 $ hg update --clean tip 2>/dev/null
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 $ hg id -n
572 $ hg id -n
573 7
573 7
574 $ cd s
574 $ cd s
575 $ git rev-parse HEAD
575 $ git rev-parse HEAD
576 32a343883b74769118bb1d3b4b1fbf9156f4dddc
576 32a343883b74769118bb1d3b4b1fbf9156f4dddc
577 $ cd ..
577 $ cd ..
578
578
579 Test subrepo already at intended revision:
579 Test subrepo already at intended revision:
580 $ cd s
580 $ cd s
581 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
581 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
582 HEAD is now at 32a3438... fff
582 HEAD is now at 32a3438... fff
583 $ cd ..
583 $ cd ..
584 $ hg update 1
584 $ hg update 1
585 Previous HEAD position was 32a3438... fff
585 Previous HEAD position was 32a3438... fff
586 HEAD is now at da5f5b1... g
586 HEAD is now at da5f5b1... g
587 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
587 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
588 $ hg id -n
588 $ hg id -n
589 1
589 1
590 $ cd s
590 $ cd s
591 $ git rev-parse HEAD
591 $ git rev-parse HEAD
592 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
592 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
593 $ cd ..
593 $ cd ..
594
594
595 Test forgetting files, not implemented in git subrepo, used to
595 Test forgetting files, not implemented in git subrepo, used to
596 traceback
596 traceback
597 #if no-windows
597 #if no-windows
598 $ hg forget 'notafile*'
598 $ hg forget 'notafile*'
599 notafile*: No such file or directory
599 notafile*: No such file or directory
600 [1]
600 [1]
601 #else
601 #else
602 $ hg forget 'notafile'
602 $ hg forget 'notafile'
603 notafile: * (glob)
603 notafile: * (glob)
604 [1]
604 [1]
605 #endif
605 #endif
606
606
607 $ cd ..
607 $ cd ..
608
608
609 Test sanitizing ".hg/hgrc" in subrepo
609 Test sanitizing ".hg/hgrc" in subrepo
610
610
611 $ cd t
611 $ cd t
612 $ hg tip -q
612 $ hg tip -q
613 7:af6d2edbb0d3
613 7:af6d2edbb0d3
614 $ hg update -q -C af6d2edbb0d3
614 $ hg update -q -C af6d2edbb0d3
615 $ cd s
615 $ cd s
616 $ git checkout -q -b sanitize-test
616 $ git checkout -q -b sanitize-test
617 $ mkdir .hg
617 $ mkdir .hg
618 $ echo '.hg/hgrc in git repo' > .hg/hgrc
618 $ echo '.hg/hgrc in git repo' > .hg/hgrc
619 $ mkdir -p sub/.hg
619 $ mkdir -p sub/.hg
620 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
620 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
621 $ git add .hg sub
621 $ git add .hg sub
622 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
622 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
623 $ git push -q origin sanitize-test
623 $ git push -q origin sanitize-test
624 $ cd ..
624 $ cd ..
625 $ grep ' s$' .hgsubstate
625 $ grep ' s$' .hgsubstate
626 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
626 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
627 $ hg commit -qm 'commit with git revision including .hg/hgrc'
627 $ hg commit -qm 'commit with git revision including .hg/hgrc'
628 $ hg parents -q
628 $ hg parents -q
629 8:3473d20bddcf
629 8:3473d20bddcf
630 $ grep ' s$' .hgsubstate
630 $ grep ' s$' .hgsubstate
631 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
631 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
632 $ cd ..
632 $ cd ..
633
633
634 $ hg -R tc pull -q
634 $ hg -R tc pull -q
635 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
635 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
636 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
636 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
637 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
637 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
638 $ cd tc
638 $ cd tc
639 $ hg parents -q
639 $ hg parents -q
640 8:3473d20bddcf
640 8:3473d20bddcf
641 $ grep ' s$' .hgsubstate
641 $ grep ' s$' .hgsubstate
642 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
642 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
643 $ test -f s/.hg/hgrc
643 $ test -f s/.hg/hgrc
644 [1]
644 [1]
645 $ test -f s/sub/.hg/hgrc
645 $ test -f s/sub/.hg/hgrc
646 [1]
646 [1]
647 $ cd ..
647 $ cd ..
648
648
649 additional test for "git merge --ff" route:
649 additional test for "git merge --ff" route:
650
650
651 $ cd t
651 $ cd t
652 $ hg tip -q
652 $ hg tip -q
653 8:3473d20bddcf
653 8:3473d20bddcf
654 $ hg update -q -C af6d2edbb0d3
654 $ hg update -q -C af6d2edbb0d3
655 $ cd s
655 $ cd s
656 $ git checkout -q testing
656 $ git checkout -q testing
657 $ mkdir .hg
657 $ mkdir .hg
658 $ echo '.hg/hgrc in git repo' > .hg/hgrc
658 $ echo '.hg/hgrc in git repo' > .hg/hgrc
659 $ mkdir -p sub/.hg
659 $ mkdir -p sub/.hg
660 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
660 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
661 $ git add .hg sub
661 $ git add .hg sub
662 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
662 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
663 $ git push -q origin testing
663 $ git push -q origin testing
664 $ cd ..
664 $ cd ..
665 $ grep ' s$' .hgsubstate
665 $ grep ' s$' .hgsubstate
666 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
666 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
667 $ hg commit -qm 'commit with git revision including .hg/hgrc'
667 $ hg commit -qm 'commit with git revision including .hg/hgrc'
668 $ hg parents -q
668 $ hg parents -q
669 9:ed23f7fe024e
669 9:ed23f7fe024e
670 $ grep ' s$' .hgsubstate
670 $ grep ' s$' .hgsubstate
671 f262643c1077219fbd3858d54e78ef050ef84fbf s
671 f262643c1077219fbd3858d54e78ef050ef84fbf s
672 $ cd ..
672 $ cd ..
673
673
674 $ cd tc
674 $ cd tc
675 $ hg update -q -C af6d2edbb0d3
675 $ hg update -q -C af6d2edbb0d3
676 $ test -f s/.hg/hgrc
676 $ test -f s/.hg/hgrc
677 [1]
677 [1]
678 $ test -f s/sub/.hg/hgrc
678 $ test -f s/sub/.hg/hgrc
679 [1]
679 [1]
680 $ cd ..
680 $ cd ..
681 $ hg -R tc pull -q
681 $ hg -R tc pull -q
682 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
682 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
683 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
683 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
684 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
684 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
685 $ cd tc
685 $ cd tc
686 $ hg parents -q
686 $ hg parents -q
687 9:ed23f7fe024e
687 9:ed23f7fe024e
688 $ grep ' s$' .hgsubstate
688 $ grep ' s$' .hgsubstate
689 f262643c1077219fbd3858d54e78ef050ef84fbf s
689 f262643c1077219fbd3858d54e78ef050ef84fbf s
690 $ test -f s/.hg/hgrc
690 $ test -f s/.hg/hgrc
691 [1]
691 [1]
692 $ test -f s/sub/.hg/hgrc
692 $ test -f s/sub/.hg/hgrc
693 [1]
693 [1]
694
694
695 Test that sanitizing is omitted in meta data area:
695 Test that sanitizing is omitted in meta data area:
696
696
697 $ mkdir s/.git/.hg
697 $ mkdir s/.git/.hg
698 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
698 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
699 $ hg update -q -C af6d2edbb0d3
699 $ hg update -q -C af6d2edbb0d3
700 checking out detached HEAD in subrepo s
700 checking out detached HEAD in subrepo s
701 check out a git branch if you intend to make changes
701 check out a git branch if you intend to make changes
702
702
703 check differences made by most recent change
703 check differences made by most recent change
704 $ cd s
704 $ cd s
705 $ cat > foobar << EOF
705 $ cat > foobar << EOF
706 > woopwoop
706 > woopwoop
707 >
707 >
708 > foo
708 > foo
709 > bar
709 > bar
710 > EOF
710 > EOF
711 $ git add foobar
711 $ git add foobar
712 $ cd ..
712 $ cd ..
713
713
714 $ hg diff --subrepos
714 $ hg diff --subrepos
715 diff --git a/s/foobar b/s/foobar
715 diff --git a/s/foobar b/s/foobar
716 new file mode 100644
716 new file mode 100644
717 index 0000000..8a5a5e2
717 index 0000000..8a5a5e2
718 --- /dev/null
718 --- /dev/null
719 +++ b/s/foobar
719 +++ b/s/foobar
720 @@ -0,0 +1,4 @@
720 @@ -0,0 +1,4 @@
721 +woopwoop
721 +woopwoop
722 +
722 +
723 +foo
723 +foo
724 +bar
724 +bar
725
725
726 $ hg commit --subrepos -m "Added foobar"
726 $ hg commit --subrepos -m "Added foobar"
727 committing subrepository s
727 committing subrepository s
728 created new head
728 created new head
729
729
730 $ hg diff -c . --subrepos --nodates
730 $ hg diff -c . --subrepos --nodates
731 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
731 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
732 --- a/.hgsubstate
732 --- a/.hgsubstate
733 +++ b/.hgsubstate
733 +++ b/.hgsubstate
734 @@ -1,1 +1,1 @@
734 @@ -1,1 +1,1 @@
735 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
735 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
736 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
736 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
737 diff --git a/s/foobar b/s/foobar
737 diff --git a/s/foobar b/s/foobar
738 new file mode 100644
738 new file mode 100644
739 index 0000000..8a5a5e2
739 index 0000000..8a5a5e2
740 --- /dev/null
740 --- /dev/null
741 +++ b/s/foobar
741 +++ b/s/foobar
742 @@ -0,0 +1,4 @@
742 @@ -0,0 +1,4 @@
743 +woopwoop
743 +woopwoop
744 +
744 +
745 +foo
745 +foo
746 +bar
746 +bar
747
747
748 check output when only diffing the subrepository
748 check output when only diffing the subrepository
749 $ hg diff -c . --subrepos s
749 $ hg diff -c . --subrepos s
750 diff --git a/s/foobar b/s/foobar
750 diff --git a/s/foobar b/s/foobar
751 new file mode 100644
751 new file mode 100644
752 index 0000000..8a5a5e2
752 index 0000000..8a5a5e2
753 --- /dev/null
753 --- /dev/null
754 +++ b/s/foobar
754 +++ b/s/foobar
755 @@ -0,0 +1,4 @@
755 @@ -0,0 +1,4 @@
756 +woopwoop
756 +woopwoop
757 +
757 +
758 +foo
758 +foo
759 +bar
759 +bar
760
760
761 check output when diffing something else
761 check output when diffing something else
762 $ hg diff -c . --subrepos .hgsubstate --nodates
762 $ hg diff -c . --subrepos .hgsubstate --nodates
763 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
763 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
764 --- a/.hgsubstate
764 --- a/.hgsubstate
765 +++ b/.hgsubstate
765 +++ b/.hgsubstate
766 @@ -1,1 +1,1 @@
766 @@ -1,1 +1,1 @@
767 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
767 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
768 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
768 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
769
769
770 add new changes, including whitespace
770 add new changes, including whitespace
771 $ cd s
771 $ cd s
772 $ cat > foobar << EOF
772 $ cat > foobar << EOF
773 > woop woop
773 > woop woop
774 >
774 >
775 > foo
775 > foo
776 > bar
776 > bar
777 > EOF
777 > EOF
778 $ echo foo > barfoo
778 $ echo foo > barfoo
779 $ git add barfoo
779 $ git add barfoo
780 $ cd ..
780 $ cd ..
781
781
782 $ hg diff --subrepos --ignore-all-space
782 $ hg diff --subrepos --ignore-all-space
783 diff --git a/s/barfoo b/s/barfoo
783 diff --git a/s/barfoo b/s/barfoo
784 new file mode 100644
784 new file mode 100644
785 index 0000000..257cc56
785 index 0000000..257cc56
786 --- /dev/null
786 --- /dev/null
787 +++ b/s/barfoo
787 +++ b/s/barfoo
788 @@ -0,0 +1* @@ (glob)
788 @@ -0,0 +1* @@ (glob)
789 +foo
789 +foo
790 $ hg diff --subrepos s/foobar
790 $ hg diff --subrepos s/foobar
791 diff --git a/s/foobar b/s/foobar
791 diff --git a/s/foobar b/s/foobar
792 index 8a5a5e2..bd5812a 100644
792 index 8a5a5e2..bd5812a 100644
793 --- a/s/foobar
793 --- a/s/foobar
794 +++ b/s/foobar
794 +++ b/s/foobar
795 @@ -1,4 +1,4 @@
795 @@ -1,4 +1,4 @@
796 -woopwoop
796 -woopwoop
797 +woop woop
797 +woop woop
798
798
799 foo
799 foo
800 bar
800 bar
801
801
802 execute a diffstat
802 execute a diffstat
803 the output contains a regex, because git 1.7.10 and 1.7.11
803 the output contains a regex, because git 1.7.10 and 1.7.11
804 change the amount of whitespace
804 change the amount of whitespace
805 $ hg diff --subrepos --stat
805 $ hg diff --subrepos --stat
806 \s*barfoo |\s*1 + (re)
806 \s*barfoo |\s*1 + (re)
807 \s*foobar |\s*2 +- (re)
807 \s*foobar |\s*2 +- (re)
808 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
808 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
809
809
810 adding an include should ignore the other elements
810 adding an include should ignore the other elements
811 $ hg diff --subrepos -I s/foobar
811 $ hg diff --subrepos -I s/foobar
812 diff --git a/s/foobar b/s/foobar
812 diff --git a/s/foobar b/s/foobar
813 index 8a5a5e2..bd5812a 100644
813 index 8a5a5e2..bd5812a 100644
814 --- a/s/foobar
814 --- a/s/foobar
815 +++ b/s/foobar
815 +++ b/s/foobar
816 @@ -1,4 +1,4 @@
816 @@ -1,4 +1,4 @@
817 -woopwoop
817 -woopwoop
818 +woop woop
818 +woop woop
819
819
820 foo
820 foo
821 bar
821 bar
822
822
823 adding an exclude should ignore this element
823 adding an exclude should ignore this element
824 $ hg diff --subrepos -X s/foobar
824 $ hg diff --subrepos -X s/foobar
825 diff --git a/s/barfoo b/s/barfoo
825 diff --git a/s/barfoo b/s/barfoo
826 new file mode 100644
826 new file mode 100644
827 index 0000000..257cc56
827 index 0000000..257cc56
828 --- /dev/null
828 --- /dev/null
829 +++ b/s/barfoo
829 +++ b/s/barfoo
830 @@ -0,0 +1* @@ (glob)
830 @@ -0,0 +1* @@ (glob)
831 +foo
831 +foo
832
832
833 moving a file should show a removal and an add
833 moving a file should show a removal and an add
834 $ hg revert --all
834 $ hg revert --all
835 reverting subrepo ../gitroot
835 reverting subrepo ../gitroot
836 $ cd s
836 $ cd s
837 $ git mv foobar woop
837 $ git mv foobar woop
838 $ cd ..
838 $ cd ..
839 $ hg diff --subrepos
839 $ hg diff --subrepos
840 diff --git a/s/foobar b/s/foobar
840 diff --git a/s/foobar b/s/foobar
841 deleted file mode 100644
841 deleted file mode 100644
842 index 8a5a5e2..0000000
842 index 8a5a5e2..0000000
843 --- a/s/foobar
843 --- a/s/foobar
844 +++ /dev/null
844 +++ /dev/null
845 @@ -1,4 +0,0 @@
845 @@ -1,4 +0,0 @@
846 -woopwoop
846 -woopwoop
847 -
847 -
848 -foo
848 -foo
849 -bar
849 -bar
850 diff --git a/s/woop b/s/woop
850 diff --git a/s/woop b/s/woop
851 new file mode 100644
851 new file mode 100644
852 index 0000000..8a5a5e2
852 index 0000000..8a5a5e2
853 --- /dev/null
853 --- /dev/null
854 +++ b/s/woop
854 +++ b/s/woop
855 @@ -0,0 +1,4 @@
855 @@ -0,0 +1,4 @@
856 +woopwoop
856 +woopwoop
857 +
857 +
858 +foo
858 +foo
859 +bar
859 +bar
860 $ rm s/woop
860 $ rm s/woop
861
861
862 revert the subrepository
862 revert the subrepository
863 $ hg revert --all
863 $ hg revert --all
864 reverting subrepo ../gitroot
864 reverting subrepo ../gitroot
865
865
866 $ hg status --subrepos
866 $ hg status --subrepos
867 ? s/barfoo
867 ? s/barfoo
868 ? s/foobar.orig
868 ? s/foobar.orig
869
869
870 $ mv s/foobar.orig s/foobar
870 $ mv s/foobar.orig s/foobar
871
871
872 $ hg revert --no-backup s
872 $ hg revert --no-backup s
873 reverting subrepo ../gitroot
873 reverting subrepo ../gitroot
874
874
875 $ hg status --subrepos
875 $ hg status --subrepos
876 ? s/barfoo
876 ? s/barfoo
877
877
878 revert moves orig files to the right place
878 revert moves orig files to the right place
879 $ echo 'bloop' > s/foobar
879 $ echo 'bloop' > s/foobar
880 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
880 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
881 reverting subrepo ../gitroot
881 reverting subrepo ../gitroot
882 creating directory: $TESTTMP/tc/.hg/origbackups (glob)
882 creating directory: $TESTTMP/tc/.hg/origbackups (glob)
883 saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar.orig (glob)
883 saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar.orig (glob)
884 $ ls .hg/origbackups
884 $ ls .hg/origbackups
885 foobar.orig
885 foobar.orig
886 $ rm -rf .hg/origbackups
886 $ rm -rf .hg/origbackups
887
887
888 show file at specific revision
888 show file at specific revision
889 $ cat > s/foobar << EOF
889 $ cat > s/foobar << EOF
890 > woop woop
890 > woop woop
891 > fooo bar
891 > fooo bar
892 > EOF
892 > EOF
893 $ hg commit --subrepos -m "updated foobar"
893 $ hg commit --subrepos -m "updated foobar"
894 committing subrepository s
894 committing subrepository s
895 $ cat > s/foobar << EOF
895 $ cat > s/foobar << EOF
896 > current foobar
896 > current foobar
897 > (should not be visible using hg cat)
897 > (should not be visible using hg cat)
898 > EOF
898 > EOF
899
899
900 $ hg cat -r . s/foobar
900 $ hg cat -r . s/foobar
901 woop woop
901 woop woop
902 fooo bar (no-eol)
902 fooo bar (no-eol)
903 $ hg cat -r "parents(.)" s/foobar > catparents
903 $ hg cat -r "parents(.)" s/foobar > catparents
904
904
905 $ mkdir -p tmp/s
905 $ mkdir -p tmp/s
906
906
907 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
907 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
908 $ diff tmp/% catparents
908 $ diff tmp/% catparents
909
909
910 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
910 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
911 $ diff tmp/foobar catparents
911 $ diff tmp/foobar catparents
912
912
913 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
913 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
914 $ diff tmp/s/otherfoobar catparents
914 $ diff tmp/s/otherfoobar catparents
915
915
916 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
916 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
917 $ diff tmp/s/foobar catparents
917 $ diff tmp/s/foobar catparents
918
918
919 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
919 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
920 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
920 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
921
921
922 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
922 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
923 $ diff tmp/10 catparents
923 $ diff tmp/10 catparents
924
924
925 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
925 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
926 $ diff tmp/255ee8cf690e catparents
926 $ diff tmp/255ee8cf690e catparents
927
927
928 $ rm tmp/10
928 $ rm tmp/10
929 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
929 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
930 $ diff tmp/10 catparents
930 $ diff tmp/10 catparents
931
931
932 $ mkdir tmp/tc
932 $ mkdir tmp/tc
933 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
933 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
934 $ diff tmp/tc/foobar catparents
934 $ diff tmp/tc/foobar catparents
935
935
936 cleanup
936 cleanup
937 $ rm -r tmp
937 $ rm -r tmp
938 $ rm catparents
938 $ rm catparents
939
939
940 add git files, using either files or patterns
940 add git files, using either files or patterns
941 $ echo "hsss! hsssssssh!" > s/snake.python
941 $ echo "hsss! hsssssssh!" > s/snake.python
942 $ echo "ccc" > s/c.c
942 $ echo "ccc" > s/c.c
943 $ echo "cpp" > s/cpp.cpp
943 $ echo "cpp" > s/cpp.cpp
944
944
945 $ hg add s/snake.python s/c.c s/cpp.cpp
945 $ hg add s/snake.python s/c.c s/cpp.cpp
946 $ hg st --subrepos s
946 $ hg st --subrepos s
947 M s/foobar
947 M s/foobar
948 A s/c.c
948 A s/c.c
949 A s/cpp.cpp
949 A s/cpp.cpp
950 A s/snake.python
950 A s/snake.python
951 ? s/barfoo
951 ? s/barfoo
952 $ hg revert s
952 $ hg revert s
953 reverting subrepo ../gitroot
953 reverting subrepo ../gitroot
954
954
955 $ hg add --subrepos "glob:**.python"
955 $ hg add --subrepos "glob:**.python"
956 adding s/snake.python (glob)
956 adding s/snake.python (glob)
957 $ hg st --subrepos s
957 $ hg st --subrepos s
958 A s/snake.python
958 A s/snake.python
959 ? s/barfoo
959 ? s/barfoo
960 ? s/c.c
960 ? s/c.c
961 ? s/cpp.cpp
961 ? s/cpp.cpp
962 ? s/foobar.orig
962 ? s/foobar.orig
963 $ hg revert s
963 $ hg revert s
964 reverting subrepo ../gitroot
964 reverting subrepo ../gitroot
965
965
966 $ hg add --subrepos s
966 $ hg add --subrepos s
967 adding s/barfoo (glob)
967 adding s/barfoo (glob)
968 adding s/c.c (glob)
968 adding s/c.c (glob)
969 adding s/cpp.cpp (glob)
969 adding s/cpp.cpp (glob)
970 adding s/foobar.orig (glob)
970 adding s/foobar.orig (glob)
971 adding s/snake.python (glob)
971 adding s/snake.python (glob)
972 $ hg st --subrepos s
972 $ hg st --subrepos s
973 A s/barfoo
973 A s/barfoo
974 A s/c.c
974 A s/c.c
975 A s/cpp.cpp
975 A s/cpp.cpp
976 A s/foobar.orig
976 A s/foobar.orig
977 A s/snake.python
977 A s/snake.python
978 $ hg revert s
978 $ hg revert s
979 reverting subrepo ../gitroot
979 reverting subrepo ../gitroot
980 make sure everything is reverted correctly
980 make sure everything is reverted correctly
981 $ hg st --subrepos s
981 $ hg st --subrepos s
982 ? s/barfoo
982 ? s/barfoo
983 ? s/c.c
983 ? s/c.c
984 ? s/cpp.cpp
984 ? s/cpp.cpp
985 ? s/foobar.orig
985 ? s/foobar.orig
986 ? s/snake.python
986 ? s/snake.python
987
987
988 $ hg add --subrepos --exclude "path:s/c.c"
988 $ hg add --subrepos --exclude "path:s/c.c"
989 adding s/barfoo (glob)
989 adding s/barfoo (glob)
990 adding s/cpp.cpp (glob)
990 adding s/cpp.cpp (glob)
991 adding s/foobar.orig (glob)
991 adding s/foobar.orig (glob)
992 adding s/snake.python (glob)
992 adding s/snake.python (glob)
993 $ hg st --subrepos s
993 $ hg st --subrepos s
994 A s/barfoo
994 A s/barfoo
995 A s/cpp.cpp
995 A s/cpp.cpp
996 A s/foobar.orig
996 A s/foobar.orig
997 A s/snake.python
997 A s/snake.python
998 ? s/c.c
998 ? s/c.c
999 $ hg revert --all -q
999 $ hg revert --all -q
1000
1000
1001 .hgignore should not have influence in subrepos
1001 .hgignore should not have influence in subrepos
1002 $ cat > .hgignore << EOF
1002 $ cat > .hgignore << EOF
1003 > syntax: glob
1003 > syntax: glob
1004 > *.python
1004 > *.python
1005 > EOF
1005 > EOF
1006 $ hg add .hgignore
1006 $ hg add .hgignore
1007 $ hg add --subrepos "glob:**.python" s/barfoo
1007 $ hg add --subrepos "glob:**.python" s/barfoo
1008 adding s/snake.python (glob)
1008 adding s/snake.python (glob)
1009 $ hg st --subrepos s
1009 $ hg st --subrepos s
1010 A s/barfoo
1010 A s/barfoo
1011 A s/snake.python
1011 A s/snake.python
1012 ? s/c.c
1012 ? s/c.c
1013 ? s/cpp.cpp
1013 ? s/cpp.cpp
1014 ? s/foobar.orig
1014 ? s/foobar.orig
1015 $ hg revert --all -q
1015 $ hg revert --all -q
1016
1016
1017 .gitignore should have influence,
1017 .gitignore should have influence,
1018 except for explicitly added files (no patterns)
1018 except for explicitly added files (no patterns)
1019 $ cat > s/.gitignore << EOF
1019 $ cat > s/.gitignore << EOF
1020 > *.python
1020 > *.python
1021 > EOF
1021 > EOF
1022 $ hg add s/.gitignore
1022 $ hg add s/.gitignore
1023 $ hg st --subrepos s
1023 $ hg st --subrepos s
1024 A s/.gitignore
1024 A s/.gitignore
1025 ? s/barfoo
1025 ? s/barfoo
1026 ? s/c.c
1026 ? s/c.c
1027 ? s/cpp.cpp
1027 ? s/cpp.cpp
1028 ? s/foobar.orig
1028 ? s/foobar.orig
1029 $ hg st --subrepos s --all
1029 $ hg st --subrepos s --all
1030 A s/.gitignore
1030 A s/.gitignore
1031 ? s/barfoo
1031 ? s/barfoo
1032 ? s/c.c
1032 ? s/c.c
1033 ? s/cpp.cpp
1033 ? s/cpp.cpp
1034 ? s/foobar.orig
1034 ? s/foobar.orig
1035 I s/snake.python
1035 I s/snake.python
1036 C s/f
1036 C s/f
1037 C s/foobar
1037 C s/foobar
1038 C s/g
1038 C s/g
1039 $ hg add --subrepos "glob:**.python"
1039 $ hg add --subrepos "glob:**.python"
1040 $ hg st --subrepos s
1040 $ hg st --subrepos s
1041 A s/.gitignore
1041 A s/.gitignore
1042 ? s/barfoo
1042 ? s/barfoo
1043 ? s/c.c
1043 ? s/c.c
1044 ? s/cpp.cpp
1044 ? s/cpp.cpp
1045 ? s/foobar.orig
1045 ? s/foobar.orig
1046 $ hg add --subrepos s/snake.python
1046 $ hg add --subrepos s/snake.python
1047 $ hg st --subrepos s
1047 $ hg st --subrepos s
1048 A s/.gitignore
1048 A s/.gitignore
1049 A s/snake.python
1049 A s/snake.python
1050 ? s/barfoo
1050 ? s/barfoo
1051 ? s/c.c
1051 ? s/c.c
1052 ? s/cpp.cpp
1052 ? s/cpp.cpp
1053 ? s/foobar.orig
1053 ? s/foobar.orig
1054
1054
1055 correctly do a dry run
1055 correctly do a dry run
1056 $ hg add --subrepos s --dry-run
1056 $ hg add --subrepos s --dry-run
1057 adding s/barfoo (glob)
1057 adding s/barfoo (glob)
1058 adding s/c.c (glob)
1058 adding s/c.c (glob)
1059 adding s/cpp.cpp (glob)
1059 adding s/cpp.cpp (glob)
1060 adding s/foobar.orig (glob)
1060 adding s/foobar.orig (glob)
1061 $ hg st --subrepos s
1061 $ hg st --subrepos s
1062 A s/.gitignore
1062 A s/.gitignore
1063 A s/snake.python
1063 A s/snake.python
1064 ? s/barfoo
1064 ? s/barfoo
1065 ? s/c.c
1065 ? s/c.c
1066 ? s/cpp.cpp
1066 ? s/cpp.cpp
1067 ? s/foobar.orig
1067 ? s/foobar.orig
1068
1068
1069 error given when adding an already tracked file
1069 error given when adding an already tracked file
1070 $ hg add s/.gitignore
1070 $ hg add s/.gitignore
1071 s/.gitignore already tracked!
1071 s/.gitignore already tracked!
1072 [1]
1072 [1]
1073 $ hg add s/g
1073 $ hg add s/g
1074 s/g already tracked!
1074 s/g already tracked!
1075 [1]
1075 [1]
1076
1076
1077 removed files can be re-added
1077 removed files can be re-added
1078 removing files using 'rm' or 'git rm' has the same effect,
1078 removing files using 'rm' or 'git rm' has the same effect,
1079 since we ignore the staging area
1079 since we ignore the staging area
1080 $ hg ci --subrepos -m 'snake'
1080 $ hg ci --subrepos -m 'snake'
1081 committing subrepository s
1081 committing subrepository s
1082 $ cd s
1082 $ cd s
1083 $ rm snake.python
1083 $ rm snake.python
1084 (remove leftover .hg so Mercurial doesn't look for a root here)
1084 (remove leftover .hg so Mercurial doesn't look for a root here)
1085 $ rm -rf .hg
1085 $ rm -rf .hg
1086 $ hg status --subrepos --all .
1086 $ hg status --subrepos --all .
1087 R snake.python
1087 R snake.python
1088 ? barfoo
1088 ? barfoo
1089 ? c.c
1089 ? c.c
1090 ? cpp.cpp
1090 ? cpp.cpp
1091 ? foobar.orig
1091 ? foobar.orig
1092 C .gitignore
1092 C .gitignore
1093 C f
1093 C f
1094 C foobar
1094 C foobar
1095 C g
1095 C g
1096 $ git rm snake.python
1096 $ git rm snake.python
1097 rm 'snake.python'
1097 rm 'snake.python'
1098 $ hg status --subrepos --all .
1098 $ hg status --subrepos --all .
1099 R snake.python
1099 R snake.python
1100 ? barfoo
1100 ? barfoo
1101 ? c.c
1101 ? c.c
1102 ? cpp.cpp
1102 ? cpp.cpp
1103 ? foobar.orig
1103 ? foobar.orig
1104 C .gitignore
1104 C .gitignore
1105 C f
1105 C f
1106 C foobar
1106 C foobar
1107 C g
1107 C g
1108 $ touch snake.python
1108 $ touch snake.python
1109 $ cd ..
1109 $ cd ..
1110 $ hg add s/snake.python
1110 $ hg add s/snake.python
1111 $ hg status -S
1111 $ hg status -S
1112 M s/snake.python
1112 M s/snake.python
1113 ? .hgignore
1113 ? .hgignore
1114 ? s/barfoo
1114 ? s/barfoo
1115 ? s/c.c
1115 ? s/c.c
1116 ? s/cpp.cpp
1116 ? s/cpp.cpp
1117 ? s/foobar.orig
1117 ? s/foobar.orig
1118 $ hg revert --all -q
1118 $ hg revert --all -q
1119
1119
1120 make sure we show changed files, rather than changed subtrees
1120 make sure we show changed files, rather than changed subtrees
1121 $ mkdir s/foo
1121 $ mkdir s/foo
1122 $ touch s/foo/bwuh
1122 $ touch s/foo/bwuh
1123 $ hg add s/foo/bwuh
1123 $ hg add s/foo/bwuh
1124 $ hg commit -S -m "add bwuh"
1124 $ hg commit -S -m "add bwuh"
1125 committing subrepository s
1125 committing subrepository s
1126 $ hg status -S --change .
1126 $ hg status -S --change .
1127 M .hgsubstate
1127 M .hgsubstate
1128 A s/foo/bwuh
1128 A s/foo/bwuh
1129 ? s/barfoo
1129 ? s/barfoo
1130 ? s/c.c
1130 ? s/c.c
1131 ? s/cpp.cpp
1131 ? s/cpp.cpp
1132 ? s/foobar.orig
1132 ? s/foobar.orig
1133 ? s/snake.python.orig
1133 ? s/snake.python.orig
1134
1134
1135 test for Git CVE-2016-3068
1135 test for Git CVE-2016-3068
1136 $ hg init malicious-subrepository
1136 $ hg init malicious-subrepository
1137 $ cd malicious-subrepository
1137 $ cd malicious-subrepository
1138 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1138 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1139 $ git init s
1139 $ git init s
1140 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1140 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1141 $ cd s
1141 $ cd s
1142 $ git commit --allow-empty -m 'empty'
1142 $ git commit --allow-empty -m 'empty'
1143 [master (root-commit) 153f934] empty
1143 [master (root-commit) 153f934] empty
1144 $ cd ..
1144 $ cd ..
1145 $ hg add .hgsub
1145 $ hg add .hgsub
1146 $ hg commit -m "add subrepo"
1146 $ hg commit -m "add subrepo"
1147 $ cd ..
1147 $ cd ..
1148 $ rm -f pwned.txt
1148 $ rm -f pwned.txt
1149 $ unset GIT_ALLOW_PROTOCOL
1149 $ unset GIT_ALLOW_PROTOCOL
1150 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1150 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1151 > malicious-subrepository malicious-subrepository-protected
1151 > malicious-subrepository malicious-subrepository-protected
1152 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'... (glob)
1152 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'... (glob)
1153 fatal: transport 'ext' not allowed
1153 fatal: transport 'ext' not allowed
1154 updating to branch default
1154 updating to branch default
1155 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1155 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1156 abort: git clone error 128 in s (in subrepo s)
1156 abort: git clone error 128 in s (in subrepo s)
1157 [255]
1157 [255]
1158 $ f -Dq pwned.txt
1158 $ f -Dq pwned.txt
1159 pwned.txt: file not found
1159 pwned.txt: file not found
1160
1160
1161 whitelisting of ext should be respected (that's the git submodule behaviour)
1161 whitelisting of ext should be respected (that's the git submodule behaviour)
1162 $ rm -f pwned.txt
1162 $ rm -f pwned.txt
1163 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1163 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1164 > malicious-subrepository malicious-subrepository-clone-allowed
1164 > malicious-subrepository malicious-subrepository-clone-allowed
1165 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'... (glob)
1165 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'... (glob)
1166 fatal: Could not read from remote repository.
1166 fatal: Could not read from remote repository.
1167
1167
1168 Please make sure you have the correct access rights
1168 Please make sure you have the correct access rights
1169 and the repository exists.
1169 and the repository exists.
1170 updating to branch default
1170 updating to branch default
1171 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1171 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1172 abort: git clone error 128 in s (in subrepo s)
1172 abort: git clone error 128 in s (in subrepo s)
1173 [255]
1173 [255]
1174 $ f -Dq pwned.txt
1174 $ f -Dq pwned.txt
1175 pwned: you asked for it
1175 pwned: you asked for it
@@ -1,641 +1,641 b''
1 #require svn15
1 #require svn15
2
2
3 $ SVNREPOPATH=`pwd`/svn-repo
3 $ SVNREPOPATH=`pwd`/svn-repo
4 #if windows
4 #if windows
5 $ SVNREPOURL=file:///`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
5 $ SVNREPOURL=file:///`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
6 #else
6 #else
7 $ SVNREPOURL=file://`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
7 $ SVNREPOURL=file://`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
8 #endif
8 #endif
9
9
10 $ filter_svn_output () {
10 $ filter_svn_output () {
11 > egrep -v 'Committing|Transmitting|Updating|(^$)' || true
11 > egrep -v 'Committing|Transmitting|Updating|(^$)' || true
12 > }
12 > }
13
13
14 create subversion repo
14 create subversion repo
15
15
16 $ WCROOT="`pwd`/svn-wc"
16 $ WCROOT="`pwd`/svn-wc"
17 $ svnadmin create svn-repo
17 $ svnadmin create svn-repo
18 $ svn co "$SVNREPOURL" svn-wc
18 $ svn co "$SVNREPOURL" svn-wc
19 Checked out revision 0.
19 Checked out revision 0.
20 $ cd svn-wc
20 $ cd svn-wc
21 $ mkdir src
21 $ mkdir src
22 $ echo alpha > src/alpha
22 $ echo alpha > src/alpha
23 $ svn add src
23 $ svn add src
24 A src
24 A src
25 A src/alpha (glob)
25 A src/alpha (glob)
26 $ mkdir externals
26 $ mkdir externals
27 $ echo other > externals/other
27 $ echo other > externals/other
28 $ svn add externals
28 $ svn add externals
29 A externals
29 A externals
30 A externals/other (glob)
30 A externals/other (glob)
31 $ svn ci -qm 'Add alpha'
31 $ svn ci -qm 'Add alpha'
32 $ svn up -q
32 $ svn up -q
33 $ echo "externals -r1 $SVNREPOURL/externals" > extdef
33 $ echo "externals -r1 $SVNREPOURL/externals" > extdef
34 $ svn propset -F extdef svn:externals src
34 $ svn propset -F extdef svn:externals src
35 property 'svn:externals' set on 'src'
35 property 'svn:externals' set on 'src'
36 $ svn ci -qm 'Setting externals'
36 $ svn ci -qm 'Setting externals'
37 $ cd ..
37 $ cd ..
38
38
39 create hg repo
39 create hg repo
40
40
41 $ mkdir sub
41 $ mkdir sub
42 $ cd sub
42 $ cd sub
43 $ hg init t
43 $ hg init t
44 $ cd t
44 $ cd t
45
45
46 first revision, no sub
46 first revision, no sub
47
47
48 $ echo a > a
48 $ echo a > a
49 $ hg ci -Am0
49 $ hg ci -Am0
50 adding a
50 adding a
51
51
52 add first svn sub with leading whitespaces
52 add first svn sub with leading whitespaces
53
53
54 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
54 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
55 $ echo "subdir/s = [svn] $SVNREPOURL/src" >> .hgsub
55 $ echo "subdir/s = [svn] $SVNREPOURL/src" >> .hgsub
56 $ svn co --quiet "$SVNREPOURL"/src s
56 $ svn co --quiet "$SVNREPOURL"/src s
57 $ mkdir subdir
57 $ mkdir subdir
58 $ svn co --quiet "$SVNREPOURL"/src subdir/s
58 $ svn co --quiet "$SVNREPOURL"/src subdir/s
59 $ hg add .hgsub
59 $ hg add .hgsub
60 $ hg ci -m1
60 $ hg ci -m1
61
61
62 make sure we avoid empty commits (issue2445)
62 make sure we avoid empty commits (issue2445)
63
63
64 $ hg sum
64 $ hg sum
65 parent: 1:* tip (glob)
65 parent: 1:* tip (glob)
66 1
66 1
67 branch: default
67 branch: default
68 commit: (clean)
68 commit: (clean)
69 update: (current)
69 update: (current)
70 phases: 2 draft
70 phases: 2 draft
71 $ hg ci -moops
71 $ hg ci -moops
72 nothing changed
72 nothing changed
73 [1]
73 [1]
74
74
75 debugsub
75 debugsub
76
76
77 $ hg debugsub
77 $ hg debugsub
78 path s
78 path s
79 source file://*/svn-repo/src (glob)
79 source file://*/svn-repo/src (glob)
80 revision 2
80 revision 2
81 path subdir/s
81 path subdir/s
82 source file://*/svn-repo/src (glob)
82 source file://*/svn-repo/src (glob)
83 revision 2
83 revision 2
84
84
85 change file in svn and hg, commit
85 change file in svn and hg, commit
86
86
87 $ echo a >> a
87 $ echo a >> a
88 $ echo alpha >> s/alpha
88 $ echo alpha >> s/alpha
89 $ hg sum
89 $ hg sum
90 parent: 1:* tip (glob)
90 parent: 1:* tip (glob)
91 1
91 1
92 branch: default
92 branch: default
93 commit: 1 modified, 1 subrepos
93 commit: 1 modified, 1 subrepos
94 update: (current)
94 update: (current)
95 phases: 2 draft
95 phases: 2 draft
96 $ hg commit --subrepos -m 'Message!' | filter_svn_output
96 $ hg commit --subrepos -m 'Message!' | filter_svn_output
97 committing subrepository s
97 committing subrepository s
98 Sending*s/alpha (glob)
98 Sending*s/alpha (glob)
99 Committed revision 3.
99 Committed revision 3.
100 Fetching external item into '*s/externals'* (glob)
100 Fetching external item into '*s/externals'* (glob)
101 External at revision 1.
101 External at revision 1.
102 At revision 3.
102 At revision 3.
103 $ hg debugsub
103 $ hg debugsub
104 path s
104 path s
105 source file://*/svn-repo/src (glob)
105 source file://*/svn-repo/src (glob)
106 revision 3
106 revision 3
107 path subdir/s
107 path subdir/s
108 source file://*/svn-repo/src (glob)
108 source file://*/svn-repo/src (glob)
109 revision 2
109 revision 2
110
110
111 missing svn file, commit should fail
111 missing svn file, commit should fail
112
112
113 $ rm s/alpha
113 $ rm s/alpha
114 $ hg commit --subrepos -m 'abort on missing file'
114 $ hg commit --subrepos -m 'abort on missing file'
115 committing subrepository s
115 committing subrepository s
116 abort: cannot commit missing svn entries (in subrepo s)
116 abort: cannot commit missing svn entries (in subrepo s)
117 [255]
117 [255]
118 $ svn revert s/alpha > /dev/null
118 $ svn revert s/alpha > /dev/null
119
119
120 add an unrelated revision in svn and update the subrepo to without
120 add an unrelated revision in svn and update the subrepo to without
121 bringing any changes.
121 bringing any changes.
122
122
123 $ svn mkdir "$SVNREPOURL/unrelated" -qm 'create unrelated'
123 $ svn mkdir "$SVNREPOURL/unrelated" -qm 'create unrelated'
124 $ svn up -q s
124 $ svn up -q s
125 $ hg sum
125 $ hg sum
126 parent: 2:* tip (glob)
126 parent: 2:* tip (glob)
127 Message!
127 Message!
128 branch: default
128 branch: default
129 commit: (clean)
129 commit: (clean)
130 update: (current)
130 update: (current)
131 phases: 3 draft
131 phases: 3 draft
132
132
133 $ echo a > s/a
133 $ echo a > s/a
134
134
135 should be empty despite change to s/a
135 should be empty despite change to s/a
136
136
137 $ hg st
137 $ hg st
138
138
139 add a commit from svn
139 add a commit from svn
140
140
141 $ cd "$WCROOT/src"
141 $ cd "$WCROOT/src"
142 $ svn up -q
142 $ svn up -q
143 $ echo xyz >> alpha
143 $ echo xyz >> alpha
144 $ svn propset svn:mime-type 'text/xml' alpha
144 $ svn propset svn:mime-type 'text/xml' alpha
145 property 'svn:mime-type' set on 'alpha'
145 property 'svn:mime-type' set on 'alpha'
146 $ svn ci -qm 'amend a from svn'
146 $ svn ci -qm 'amend a from svn'
147 $ cd ../../sub/t
147 $ cd ../../sub/t
148
148
149 this commit from hg will fail
149 this commit from hg will fail
150
150
151 $ echo zzz >> s/alpha
151 $ echo zzz >> s/alpha
152 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
152 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
153 committing subrepository s
153 committing subrepository s
154 abort: svn:*Commit failed (details follow): (glob)
154 abort: svn:*Commit failed (details follow): (glob)
155 [255]
155 [255]
156 $ svn revert -q s/alpha
156 $ svn revert -q s/alpha
157
157
158 this commit fails because of meta changes
158 this commit fails because of meta changes
159
159
160 $ svn propset svn:mime-type 'text/html' s/alpha
160 $ svn propset svn:mime-type 'text/html' s/alpha
161 property 'svn:mime-type' set on 's/alpha' (glob)
161 property 'svn:mime-type' set on 's/alpha' (glob)
162 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
162 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
163 committing subrepository s
163 committing subrepository s
164 abort: svn:*Commit failed (details follow): (glob)
164 abort: svn:*Commit failed (details follow): (glob)
165 [255]
165 [255]
166 $ svn revert -q s/alpha
166 $ svn revert -q s/alpha
167
167
168 this commit fails because of externals changes
168 this commit fails because of externals changes
169
169
170 $ echo zzz > s/externals/other
170 $ echo zzz > s/externals/other
171 $ hg ci --subrepos -m 'amend externals from hg'
171 $ hg ci --subrepos -m 'amend externals from hg'
172 committing subrepository s
172 committing subrepository s
173 abort: cannot commit svn externals (in subrepo s)
173 abort: cannot commit svn externals (in subrepo s)
174 [255]
174 [255]
175 $ hg diff --subrepos -r 1:2 | grep -v diff
175 $ hg diff --subrepos -r 1:2 | grep -v diff
176 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
176 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
177 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
177 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
178 @@ -1,2 +1,2 @@
178 @@ -1,2 +1,2 @@
179 -2 s
179 -2 s
180 +3 s
180 +3 s
181 2 subdir/s
181 2 subdir/s
182 --- a/a Thu Jan 01 00:00:00 1970 +0000
182 --- a/a Thu Jan 01 00:00:00 1970 +0000
183 +++ b/a Thu Jan 01 00:00:00 1970 +0000
183 +++ b/a Thu Jan 01 00:00:00 1970 +0000
184 @@ -1,1 +1,2 @@
184 @@ -1,1 +1,2 @@
185 a
185 a
186 +a
186 +a
187 $ svn revert -q s/externals/other
187 $ svn revert -q s/externals/other
188
188
189 this commit fails because of externals meta changes
189 this commit fails because of externals meta changes
190
190
191 $ svn propset svn:mime-type 'text/html' s/externals/other
191 $ svn propset svn:mime-type 'text/html' s/externals/other
192 property 'svn:mime-type' set on 's/externals/other' (glob)
192 property 'svn:mime-type' set on 's/externals/other' (glob)
193 $ hg ci --subrepos -m 'amend externals from hg'
193 $ hg ci --subrepos -m 'amend externals from hg'
194 committing subrepository s
194 committing subrepository s
195 abort: cannot commit svn externals (in subrepo s)
195 abort: cannot commit svn externals (in subrepo s)
196 [255]
196 [255]
197 $ svn revert -q s/externals/other
197 $ svn revert -q s/externals/other
198
198
199 clone
199 clone
200
200
201 $ cd ..
201 $ cd ..
202 $ hg clone t tc
202 $ hg clone t tc
203 updating to branch default
203 updating to branch default
204 A tc/s/alpha (glob)
204 A tc/s/alpha (glob)
205 U tc/s (glob)
205 U tc/s (glob)
206
206
207 Fetching external item into 'tc/s/externals'* (glob)
207 Fetching external item into 'tc/s/externals'* (glob)
208 A tc/s/externals/other (glob)
208 A tc/s/externals/other (glob)
209 Checked out external at revision 1.
209 Checked out external at revision 1.
210
210
211 Checked out revision 3.
211 Checked out revision 3.
212 A tc/subdir/s/alpha (glob)
212 A tc/subdir/s/alpha (glob)
213 U tc/subdir/s (glob)
213 U tc/subdir/s (glob)
214
214
215 Fetching external item into 'tc/subdir/s/externals'* (glob)
215 Fetching external item into 'tc/subdir/s/externals'* (glob)
216 A tc/subdir/s/externals/other (glob)
216 A tc/subdir/s/externals/other (glob)
217 Checked out external at revision 1.
217 Checked out external at revision 1.
218
218
219 Checked out revision 2.
219 Checked out revision 2.
220 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 $ cd tc
221 $ cd tc
222
222
223 debugsub in clone
223 debugsub in clone
224
224
225 $ hg debugsub
225 $ hg debugsub
226 path s
226 path s
227 source file://*/svn-repo/src (glob)
227 source file://*/svn-repo/src (glob)
228 revision 3
228 revision 3
229 path subdir/s
229 path subdir/s
230 source file://*/svn-repo/src (glob)
230 source file://*/svn-repo/src (glob)
231 revision 2
231 revision 2
232
232
233 verify subrepo is contained within the repo directory
233 verify subrepo is contained within the repo directory
234
234
235 $ $PYTHON -c "import os.path; print os.path.exists('s')"
235 $ $PYTHON -c "import os.path; print os.path.exists('s')"
236 True
236 True
237
237
238 update to nullrev (must delete the subrepo)
238 update to nullrev (must delete the subrepo)
239
239
240 $ hg up null
240 $ hg up null
241 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
241 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
242 $ ls
242 $ ls
243
243
244 Check hg update --clean
244 Check hg update --clean
245 $ cd "$TESTTMP/sub/t"
245 $ cd "$TESTTMP/sub/t"
246 $ cd s
246 $ cd s
247 $ echo c0 > alpha
247 $ echo c0 > alpha
248 $ echo c1 > f1
248 $ echo c1 > f1
249 $ echo c1 > f2
249 $ echo c1 > f2
250 $ svn add f1 -q
250 $ svn add f1 -q
251 $ svn status | sort
251 $ svn status | sort
252
252
253 ? * a (glob)
253 ? * a (glob)
254 ? * f2 (glob)
254 ? * f2 (glob)
255 A * f1 (glob)
255 A * f1 (glob)
256 M * alpha (glob)
256 M * alpha (glob)
257 Performing status on external item at 'externals'* (glob)
257 Performing status on external item at 'externals'* (glob)
258 X * externals (glob)
258 X * externals (glob)
259 $ cd ../..
259 $ cd ../..
260 $ hg -R t update -C
260 $ hg -R t update -C
261
261
262 Fetching external item into 't/s/externals'* (glob)
262 Fetching external item into 't/s/externals'* (glob)
263 Checked out external at revision 1.
263 Checked out external at revision 1.
264
264
265 Checked out revision 3.
265 Checked out revision 3.
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 $ cd t/s
267 $ cd t/s
268 $ svn status | sort
268 $ svn status | sort
269
269
270 ? * a (glob)
270 ? * a (glob)
271 ? * f1 (glob)
271 ? * f1 (glob)
272 ? * f2 (glob)
272 ? * f2 (glob)
273 Performing status on external item at 'externals'* (glob)
273 Performing status on external item at 'externals'* (glob)
274 X * externals (glob)
274 X * externals (glob)
275
275
276 Sticky subrepositories, no changes
276 Sticky subrepositories, no changes
277 $ cd "$TESTTMP/sub/t"
277 $ cd "$TESTTMP/sub/t"
278 $ hg id -n
278 $ hg id -n
279 2
279 2
280 $ cd s
280 $ cd s
281 $ svnversion
281 $ svnversion
282 3
282 3
283 $ cd ..
283 $ cd ..
284 $ hg update 1
284 $ hg update 1
285 U *s/alpha (glob)
285 U *s/alpha (glob)
286
286
287 Fetching external item into '*s/externals'* (glob)
287 Fetching external item into '*s/externals'* (glob)
288 Checked out external at revision 1.
288 Checked out external at revision 1.
289
289
290 Checked out revision 2.
290 Checked out revision 2.
291 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 $ hg id -n
292 $ hg id -n
293 1
293 1
294 $ cd s
294 $ cd s
295 $ svnversion
295 $ svnversion
296 2
296 2
297 $ cd ..
297 $ cd ..
298
298
299 Sticky subrepositories, file changes
299 Sticky subrepositories, file changes
300 $ touch s/f1
300 $ touch s/f1
301 $ cd s
301 $ cd s
302 $ svn add f1
302 $ svn add f1
303 A f1
303 A f1
304 $ cd ..
304 $ cd ..
305 $ hg id -n
305 $ hg id -n
306 1+
306 1+
307 $ cd s
307 $ cd s
308 $ svnversion
308 $ svnversion
309 2M
309 2M
310 $ cd ..
310 $ cd ..
311 $ hg update tip
311 $ hg update tip
312 subrepository s diverged (local revision: 2, remote revision: 3)
312 subrepository s diverged (local revision: 2, remote revision: 3)
313 (M)erge, keep (l)ocal or keep (r)emote? m
313 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
314 subrepository sources for s differ
314 subrepository sources for s differ
315 use (l)ocal source (2) or (r)emote source (3)? l
315 use (l)ocal source (2) or (r)emote source (3)? l
316 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
317 $ hg id -n
317 $ hg id -n
318 2+
318 2+
319 $ cd s
319 $ cd s
320 $ svnversion
320 $ svnversion
321 2M
321 2M
322 $ cd ..
322 $ cd ..
323 $ hg update --clean tip
323 $ hg update --clean tip
324 U *s/alpha (glob)
324 U *s/alpha (glob)
325
325
326 Fetching external item into '*s/externals'* (glob)
326 Fetching external item into '*s/externals'* (glob)
327 Checked out external at revision 1.
327 Checked out external at revision 1.
328
328
329 Checked out revision 3.
329 Checked out revision 3.
330 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
331
331
332 Sticky subrepository, revision updates
332 Sticky subrepository, revision updates
333 $ hg id -n
333 $ hg id -n
334 2
334 2
335 $ cd s
335 $ cd s
336 $ svnversion
336 $ svnversion
337 3
337 3
338 $ cd ..
338 $ cd ..
339 $ cd s
339 $ cd s
340 $ svn update -qr 1
340 $ svn update -qr 1
341 $ cd ..
341 $ cd ..
342 $ hg update 1
342 $ hg update 1
343 subrepository s diverged (local revision: 3, remote revision: 2)
343 subrepository s diverged (local revision: 3, remote revision: 2)
344 (M)erge, keep (l)ocal or keep (r)emote? m
344 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
345 subrepository sources for s differ (in checked out version)
345 subrepository sources for s differ (in checked out version)
346 use (l)ocal source (1) or (r)emote source (2)? l
346 use (l)ocal source (1) or (r)emote source (2)? l
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 $ hg id -n
348 $ hg id -n
349 1+
349 1+
350 $ cd s
350 $ cd s
351 $ svnversion
351 $ svnversion
352 1
352 1
353 $ cd ..
353 $ cd ..
354
354
355 Sticky subrepository, file changes and revision updates
355 Sticky subrepository, file changes and revision updates
356 $ touch s/f1
356 $ touch s/f1
357 $ cd s
357 $ cd s
358 $ svn add f1
358 $ svn add f1
359 A f1
359 A f1
360 $ svnversion
360 $ svnversion
361 1M
361 1M
362 $ cd ..
362 $ cd ..
363 $ hg id -n
363 $ hg id -n
364 1+
364 1+
365 $ hg update tip
365 $ hg update tip
366 subrepository s diverged (local revision: 3, remote revision: 3)
366 subrepository s diverged (local revision: 3, remote revision: 3)
367 (M)erge, keep (l)ocal or keep (r)emote? m
367 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
368 subrepository sources for s differ
368 subrepository sources for s differ
369 use (l)ocal source (1) or (r)emote source (3)? l
369 use (l)ocal source (1) or (r)emote source (3)? l
370 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 $ hg id -n
371 $ hg id -n
372 2+
372 2+
373 $ cd s
373 $ cd s
374 $ svnversion
374 $ svnversion
375 1M
375 1M
376 $ cd ..
376 $ cd ..
377
377
378 Sticky repository, update --clean
378 Sticky repository, update --clean
379 $ hg update --clean tip | grep -v 's[/\]externals[/\]other'
379 $ hg update --clean tip | grep -v 's[/\]externals[/\]other'
380 U *s/alpha (glob)
380 U *s/alpha (glob)
381 U *s (glob)
381 U *s (glob)
382
382
383 Fetching external item into '*s/externals'* (glob)
383 Fetching external item into '*s/externals'* (glob)
384 Checked out external at revision 1.
384 Checked out external at revision 1.
385
385
386 Checked out revision 3.
386 Checked out revision 3.
387 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
388 $ hg id -n
388 $ hg id -n
389 2
389 2
390 $ cd s
390 $ cd s
391 $ svnversion
391 $ svnversion
392 3
392 3
393 $ cd ..
393 $ cd ..
394
394
395 Test subrepo already at intended revision:
395 Test subrepo already at intended revision:
396 $ cd s
396 $ cd s
397 $ svn update -qr 2
397 $ svn update -qr 2
398 $ cd ..
398 $ cd ..
399 $ hg update 1
399 $ hg update 1
400 subrepository s diverged (local revision: 3, remote revision: 2)
400 subrepository s diverged (local revision: 3, remote revision: 2)
401 (M)erge, keep (l)ocal or keep (r)emote? m
401 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 $ hg id -n
403 $ hg id -n
404 1+
404 1+
405 $ cd s
405 $ cd s
406 $ svnversion
406 $ svnversion
407 2
407 2
408 $ cd ..
408 $ cd ..
409
409
410 Test case where subversion would fail to update the subrepo because there
410 Test case where subversion would fail to update the subrepo because there
411 are unknown directories being replaced by tracked ones (happens with rebase).
411 are unknown directories being replaced by tracked ones (happens with rebase).
412
412
413 $ cd "$WCROOT/src"
413 $ cd "$WCROOT/src"
414 $ mkdir dir
414 $ mkdir dir
415 $ echo epsilon.py > dir/epsilon.py
415 $ echo epsilon.py > dir/epsilon.py
416 $ svn add dir
416 $ svn add dir
417 A dir
417 A dir
418 A dir/epsilon.py (glob)
418 A dir/epsilon.py (glob)
419 $ svn ci -qm 'Add dir/epsilon.py'
419 $ svn ci -qm 'Add dir/epsilon.py'
420 $ cd ../..
420 $ cd ../..
421 $ hg init rebaserepo
421 $ hg init rebaserepo
422 $ cd rebaserepo
422 $ cd rebaserepo
423 $ svn co -r5 --quiet "$SVNREPOURL"/src s
423 $ svn co -r5 --quiet "$SVNREPOURL"/src s
424 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
424 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
425 $ hg add .hgsub
425 $ hg add .hgsub
426 $ hg ci -m addsub
426 $ hg ci -m addsub
427 $ echo a > a
427 $ echo a > a
428 $ hg add .
428 $ hg add .
429 adding a
429 adding a
430 $ hg ci -m adda
430 $ hg ci -m adda
431 $ hg up 0
431 $ hg up 0
432 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
432 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
433 $ svn up -qr6 s
433 $ svn up -qr6 s
434 $ hg ci -m updatesub
434 $ hg ci -m updatesub
435 created new head
435 created new head
436 $ echo pyc > s/dir/epsilon.pyc
436 $ echo pyc > s/dir/epsilon.pyc
437 $ hg up 1
437 $ hg up 1
438 D *s/dir (glob)
438 D *s/dir (glob)
439
439
440 Fetching external item into '*s/externals'* (glob)
440 Fetching external item into '*s/externals'* (glob)
441 Checked out external at revision 1.
441 Checked out external at revision 1.
442
442
443 Checked out revision 5.
443 Checked out revision 5.
444 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
444 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
445 $ hg up -q 2
445 $ hg up -q 2
446
446
447 Modify one of the externals to point to a different path so we can
447 Modify one of the externals to point to a different path so we can
448 test having obstructions when switching branches on checkout:
448 test having obstructions when switching branches on checkout:
449 $ hg checkout tip
449 $ hg checkout tip
450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 $ echo "obstruct = [svn] $SVNREPOURL/externals" >> .hgsub
451 $ echo "obstruct = [svn] $SVNREPOURL/externals" >> .hgsub
452 $ svn co -r5 --quiet "$SVNREPOURL"/externals obstruct
452 $ svn co -r5 --quiet "$SVNREPOURL"/externals obstruct
453 $ hg commit -m 'Start making obstructed working copy'
453 $ hg commit -m 'Start making obstructed working copy'
454 $ hg book other
454 $ hg book other
455 $ hg co -r 'p1(tip)'
455 $ hg co -r 'p1(tip)'
456 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
457 (leaving bookmark other)
457 (leaving bookmark other)
458 $ echo "obstruct = [svn] $SVNREPOURL/src" >> .hgsub
458 $ echo "obstruct = [svn] $SVNREPOURL/src" >> .hgsub
459 $ svn co -r5 --quiet "$SVNREPOURL"/src obstruct
459 $ svn co -r5 --quiet "$SVNREPOURL"/src obstruct
460 $ hg commit -m 'Other branch which will be obstructed'
460 $ hg commit -m 'Other branch which will be obstructed'
461 created new head
461 created new head
462
462
463 Switching back to the head where we have another path mapped to the
463 Switching back to the head where we have another path mapped to the
464 same subrepo should work if the subrepo is clean.
464 same subrepo should work if the subrepo is clean.
465 $ hg co other
465 $ hg co other
466 A *obstruct/other (glob)
466 A *obstruct/other (glob)
467 Checked out revision 1.
467 Checked out revision 1.
468 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 (activating bookmark other)
469 (activating bookmark other)
470
470
471 This is surprising, but is also correct based on the current code:
471 This is surprising, but is also correct based on the current code:
472 $ echo "updating should (maybe) fail" > obstruct/other
472 $ echo "updating should (maybe) fail" > obstruct/other
473 $ hg co tip
473 $ hg co tip
474 abort: uncommitted changes
474 abort: uncommitted changes
475 (commit or update --clean to discard changes)
475 (commit or update --clean to discard changes)
476 [255]
476 [255]
477
477
478 Point to a Subversion branch which has since been deleted and recreated
478 Point to a Subversion branch which has since been deleted and recreated
479 First, create that condition in the repository.
479 First, create that condition in the repository.
480
480
481 $ hg ci --subrepos -m cleanup | filter_svn_output
481 $ hg ci --subrepos -m cleanup | filter_svn_output
482 committing subrepository obstruct
482 committing subrepository obstruct
483 Sending obstruct/other (glob)
483 Sending obstruct/other (glob)
484 Committed revision 7.
484 Committed revision 7.
485 At revision 7.
485 At revision 7.
486 $ svn mkdir -qm "baseline" $SVNREPOURL/trunk
486 $ svn mkdir -qm "baseline" $SVNREPOURL/trunk
487 $ svn copy -qm "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch
487 $ svn copy -qm "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch
488 $ svn co --quiet "$SVNREPOURL"/branch tempwc
488 $ svn co --quiet "$SVNREPOURL"/branch tempwc
489 $ cd tempwc
489 $ cd tempwc
490 $ echo "something old" > somethingold
490 $ echo "something old" > somethingold
491 $ svn add somethingold
491 $ svn add somethingold
492 A somethingold
492 A somethingold
493 $ svn ci -qm 'Something old'
493 $ svn ci -qm 'Something old'
494 $ svn rm -qm "remove branch" $SVNREPOURL/branch
494 $ svn rm -qm "remove branch" $SVNREPOURL/branch
495 $ svn copy -qm "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch
495 $ svn copy -qm "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch
496 $ svn up -q
496 $ svn up -q
497 $ echo "something new" > somethingnew
497 $ echo "something new" > somethingnew
498 $ svn add somethingnew
498 $ svn add somethingnew
499 A somethingnew
499 A somethingnew
500 $ svn ci -qm 'Something new'
500 $ svn ci -qm 'Something new'
501 $ cd ..
501 $ cd ..
502 $ rm -rf tempwc
502 $ rm -rf tempwc
503 $ svn co "$SVNREPOURL/branch"@10 recreated
503 $ svn co "$SVNREPOURL/branch"@10 recreated
504 A recreated/somethingold (glob)
504 A recreated/somethingold (glob)
505 Checked out revision 10.
505 Checked out revision 10.
506 $ echo "recreated = [svn] $SVNREPOURL/branch" >> .hgsub
506 $ echo "recreated = [svn] $SVNREPOURL/branch" >> .hgsub
507 $ hg ci -m addsub
507 $ hg ci -m addsub
508 $ cd recreated
508 $ cd recreated
509 $ svn up -q
509 $ svn up -q
510 $ cd ..
510 $ cd ..
511 $ hg ci -m updatesub
511 $ hg ci -m updatesub
512 $ hg up -r-2
512 $ hg up -r-2
513 D *recreated/somethingnew (glob)
513 D *recreated/somethingnew (glob)
514 A *recreated/somethingold (glob)
514 A *recreated/somethingold (glob)
515 Checked out revision 10.
515 Checked out revision 10.
516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 (leaving bookmark other)
517 (leaving bookmark other)
518 $ test -f recreated/somethingold
518 $ test -f recreated/somethingold
519
519
520 Test archive
520 Test archive
521
521
522 $ hg archive -S ../archive-all --debug --config progress.debug=true
522 $ hg archive -S ../archive-all --debug --config progress.debug=true
523 archiving: 0/2 files (0.00%)
523 archiving: 0/2 files (0.00%)
524 archiving: .hgsub 1/2 files (50.00%)
524 archiving: .hgsub 1/2 files (50.00%)
525 archiving: .hgsubstate 2/2 files (100.00%)
525 archiving: .hgsubstate 2/2 files (100.00%)
526 archiving (obstruct): 0/1 files (0.00%)
526 archiving (obstruct): 0/1 files (0.00%)
527 archiving (obstruct): 1/1 files (100.00%)
527 archiving (obstruct): 1/1 files (100.00%)
528 archiving (recreated): 0/1 files (0.00%)
528 archiving (recreated): 0/1 files (0.00%)
529 archiving (recreated): 1/1 files (100.00%)
529 archiving (recreated): 1/1 files (100.00%)
530 archiving (s): 0/2 files (0.00%)
530 archiving (s): 0/2 files (0.00%)
531 archiving (s): 1/2 files (50.00%)
531 archiving (s): 1/2 files (50.00%)
532 archiving (s): 2/2 files (100.00%)
532 archiving (s): 2/2 files (100.00%)
533
533
534 $ hg archive -S ../archive-exclude --debug --config progress.debug=true -X **old
534 $ hg archive -S ../archive-exclude --debug --config progress.debug=true -X **old
535 archiving: 0/2 files (0.00%)
535 archiving: 0/2 files (0.00%)
536 archiving: .hgsub 1/2 files (50.00%)
536 archiving: .hgsub 1/2 files (50.00%)
537 archiving: .hgsubstate 2/2 files (100.00%)
537 archiving: .hgsubstate 2/2 files (100.00%)
538 archiving (obstruct): 0/1 files (0.00%)
538 archiving (obstruct): 0/1 files (0.00%)
539 archiving (obstruct): 1/1 files (100.00%)
539 archiving (obstruct): 1/1 files (100.00%)
540 archiving (recreated): 0 files
540 archiving (recreated): 0 files
541 archiving (s): 0/2 files (0.00%)
541 archiving (s): 0/2 files (0.00%)
542 archiving (s): 1/2 files (50.00%)
542 archiving (s): 1/2 files (50.00%)
543 archiving (s): 2/2 files (100.00%)
543 archiving (s): 2/2 files (100.00%)
544 $ find ../archive-exclude | sort
544 $ find ../archive-exclude | sort
545 ../archive-exclude
545 ../archive-exclude
546 ../archive-exclude/.hg_archival.txt
546 ../archive-exclude/.hg_archival.txt
547 ../archive-exclude/.hgsub
547 ../archive-exclude/.hgsub
548 ../archive-exclude/.hgsubstate
548 ../archive-exclude/.hgsubstate
549 ../archive-exclude/obstruct
549 ../archive-exclude/obstruct
550 ../archive-exclude/obstruct/other
550 ../archive-exclude/obstruct/other
551 ../archive-exclude/s
551 ../archive-exclude/s
552 ../archive-exclude/s/alpha
552 ../archive-exclude/s/alpha
553 ../archive-exclude/s/dir
553 ../archive-exclude/s/dir
554 ../archive-exclude/s/dir/epsilon.py
554 ../archive-exclude/s/dir/epsilon.py
555
555
556 Test forgetting files, not implemented in svn subrepo, used to
556 Test forgetting files, not implemented in svn subrepo, used to
557 traceback
557 traceback
558
558
559 #if no-windows
559 #if no-windows
560 $ hg forget 'notafile*'
560 $ hg forget 'notafile*'
561 notafile*: No such file or directory
561 notafile*: No such file or directory
562 [1]
562 [1]
563 #else
563 #else
564 $ hg forget 'notafile'
564 $ hg forget 'notafile'
565 notafile: * (glob)
565 notafile: * (glob)
566 [1]
566 [1]
567 #endif
567 #endif
568
568
569 Test a subrepo referencing a just moved svn path. Last commit rev will
569 Test a subrepo referencing a just moved svn path. Last commit rev will
570 be different from the revision, and the path will be different as
570 be different from the revision, and the path will be different as
571 well.
571 well.
572
572
573 $ cd "$WCROOT"
573 $ cd "$WCROOT"
574 $ svn up > /dev/null
574 $ svn up > /dev/null
575 $ mkdir trunk/subdir branches
575 $ mkdir trunk/subdir branches
576 $ echo a > trunk/subdir/a
576 $ echo a > trunk/subdir/a
577 $ svn add trunk/subdir branches
577 $ svn add trunk/subdir branches
578 A trunk/subdir (glob)
578 A trunk/subdir (glob)
579 A trunk/subdir/a (glob)
579 A trunk/subdir/a (glob)
580 A branches
580 A branches
581 $ svn ci -qm addsubdir
581 $ svn ci -qm addsubdir
582 $ svn cp -qm branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch
582 $ svn cp -qm branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch
583 $ cd ..
583 $ cd ..
584
584
585 $ hg init repo2
585 $ hg init repo2
586 $ cd repo2
586 $ cd repo2
587 $ svn co $SVNREPOURL/branches/somebranch/subdir
587 $ svn co $SVNREPOURL/branches/somebranch/subdir
588 A subdir/a (glob)
588 A subdir/a (glob)
589 Checked out revision 15.
589 Checked out revision 15.
590 $ echo "subdir = [svn] $SVNREPOURL/branches/somebranch/subdir" > .hgsub
590 $ echo "subdir = [svn] $SVNREPOURL/branches/somebranch/subdir" > .hgsub
591 $ hg add .hgsub
591 $ hg add .hgsub
592 $ hg ci -m addsub
592 $ hg ci -m addsub
593 $ hg up null
593 $ hg up null
594 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
594 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
595 $ hg up
595 $ hg up
596 A *subdir/a (glob)
596 A *subdir/a (glob)
597 Checked out revision 15.
597 Checked out revision 15.
598 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
598 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
599 $ cd ..
599 $ cd ..
600
600
601 Test sanitizing ".hg/hgrc" in subrepo
601 Test sanitizing ".hg/hgrc" in subrepo
602
602
603 $ cd sub/t
603 $ cd sub/t
604 $ hg update -q -C tip
604 $ hg update -q -C tip
605 $ cd s
605 $ cd s
606 $ mkdir .hg
606 $ mkdir .hg
607 $ echo '.hg/hgrc in svn repo' > .hg/hgrc
607 $ echo '.hg/hgrc in svn repo' > .hg/hgrc
608 $ mkdir -p sub/.hg
608 $ mkdir -p sub/.hg
609 $ echo 'sub/.hg/hgrc in svn repo' > sub/.hg/hgrc
609 $ echo 'sub/.hg/hgrc in svn repo' > sub/.hg/hgrc
610 $ svn add .hg sub
610 $ svn add .hg sub
611 A .hg
611 A .hg
612 A .hg/hgrc (glob)
612 A .hg/hgrc (glob)
613 A sub
613 A sub
614 A sub/.hg (glob)
614 A sub/.hg (glob)
615 A sub/.hg/hgrc (glob)
615 A sub/.hg/hgrc (glob)
616 $ svn ci -qm 'add .hg/hgrc to be sanitized at hg update'
616 $ svn ci -qm 'add .hg/hgrc to be sanitized at hg update'
617 $ svn up -q
617 $ svn up -q
618 $ cd ..
618 $ cd ..
619 $ hg commit -S -m 'commit with svn revision including .hg/hgrc'
619 $ hg commit -S -m 'commit with svn revision including .hg/hgrc'
620 $ grep ' s$' .hgsubstate
620 $ grep ' s$' .hgsubstate
621 16 s
621 16 s
622 $ cd ..
622 $ cd ..
623
623
624 $ hg -R tc pull -u -q 2>&1 | sort
624 $ hg -R tc pull -u -q 2>&1 | sort
625 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/.hg' (glob)
625 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/.hg' (glob)
626 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/sub/.hg' (glob)
626 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/sub/.hg' (glob)
627 $ cd tc
627 $ cd tc
628 $ grep ' s$' .hgsubstate
628 $ grep ' s$' .hgsubstate
629 16 s
629 16 s
630 $ test -f s/.hg/hgrc
630 $ test -f s/.hg/hgrc
631 [1]
631 [1]
632 $ test -f s/sub/.hg/hgrc
632 $ test -f s/sub/.hg/hgrc
633 [1]
633 [1]
634
634
635 Test that sanitizing is omitted in meta data area:
635 Test that sanitizing is omitted in meta data area:
636
636
637 $ mkdir s/.svn/.hg
637 $ mkdir s/.svn/.hg
638 $ echo '.hg/hgrc in svn metadata area' > s/.svn/.hg/hgrc
638 $ echo '.hg/hgrc in svn metadata area' > s/.svn/.hg/hgrc
639 $ hg update -q -C '.^1'
639 $ hg update -q -C '.^1'
640
640
641 $ cd ../..
641 $ cd ../..
@@ -1,1778 +1,1778 b''
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 (glob)
32 s/a (glob)
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 (glob)
73 reverting s/a (glob)
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 (glob)
78 reverting s/a (glob)
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 (glob)
134 committing subrepository s/ss (glob)
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 (glob)
208 adding t/t (glob)
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 (M)erge, keep (l)ocal or keep (r)emote? m
307 (M)erge, keep (l)ocal or keep (r)emote? m
307 merging subrepo t
308 merging subrepo t
308 searching for copies back to rev 2
309 searching for copies back to rev 2
309 resolving manifests
310 resolving manifests
310 branchmerge: True, force: False, partial: False
311 branchmerge: True, force: False, partial: False
311 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
312 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
312 preserving t for resolve of t
313 preserving t for resolve of t
313 starting 4 threads for background file closing (?)
314 t: versions differ -> m (premerge)
314 t: versions differ -> m (premerge)
315 picked tool ':merge' for t (binary False symlink False changedelete False)
315 picked tool ':merge' for t (binary False symlink False changedelete False)
316 merging t
316 merging t
317 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
317 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
318 t: versions differ -> m (merge)
318 t: versions differ -> m (merge)
319 picked tool ':merge' for t (binary False symlink False changedelete False)
319 picked tool ':merge' for t (binary False symlink False changedelete False)
320 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
320 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
321 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
321 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
322 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
322 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
323 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
323 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
324 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
324 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
325 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
325 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
326 (branch merge, don't forget to commit)
326 (branch merge, don't forget to commit)
327
327
328 should conflict
328 should conflict
329
329
330 $ cat t/t
330 $ cat t/t
331 <<<<<<< local: 20a0db6fbf6c - test: 10
331 <<<<<<< local: 20a0db6fbf6c - test: 10
332 conflict
332 conflict
333 =======
333 =======
334 t3
334 t3
335 >>>>>>> other: 7af322bc1198 - test: 7
335 >>>>>>> other: 7af322bc1198 - test: 7
336
336
337 11: remove subrepo t
337 11: remove subrepo t
338
338
339 $ hg co -C 5
339 $ hg co -C 5
340 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 $ hg revert -r 4 .hgsub # remove t
341 $ hg revert -r 4 .hgsub # remove t
342 $ hg ci -m11
342 $ hg ci -m11
343 created new head
343 created new head
344 $ hg debugsub
344 $ hg debugsub
345 path s
345 path s
346 source s
346 source s
347 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
347 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
348
348
349 local removed, remote changed, keep changed
349 local removed, remote changed, keep changed
350
350
351 $ hg merge 6
351 $ hg merge 6
352 remote changed subrepository t which local removed
352 remote changed subrepository s which local removed
353 use (c)hanged version or (d)elete? c
353 use (c)hanged version or (d)elete? c
354 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 (branch merge, don't forget to commit)
355 (branch merge, don't forget to commit)
356 BROKEN: should include subrepo t
356 BROKEN: should include subrepo t
357 $ hg debugsub
357 $ hg debugsub
358 path s
358 path s
359 source s
359 source s
360 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
360 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
361 $ cat .hgsubstate
361 $ cat .hgsubstate
362 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
362 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
363 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
363 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
364 $ hg ci -m 'local removed, remote changed, keep changed'
364 $ hg ci -m 'local removed, remote changed, keep changed'
365 BROKEN: should include subrepo t
365 BROKEN: should include subrepo t
366 $ hg debugsub
366 $ hg debugsub
367 path s
367 path s
368 source s
368 source s
369 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
369 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
370 BROKEN: should include subrepo t
370 BROKEN: should include subrepo t
371 $ cat .hgsubstate
371 $ cat .hgsubstate
372 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
372 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
373 $ cat t/t
373 $ cat t/t
374 t2
374 t2
375
375
376 local removed, remote changed, keep removed
376 local removed, remote changed, keep removed
377
377
378 $ hg co -C 11
378 $ hg co -C 11
379 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
380 $ hg merge --config ui.interactive=true 6 <<EOF
380 $ hg merge --config ui.interactive=true 6 <<EOF
381 > d
381 > d
382 > EOF
382 > EOF
383 remote changed subrepository t which local removed
383 remote changed subrepository s which local removed
384 use (c)hanged version or (d)elete? d
384 use (c)hanged version or (d)elete? d
385 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 (branch merge, don't forget to commit)
386 (branch merge, don't forget to commit)
387 $ hg debugsub
387 $ hg debugsub
388 path s
388 path s
389 source s
389 source s
390 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
390 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
391 $ cat .hgsubstate
391 $ cat .hgsubstate
392 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
392 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
393 $ hg ci -m 'local removed, remote changed, keep removed'
393 $ hg ci -m 'local removed, remote changed, keep removed'
394 created new head
394 created new head
395 $ hg debugsub
395 $ hg debugsub
396 path s
396 path s
397 source s
397 source s
398 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
398 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
399 $ cat .hgsubstate
399 $ cat .hgsubstate
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
401
401
402 local changed, remote removed, keep changed
402 local changed, remote removed, keep changed
403
403
404 $ hg co -C 6
404 $ hg co -C 6
405 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 $ hg merge 11
406 $ hg merge 11
407 local changed subrepository t which remote removed
407 local changed subrepository s which remote removed
408 use (c)hanged version or (d)elete? c
408 use (c)hanged version or (d)elete? c
409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 (branch merge, don't forget to commit)
410 (branch merge, don't forget to commit)
411 BROKEN: should include subrepo t
411 BROKEN: should include subrepo t
412 $ hg debugsub
412 $ hg debugsub
413 path s
413 path s
414 source s
414 source s
415 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
415 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
416 BROKEN: should include subrepo t
416 BROKEN: should include subrepo t
417 $ cat .hgsubstate
417 $ cat .hgsubstate
418 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
418 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
419 $ hg ci -m 'local changed, remote removed, keep changed'
419 $ hg ci -m 'local changed, remote removed, keep changed'
420 created new head
420 created new head
421 BROKEN: should include subrepo t
421 BROKEN: should include subrepo t
422 $ hg debugsub
422 $ hg debugsub
423 path s
423 path s
424 source s
424 source s
425 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
425 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
426 BROKEN: should include subrepo t
426 BROKEN: should include subrepo t
427 $ cat .hgsubstate
427 $ cat .hgsubstate
428 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
428 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
429 $ cat t/t
429 $ cat t/t
430 t2
430 t2
431
431
432 local changed, remote removed, keep removed
432 local changed, remote removed, keep removed
433
433
434 $ hg co -C 6
434 $ hg co -C 6
435 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ hg merge --config ui.interactive=true 11 <<EOF
436 $ hg merge --config ui.interactive=true 11 <<EOF
437 > d
437 > d
438 > EOF
438 > EOF
439 local changed subrepository t which remote removed
439 local changed subrepository s which remote removed
440 use (c)hanged version or (d)elete? d
440 use (c)hanged version or (d)elete? d
441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 (branch merge, don't forget to commit)
442 (branch merge, don't forget to commit)
443 $ hg debugsub
443 $ hg debugsub
444 path s
444 path s
445 source s
445 source s
446 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
446 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
447 $ cat .hgsubstate
447 $ cat .hgsubstate
448 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
448 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
449 $ hg ci -m 'local changed, remote removed, keep removed'
449 $ hg ci -m 'local changed, remote removed, keep removed'
450 created new head
450 created new head
451 $ hg debugsub
451 $ hg debugsub
452 path s
452 path s
453 source s
453 source s
454 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
454 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
455 $ cat .hgsubstate
455 $ cat .hgsubstate
456 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
456 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
457
457
458 clean up to avoid having to fix up the tests below
458 clean up to avoid having to fix up the tests below
459
459
460 $ hg co -C 10
460 $ hg co -C 10
461 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
461 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 $ cat >> $HGRCPATH <<EOF
462 $ cat >> $HGRCPATH <<EOF
463 > [extensions]
463 > [extensions]
464 > strip=
464 > strip=
465 > EOF
465 > EOF
466 $ hg strip -r 11:15
466 $ hg strip -r 11:15
467 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
467 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
468
468
469 clone
469 clone
470
470
471 $ cd ..
471 $ cd ..
472 $ hg clone t tc
472 $ hg clone t tc
473 updating to branch default
473 updating to branch default
474 cloning subrepo s from $TESTTMP/t/s
474 cloning subrepo s from $TESTTMP/t/s
475 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
475 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
476 cloning subrepo t from $TESTTMP/t/t
476 cloning subrepo t from $TESTTMP/t/t
477 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
477 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 $ cd tc
478 $ cd tc
479 $ hg debugsub
479 $ hg debugsub
480 path s
480 path s
481 source s
481 source s
482 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
482 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
483 path t
483 path t
484 source t
484 source t
485 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
485 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
486
486
487 push
487 push
488
488
489 $ echo bah > t/t
489 $ echo bah > t/t
490 $ hg ci -m11
490 $ hg ci -m11
491 committing subrepository t
491 committing subrepository t
492 $ hg push
492 $ hg push
493 pushing to $TESTTMP/t (glob)
493 pushing to $TESTTMP/t (glob)
494 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
494 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
495 no changes made to subrepo s since last push to $TESTTMP/t/s
495 no changes made to subrepo s since last push to $TESTTMP/t/s
496 pushing subrepo t to $TESTTMP/t/t
496 pushing subrepo t to $TESTTMP/t/t
497 searching for changes
497 searching for changes
498 adding changesets
498 adding changesets
499 adding manifests
499 adding manifests
500 adding file changes
500 adding file changes
501 added 1 changesets with 1 changes to 1 files
501 added 1 changesets with 1 changes to 1 files
502 searching for changes
502 searching for changes
503 adding changesets
503 adding changesets
504 adding manifests
504 adding manifests
505 adding file changes
505 adding file changes
506 added 1 changesets with 1 changes to 1 files
506 added 1 changesets with 1 changes to 1 files
507
507
508 push -f
508 push -f
509
509
510 $ echo bah > s/a
510 $ echo bah > s/a
511 $ hg ci -m12
511 $ hg ci -m12
512 committing subrepository s
512 committing subrepository s
513 $ hg push
513 $ hg push
514 pushing to $TESTTMP/t (glob)
514 pushing to $TESTTMP/t (glob)
515 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
515 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
516 pushing subrepo s to $TESTTMP/t/s
516 pushing subrepo s to $TESTTMP/t/s
517 searching for changes
517 searching for changes
518 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
518 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
519 (merge or see 'hg help push' for details about pushing new heads)
519 (merge or see 'hg help push' for details about pushing new heads)
520 [255]
520 [255]
521 $ hg push -f
521 $ hg push -f
522 pushing to $TESTTMP/t (glob)
522 pushing to $TESTTMP/t (glob)
523 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
523 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
524 searching for changes
524 searching for changes
525 no changes found
525 no changes found
526 pushing subrepo s to $TESTTMP/t/s
526 pushing subrepo s to $TESTTMP/t/s
527 searching for changes
527 searching for changes
528 adding changesets
528 adding changesets
529 adding manifests
529 adding manifests
530 adding file changes
530 adding file changes
531 added 1 changesets with 1 changes to 1 files (+1 heads)
531 added 1 changesets with 1 changes to 1 files (+1 heads)
532 pushing subrepo t to $TESTTMP/t/t
532 pushing subrepo t to $TESTTMP/t/t
533 searching for changes
533 searching for changes
534 no changes found
534 no changes found
535 searching for changes
535 searching for changes
536 adding changesets
536 adding changesets
537 adding manifests
537 adding manifests
538 adding file changes
538 adding file changes
539 added 1 changesets with 1 changes to 1 files
539 added 1 changesets with 1 changes to 1 files
540
540
541 check that unmodified subrepos are not pushed
541 check that unmodified subrepos are not pushed
542
542
543 $ hg clone . ../tcc
543 $ hg clone . ../tcc
544 updating to branch default
544 updating to branch default
545 cloning subrepo s from $TESTTMP/tc/s
545 cloning subrepo s from $TESTTMP/tc/s
546 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
546 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
547 cloning subrepo t from $TESTTMP/tc/t
547 cloning subrepo t from $TESTTMP/tc/t
548 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
548 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
549
549
550 the subrepos on the new clone have nothing to push to its source
550 the subrepos on the new clone have nothing to push to its source
551
551
552 $ hg push -R ../tcc .
552 $ hg push -R ../tcc .
553 pushing to .
553 pushing to .
554 no changes made to subrepo s/ss since last push to s/ss (glob)
554 no changes made to subrepo s/ss since last push to s/ss (glob)
555 no changes made to subrepo s since last push to s
555 no changes made to subrepo s since last push to s
556 no changes made to subrepo t since last push to t
556 no changes made to subrepo t since last push to t
557 searching for changes
557 searching for changes
558 no changes found
558 no changes found
559 [1]
559 [1]
560
560
561 the subrepos on the source do not have a clean store versus the clone target
561 the subrepos on the source do not have a clean store versus the clone target
562 because they were never explicitly pushed to the source
562 because they were never explicitly pushed to the source
563
563
564 $ hg push ../tcc
564 $ hg push ../tcc
565 pushing to ../tcc
565 pushing to ../tcc
566 pushing subrepo s/ss to ../tcc/s/ss (glob)
566 pushing subrepo s/ss to ../tcc/s/ss (glob)
567 searching for changes
567 searching for changes
568 no changes found
568 no changes found
569 pushing subrepo s to ../tcc/s
569 pushing subrepo s to ../tcc/s
570 searching for changes
570 searching for changes
571 no changes found
571 no changes found
572 pushing subrepo t to ../tcc/t
572 pushing subrepo t to ../tcc/t
573 searching for changes
573 searching for changes
574 no changes found
574 no changes found
575 searching for changes
575 searching for changes
576 no changes found
576 no changes found
577 [1]
577 [1]
578
578
579 after push their stores become clean
579 after push their stores become clean
580
580
581 $ hg push ../tcc
581 $ hg push ../tcc
582 pushing to ../tcc
582 pushing to ../tcc
583 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
583 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
584 no changes made to subrepo s since last push to ../tcc/s
584 no changes made to subrepo s since last push to ../tcc/s
585 no changes made to subrepo t since last push to ../tcc/t
585 no changes made to subrepo t since last push to ../tcc/t
586 searching for changes
586 searching for changes
587 no changes found
587 no changes found
588 [1]
588 [1]
589
589
590 updating a subrepo to a different revision or changing
590 updating a subrepo to a different revision or changing
591 its working directory does not make its store dirty
591 its working directory does not make its store dirty
592
592
593 $ hg -R s update '.^'
593 $ hg -R s update '.^'
594 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
594 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 $ hg push
595 $ hg push
596 pushing to $TESTTMP/t (glob)
596 pushing to $TESTTMP/t (glob)
597 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
597 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
598 no changes made to subrepo s since last push to $TESTTMP/t/s
598 no changes made to subrepo s since last push to $TESTTMP/t/s
599 no changes made to subrepo t since last push to $TESTTMP/t/t
599 no changes made to subrepo t since last push to $TESTTMP/t/t
600 searching for changes
600 searching for changes
601 no changes found
601 no changes found
602 [1]
602 [1]
603 $ echo foo >> s/a
603 $ echo foo >> s/a
604 $ hg push
604 $ hg push
605 pushing to $TESTTMP/t (glob)
605 pushing to $TESTTMP/t (glob)
606 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
606 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
607 no changes made to subrepo s since last push to $TESTTMP/t/s
607 no changes made to subrepo s since last push to $TESTTMP/t/s
608 no changes made to subrepo t since last push to $TESTTMP/t/t
608 no changes made to subrepo t since last push to $TESTTMP/t/t
609 searching for changes
609 searching for changes
610 no changes found
610 no changes found
611 [1]
611 [1]
612 $ hg -R s update -C tip
612 $ hg -R s update -C tip
613 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
613 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
614
614
615 committing into a subrepo makes its store (but not its parent's store) dirty
615 committing into a subrepo makes its store (but not its parent's store) dirty
616
616
617 $ echo foo >> s/ss/a
617 $ echo foo >> s/ss/a
618 $ hg -R s/ss commit -m 'test dirty store detection'
618 $ hg -R s/ss commit -m 'test dirty store detection'
619
619
620 $ hg out -S -r `hg log -r tip -T "{node|short}"`
620 $ hg out -S -r `hg log -r tip -T "{node|short}"`
621 comparing with $TESTTMP/t (glob)
621 comparing with $TESTTMP/t (glob)
622 searching for changes
622 searching for changes
623 no changes found
623 no changes found
624 comparing with $TESTTMP/t/s
624 comparing with $TESTTMP/t/s
625 searching for changes
625 searching for changes
626 no changes found
626 no changes found
627 comparing with $TESTTMP/t/s/ss
627 comparing with $TESTTMP/t/s/ss
628 searching for changes
628 searching for changes
629 changeset: 1:79ea5566a333
629 changeset: 1:79ea5566a333
630 tag: tip
630 tag: tip
631 user: test
631 user: test
632 date: Thu Jan 01 00:00:00 1970 +0000
632 date: Thu Jan 01 00:00:00 1970 +0000
633 summary: test dirty store detection
633 summary: test dirty store detection
634
634
635 comparing with $TESTTMP/t/t
635 comparing with $TESTTMP/t/t
636 searching for changes
636 searching for changes
637 no changes found
637 no changes found
638
638
639 $ hg push
639 $ hg push
640 pushing to $TESTTMP/t (glob)
640 pushing to $TESTTMP/t (glob)
641 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
641 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
642 searching for changes
642 searching for changes
643 adding changesets
643 adding changesets
644 adding manifests
644 adding manifests
645 adding file changes
645 adding file changes
646 added 1 changesets with 1 changes to 1 files
646 added 1 changesets with 1 changes to 1 files
647 no changes made to subrepo s since last push to $TESTTMP/t/s
647 no changes made to subrepo s since last push to $TESTTMP/t/s
648 no changes made to subrepo t since last push to $TESTTMP/t/t
648 no changes made to subrepo t since last push to $TESTTMP/t/t
649 searching for changes
649 searching for changes
650 no changes found
650 no changes found
651 [1]
651 [1]
652
652
653 a subrepo store may be clean versus one repo but not versus another
653 a subrepo store may be clean versus one repo but not versus another
654
654
655 $ hg push
655 $ hg push
656 pushing to $TESTTMP/t (glob)
656 pushing to $TESTTMP/t (glob)
657 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
657 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
658 no changes made to subrepo s since last push to $TESTTMP/t/s
658 no changes made to subrepo s since last push to $TESTTMP/t/s
659 no changes made to subrepo t since last push to $TESTTMP/t/t
659 no changes made to subrepo t since last push to $TESTTMP/t/t
660 searching for changes
660 searching for changes
661 no changes found
661 no changes found
662 [1]
662 [1]
663 $ hg push ../tcc
663 $ hg push ../tcc
664 pushing to ../tcc
664 pushing to ../tcc
665 pushing subrepo s/ss to ../tcc/s/ss (glob)
665 pushing subrepo s/ss to ../tcc/s/ss (glob)
666 searching for changes
666 searching for changes
667 adding changesets
667 adding changesets
668 adding manifests
668 adding manifests
669 adding file changes
669 adding file changes
670 added 1 changesets with 1 changes to 1 files
670 added 1 changesets with 1 changes to 1 files
671 no changes made to subrepo s since last push to ../tcc/s
671 no changes made to subrepo s since last push to ../tcc/s
672 no changes made to subrepo t since last push to ../tcc/t
672 no changes made to subrepo t since last push to ../tcc/t
673 searching for changes
673 searching for changes
674 no changes found
674 no changes found
675 [1]
675 [1]
676
676
677 update
677 update
678
678
679 $ cd ../t
679 $ cd ../t
680 $ hg up -C # discard our earlier merge
680 $ hg up -C # discard our earlier merge
681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
682 2 other heads for branch "default"
682 2 other heads for branch "default"
683 $ echo blah > t/t
683 $ echo blah > t/t
684 $ hg ci -m13
684 $ hg ci -m13
685 committing subrepository t
685 committing subrepository t
686
686
687 backout calls revert internally with minimal opts, which should not raise
687 backout calls revert internally with minimal opts, which should not raise
688 KeyError
688 KeyError
689
689
690 $ hg backout ".^" --no-commit
690 $ hg backout ".^" --no-commit
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
692 changeset c373c8102e68 backed out, don't forget to commit.
692 changeset c373c8102e68 backed out, don't forget to commit.
693
693
694 $ hg up -C # discard changes
694 $ hg up -C # discard changes
695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 2 other heads for branch "default"
696 2 other heads for branch "default"
697
697
698 pull
698 pull
699
699
700 $ cd ../tc
700 $ cd ../tc
701 $ hg pull
701 $ hg pull
702 pulling from $TESTTMP/t (glob)
702 pulling from $TESTTMP/t (glob)
703 searching for changes
703 searching for changes
704 adding changesets
704 adding changesets
705 adding manifests
705 adding manifests
706 adding file changes
706 adding file changes
707 added 1 changesets with 1 changes to 1 files
707 added 1 changesets with 1 changes to 1 files
708 (run 'hg update' to get a working copy)
708 (run 'hg update' to get a working copy)
709
709
710 should pull t
710 should pull t
711
711
712 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
712 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
713 comparing with $TESTTMP/t (glob)
713 comparing with $TESTTMP/t (glob)
714 no changes found
714 no changes found
715 comparing with $TESTTMP/t/s
715 comparing with $TESTTMP/t/s
716 searching for changes
716 searching for changes
717 no changes found
717 no changes found
718 comparing with $TESTTMP/t/s/ss
718 comparing with $TESTTMP/t/s/ss
719 searching for changes
719 searching for changes
720 no changes found
720 no changes found
721 comparing with $TESTTMP/t/t
721 comparing with $TESTTMP/t/t
722 searching for changes
722 searching for changes
723 changeset: 5:52c0adc0515a
723 changeset: 5:52c0adc0515a
724 tag: tip
724 tag: tip
725 user: test
725 user: test
726 date: Thu Jan 01 00:00:00 1970 +0000
726 date: Thu Jan 01 00:00:00 1970 +0000
727 summary: 13
727 summary: 13
728
728
729
729
730 $ hg up
730 $ hg up
731 pulling subrepo t from $TESTTMP/t/t
731 pulling subrepo t from $TESTTMP/t/t
732 searching for changes
732 searching for changes
733 adding changesets
733 adding changesets
734 adding manifests
734 adding manifests
735 adding file changes
735 adding file changes
736 added 1 changesets with 1 changes to 1 files
736 added 1 changesets with 1 changes to 1 files
737 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
737 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
738 2 other heads for branch "default"
738 2 other heads for branch "default"
739 $ cat t/t
739 $ cat t/t
740 blah
740 blah
741
741
742 bogus subrepo path aborts
742 bogus subrepo path aborts
743
743
744 $ echo 'bogus=[boguspath' >> .hgsub
744 $ echo 'bogus=[boguspath' >> .hgsub
745 $ hg ci -m 'bogus subrepo path'
745 $ hg ci -m 'bogus subrepo path'
746 abort: missing ] in subrepo source
746 abort: missing ] in subrepo source
747 [255]
747 [255]
748
748
749 Issue1986: merge aborts when trying to merge a subrepo that
749 Issue1986: merge aborts when trying to merge a subrepo that
750 shouldn't need merging
750 shouldn't need merging
751
751
752 # subrepo layout
752 # subrepo layout
753 #
753 #
754 # o 5 br
754 # o 5 br
755 # /|
755 # /|
756 # o | 4 default
756 # o | 4 default
757 # | |
757 # | |
758 # | o 3 br
758 # | o 3 br
759 # |/|
759 # |/|
760 # o | 2 default
760 # o | 2 default
761 # | |
761 # | |
762 # | o 1 br
762 # | o 1 br
763 # |/
763 # |/
764 # o 0 default
764 # o 0 default
765
765
766 $ cd ..
766 $ cd ..
767 $ rm -rf sub
767 $ rm -rf sub
768 $ hg init main
768 $ hg init main
769 $ cd main
769 $ cd main
770 $ hg init s
770 $ hg init s
771 $ cd s
771 $ cd s
772 $ echo a > a
772 $ echo a > a
773 $ hg ci -Am1
773 $ hg ci -Am1
774 adding a
774 adding a
775 $ hg branch br
775 $ hg branch br
776 marked working directory as branch br
776 marked working directory as branch br
777 (branches are permanent and global, did you want a bookmark?)
777 (branches are permanent and global, did you want a bookmark?)
778 $ echo a >> a
778 $ echo a >> a
779 $ hg ci -m1
779 $ hg ci -m1
780 $ hg up default
780 $ hg up default
781 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
781 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 $ echo b > b
782 $ echo b > b
783 $ hg ci -Am1
783 $ hg ci -Am1
784 adding b
784 adding b
785 $ hg up br
785 $ hg up br
786 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
786 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
787 $ hg merge tip
787 $ hg merge tip
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
789 (branch merge, don't forget to commit)
789 (branch merge, don't forget to commit)
790 $ hg ci -m1
790 $ hg ci -m1
791 $ hg up 2
791 $ hg up 2
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
793 $ echo c > c
793 $ echo c > c
794 $ hg ci -Am1
794 $ hg ci -Am1
795 adding c
795 adding c
796 $ hg up 3
796 $ hg up 3
797 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
797 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
798 $ hg merge 4
798 $ hg merge 4
799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
800 (branch merge, don't forget to commit)
800 (branch merge, don't forget to commit)
801 $ hg ci -m1
801 $ hg ci -m1
802
802
803 # main repo layout:
803 # main repo layout:
804 #
804 #
805 # * <-- try to merge default into br again
805 # * <-- try to merge default into br again
806 # .`|
806 # .`|
807 # . o 5 br --> substate = 5
807 # . o 5 br --> substate = 5
808 # . |
808 # . |
809 # o | 4 default --> substate = 4
809 # o | 4 default --> substate = 4
810 # | |
810 # | |
811 # | o 3 br --> substate = 2
811 # | o 3 br --> substate = 2
812 # |/|
812 # |/|
813 # o | 2 default --> substate = 2
813 # o | 2 default --> substate = 2
814 # | |
814 # | |
815 # | o 1 br --> substate = 3
815 # | o 1 br --> substate = 3
816 # |/
816 # |/
817 # o 0 default --> substate = 2
817 # o 0 default --> substate = 2
818
818
819 $ cd ..
819 $ cd ..
820 $ echo 's = s' > .hgsub
820 $ echo 's = s' > .hgsub
821 $ hg -R s up 2
821 $ hg -R s up 2
822 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
822 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
823 $ hg ci -Am1
823 $ hg ci -Am1
824 adding .hgsub
824 adding .hgsub
825 $ hg branch br
825 $ hg branch br
826 marked working directory as branch br
826 marked working directory as branch br
827 (branches are permanent and global, did you want a bookmark?)
827 (branches are permanent and global, did you want a bookmark?)
828 $ echo b > b
828 $ echo b > b
829 $ hg -R s up 3
829 $ hg -R s up 3
830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 $ hg ci -Am1
831 $ hg ci -Am1
832 adding b
832 adding b
833 $ hg up default
833 $ hg up default
834 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
834 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
835 $ echo c > c
835 $ echo c > c
836 $ hg ci -Am1
836 $ hg ci -Am1
837 adding c
837 adding c
838 $ hg up 1
838 $ hg up 1
839 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
839 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
840 $ hg merge 2
840 $ hg merge 2
841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
842 (branch merge, don't forget to commit)
842 (branch merge, don't forget to commit)
843 $ hg ci -m1
843 $ hg ci -m1
844 $ hg up 2
844 $ hg up 2
845 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
845 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
846 $ hg -R s up 4
846 $ hg -R s up 4
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 $ echo d > d
848 $ echo d > d
849 $ hg ci -Am1
849 $ hg ci -Am1
850 adding d
850 adding d
851 $ hg up 3
851 $ hg up 3
852 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
852 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
853 $ hg -R s up 5
853 $ hg -R s up 5
854 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
854 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
855 $ echo e > e
855 $ echo e > e
856 $ hg ci -Am1
856 $ hg ci -Am1
857 adding e
857 adding e
858
858
859 $ hg up 5
859 $ hg up 5
860 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
860 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
861 $ hg merge 4 # try to merge default into br again
861 $ hg merge 4 # try to merge default into br again
862 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
862 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
863 (M)erge, keep (l)ocal or keep (r)emote? m
863 (M)erge, keep (l)ocal or keep (r)emote? m
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
865 (branch merge, don't forget to commit)
865 (branch merge, don't forget to commit)
866 $ cd ..
866 $ cd ..
867
867
868 test subrepo delete from .hgsubstate
868 test subrepo delete from .hgsubstate
869
869
870 $ hg init testdelete
870 $ hg init testdelete
871 $ mkdir testdelete/nested testdelete/nested2
871 $ mkdir testdelete/nested testdelete/nested2
872 $ hg init testdelete/nested
872 $ hg init testdelete/nested
873 $ hg init testdelete/nested2
873 $ hg init testdelete/nested2
874 $ echo test > testdelete/nested/foo
874 $ echo test > testdelete/nested/foo
875 $ echo test > testdelete/nested2/foo
875 $ echo test > testdelete/nested2/foo
876 $ hg -R testdelete/nested add
876 $ hg -R testdelete/nested add
877 adding testdelete/nested/foo (glob)
877 adding testdelete/nested/foo (glob)
878 $ hg -R testdelete/nested2 add
878 $ hg -R testdelete/nested2 add
879 adding testdelete/nested2/foo (glob)
879 adding testdelete/nested2/foo (glob)
880 $ hg -R testdelete/nested ci -m test
880 $ hg -R testdelete/nested ci -m test
881 $ hg -R testdelete/nested2 ci -m test
881 $ hg -R testdelete/nested2 ci -m test
882 $ echo nested = nested > testdelete/.hgsub
882 $ echo nested = nested > testdelete/.hgsub
883 $ echo nested2 = nested2 >> testdelete/.hgsub
883 $ echo nested2 = nested2 >> testdelete/.hgsub
884 $ hg -R testdelete add
884 $ hg -R testdelete add
885 adding testdelete/.hgsub (glob)
885 adding testdelete/.hgsub (glob)
886 $ hg -R testdelete ci -m "nested 1 & 2 added"
886 $ hg -R testdelete ci -m "nested 1 & 2 added"
887 $ echo nested = nested > testdelete/.hgsub
887 $ echo nested = nested > testdelete/.hgsub
888 $ hg -R testdelete ci -m "nested 2 deleted"
888 $ hg -R testdelete ci -m "nested 2 deleted"
889 $ cat testdelete/.hgsubstate
889 $ cat testdelete/.hgsubstate
890 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
890 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
891 $ hg -R testdelete remove testdelete/.hgsub
891 $ hg -R testdelete remove testdelete/.hgsub
892 $ hg -R testdelete ci -m ".hgsub deleted"
892 $ hg -R testdelete ci -m ".hgsub deleted"
893 $ cat testdelete/.hgsubstate
893 $ cat testdelete/.hgsubstate
894 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
894 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
895
895
896 test repository cloning
896 test repository cloning
897
897
898 $ mkdir mercurial mercurial2
898 $ mkdir mercurial mercurial2
899 $ hg init nested_absolute
899 $ hg init nested_absolute
900 $ echo test > nested_absolute/foo
900 $ echo test > nested_absolute/foo
901 $ hg -R nested_absolute add
901 $ hg -R nested_absolute add
902 adding nested_absolute/foo (glob)
902 adding nested_absolute/foo (glob)
903 $ hg -R nested_absolute ci -mtest
903 $ hg -R nested_absolute ci -mtest
904 $ cd mercurial
904 $ cd mercurial
905 $ hg init nested_relative
905 $ hg init nested_relative
906 $ echo test2 > nested_relative/foo2
906 $ echo test2 > nested_relative/foo2
907 $ hg -R nested_relative add
907 $ hg -R nested_relative add
908 adding nested_relative/foo2 (glob)
908 adding nested_relative/foo2 (glob)
909 $ hg -R nested_relative ci -mtest2
909 $ hg -R nested_relative ci -mtest2
910 $ hg init main
910 $ hg init main
911 $ echo "nested_relative = ../nested_relative" > main/.hgsub
911 $ echo "nested_relative = ../nested_relative" > main/.hgsub
912 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
912 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
913 $ hg -R main add
913 $ hg -R main add
914 adding main/.hgsub (glob)
914 adding main/.hgsub (glob)
915 $ hg -R main ci -m "add subrepos"
915 $ hg -R main ci -m "add subrepos"
916 $ cd ..
916 $ cd ..
917 $ hg clone mercurial/main mercurial2/main
917 $ hg clone mercurial/main mercurial2/main
918 updating to branch default
918 updating to branch default
919 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
919 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
920 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
921 > mercurial2/main/nested_relative/.hg/hgrc
921 > mercurial2/main/nested_relative/.hg/hgrc
922 [paths]
922 [paths]
923 default = $TESTTMP/mercurial/nested_absolute
923 default = $TESTTMP/mercurial/nested_absolute
924 [paths]
924 [paths]
925 default = $TESTTMP/mercurial/nested_relative
925 default = $TESTTMP/mercurial/nested_relative
926 $ rm -rf mercurial mercurial2
926 $ rm -rf mercurial mercurial2
927
927
928 Issue1977: multirepo push should fail if subrepo push fails
928 Issue1977: multirepo push should fail if subrepo push fails
929
929
930 $ hg init repo
930 $ hg init repo
931 $ hg init repo/s
931 $ hg init repo/s
932 $ echo a > repo/s/a
932 $ echo a > repo/s/a
933 $ hg -R repo/s ci -Am0
933 $ hg -R repo/s ci -Am0
934 adding a
934 adding a
935 $ echo s = s > repo/.hgsub
935 $ echo s = s > repo/.hgsub
936 $ hg -R repo ci -Am1
936 $ hg -R repo ci -Am1
937 adding .hgsub
937 adding .hgsub
938 $ hg clone repo repo2
938 $ hg clone repo repo2
939 updating to branch default
939 updating to branch default
940 cloning subrepo s from $TESTTMP/repo/s
940 cloning subrepo s from $TESTTMP/repo/s
941 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
941 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
942 $ hg -q -R repo2 pull -u
942 $ hg -q -R repo2 pull -u
943 $ echo 1 > repo2/s/a
943 $ echo 1 > repo2/s/a
944 $ hg -R repo2/s ci -m2
944 $ hg -R repo2/s ci -m2
945 $ hg -q -R repo2/s push
945 $ hg -q -R repo2/s push
946 $ hg -R repo2/s up -C 0
946 $ hg -R repo2/s up -C 0
947 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
947 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
948 $ echo 2 > repo2/s/b
948 $ echo 2 > repo2/s/b
949 $ hg -R repo2/s ci -m3 -A
949 $ hg -R repo2/s ci -m3 -A
950 adding b
950 adding b
951 created new head
951 created new head
952 $ hg -R repo2 ci -m3
952 $ hg -R repo2 ci -m3
953 $ hg -q -R repo2 push
953 $ hg -q -R repo2 push
954 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
954 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
955 (merge or see 'hg help push' for details about pushing new heads)
955 (merge or see 'hg help push' for details about pushing new heads)
956 [255]
956 [255]
957 $ hg -R repo update
957 $ hg -R repo update
958 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
958 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
959
959
960 test if untracked file is not overwritten
960 test if untracked file is not overwritten
961
961
962 (this also tests that updated .hgsubstate is treated as "modified",
962 (this also tests that updated .hgsubstate is treated as "modified",
963 when 'merge.update()' is aborted before 'merge.recordupdates()', even
963 when 'merge.update()' is aborted before 'merge.recordupdates()', even
964 if none of mode, size and timestamp of it isn't changed on the
964 if none of mode, size and timestamp of it isn't changed on the
965 filesystem (see also issue4583))
965 filesystem (see also issue4583))
966
966
967 $ echo issue3276_ok > repo/s/b
967 $ echo issue3276_ok > repo/s/b
968 $ hg -R repo2 push -f -q
968 $ hg -R repo2 push -f -q
969 $ touch -t 200001010000 repo/.hgsubstate
969 $ touch -t 200001010000 repo/.hgsubstate
970
970
971 $ cat >> repo/.hg/hgrc <<EOF
971 $ cat >> repo/.hg/hgrc <<EOF
972 > [fakedirstatewritetime]
972 > [fakedirstatewritetime]
973 > # emulate invoking dirstate.write() via repo.status()
973 > # emulate invoking dirstate.write() via repo.status()
974 > # at 2000-01-01 00:00
974 > # at 2000-01-01 00:00
975 > fakenow = 200001010000
975 > fakenow = 200001010000
976 >
976 >
977 > [extensions]
977 > [extensions]
978 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
978 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
979 > EOF
979 > EOF
980 $ hg -R repo update
980 $ hg -R repo update
981 b: untracked file differs
981 b: untracked file differs
982 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
982 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
983 [255]
983 [255]
984 $ cat >> repo/.hg/hgrc <<EOF
984 $ cat >> repo/.hg/hgrc <<EOF
985 > [extensions]
985 > [extensions]
986 > fakedirstatewritetime = !
986 > fakedirstatewritetime = !
987 > EOF
987 > EOF
988
988
989 $ cat repo/s/b
989 $ cat repo/s/b
990 issue3276_ok
990 issue3276_ok
991 $ rm repo/s/b
991 $ rm repo/s/b
992 $ touch -t 200001010000 repo/.hgsubstate
992 $ touch -t 200001010000 repo/.hgsubstate
993 $ hg -R repo revert --all
993 $ hg -R repo revert --all
994 reverting repo/.hgsubstate (glob)
994 reverting repo/.hgsubstate (glob)
995 reverting subrepo s
995 reverting subrepo s
996 $ hg -R repo update
996 $ hg -R repo update
997 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
997 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
998 $ cat repo/s/b
998 $ cat repo/s/b
999 2
999 2
1000 $ rm -rf repo2 repo
1000 $ rm -rf repo2 repo
1001
1001
1002
1002
1003 Issue1852 subrepos with relative paths always push/pull relative to default
1003 Issue1852 subrepos with relative paths always push/pull relative to default
1004
1004
1005 Prepare a repo with subrepo
1005 Prepare a repo with subrepo
1006
1006
1007 $ hg init issue1852a
1007 $ hg init issue1852a
1008 $ cd issue1852a
1008 $ cd issue1852a
1009 $ hg init sub/repo
1009 $ hg init sub/repo
1010 $ echo test > sub/repo/foo
1010 $ echo test > sub/repo/foo
1011 $ hg -R sub/repo add sub/repo/foo
1011 $ hg -R sub/repo add sub/repo/foo
1012 $ echo sub/repo = sub/repo > .hgsub
1012 $ echo sub/repo = sub/repo > .hgsub
1013 $ hg add .hgsub
1013 $ hg add .hgsub
1014 $ hg ci -mtest
1014 $ hg ci -mtest
1015 committing subrepository sub/repo (glob)
1015 committing subrepository sub/repo (glob)
1016 $ echo test >> sub/repo/foo
1016 $ echo test >> sub/repo/foo
1017 $ hg ci -mtest
1017 $ hg ci -mtest
1018 committing subrepository sub/repo (glob)
1018 committing subrepository sub/repo (glob)
1019 $ hg cat sub/repo/foo
1019 $ hg cat sub/repo/foo
1020 test
1020 test
1021 test
1021 test
1022 $ mkdir -p tmp/sub/repo
1022 $ mkdir -p tmp/sub/repo
1023 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1023 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1024 $ cat tmp/sub/repo/foo_p
1024 $ cat tmp/sub/repo/foo_p
1025 test
1025 test
1026 $ mv sub/repo sub_
1026 $ mv sub/repo sub_
1027 $ hg cat sub/repo/baz
1027 $ hg cat sub/repo/baz
1028 skipping missing subrepository: sub/repo
1028 skipping missing subrepository: sub/repo
1029 [1]
1029 [1]
1030 $ rm -rf sub/repo
1030 $ rm -rf sub/repo
1031 $ mv sub_ sub/repo
1031 $ mv sub_ sub/repo
1032 $ cd ..
1032 $ cd ..
1033
1033
1034 Create repo without default path, pull top repo, and see what happens on update
1034 Create repo without default path, pull top repo, and see what happens on update
1035
1035
1036 $ hg init issue1852b
1036 $ hg init issue1852b
1037 $ hg -R issue1852b pull issue1852a
1037 $ hg -R issue1852b pull issue1852a
1038 pulling from issue1852a
1038 pulling from issue1852a
1039 requesting all changes
1039 requesting all changes
1040 adding changesets
1040 adding changesets
1041 adding manifests
1041 adding manifests
1042 adding file changes
1042 adding file changes
1043 added 2 changesets with 3 changes to 2 files
1043 added 2 changesets with 3 changes to 2 files
1044 (run 'hg update' to get a working copy)
1044 (run 'hg update' to get a working copy)
1045 $ hg -R issue1852b update
1045 $ hg -R issue1852b update
1046 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
1046 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
1047 [255]
1047 [255]
1048
1048
1049 Ensure a full traceback, not just the SubrepoAbort part
1049 Ensure a full traceback, not just the SubrepoAbort part
1050
1050
1051 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1051 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1052 raise error.Abort(_("default path for subrepository not found"))
1052 raise error.Abort(_("default path for subrepository not found"))
1053
1053
1054 Pull -u now doesn't help
1054 Pull -u now doesn't help
1055
1055
1056 $ hg -R issue1852b pull -u issue1852a
1056 $ hg -R issue1852b pull -u issue1852a
1057 pulling from issue1852a
1057 pulling from issue1852a
1058 searching for changes
1058 searching for changes
1059 no changes found
1059 no changes found
1060
1060
1061 Try the same, but with pull -u
1061 Try the same, but with pull -u
1062
1062
1063 $ hg init issue1852c
1063 $ hg init issue1852c
1064 $ hg -R issue1852c pull -r0 -u issue1852a
1064 $ hg -R issue1852c pull -r0 -u issue1852a
1065 pulling from issue1852a
1065 pulling from issue1852a
1066 adding changesets
1066 adding changesets
1067 adding manifests
1067 adding manifests
1068 adding file changes
1068 adding file changes
1069 added 1 changesets with 2 changes to 2 files
1069 added 1 changesets with 2 changes to 2 files
1070 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
1070 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
1071 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1071 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1072
1072
1073 Try to push from the other side
1073 Try to push from the other side
1074
1074
1075 $ hg -R issue1852a push `pwd`/issue1852c
1075 $ hg -R issue1852a push `pwd`/issue1852c
1076 pushing to $TESTTMP/issue1852c (glob)
1076 pushing to $TESTTMP/issue1852c (glob)
1077 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1077 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1078 searching for changes
1078 searching for changes
1079 no changes found
1079 no changes found
1080 searching for changes
1080 searching for changes
1081 adding changesets
1081 adding changesets
1082 adding manifests
1082 adding manifests
1083 adding file changes
1083 adding file changes
1084 added 1 changesets with 1 changes to 1 files
1084 added 1 changesets with 1 changes to 1 files
1085
1085
1086 Incoming and outgoing should not use the default path:
1086 Incoming and outgoing should not use the default path:
1087
1087
1088 $ hg clone -q issue1852a issue1852d
1088 $ hg clone -q issue1852a issue1852d
1089 $ hg -R issue1852d outgoing --subrepos issue1852c
1089 $ hg -R issue1852d outgoing --subrepos issue1852c
1090 comparing with issue1852c
1090 comparing with issue1852c
1091 searching for changes
1091 searching for changes
1092 no changes found
1092 no changes found
1093 comparing with issue1852c/sub/repo
1093 comparing with issue1852c/sub/repo
1094 searching for changes
1094 searching for changes
1095 no changes found
1095 no changes found
1096 [1]
1096 [1]
1097 $ hg -R issue1852d incoming --subrepos issue1852c
1097 $ hg -R issue1852d incoming --subrepos issue1852c
1098 comparing with issue1852c
1098 comparing with issue1852c
1099 searching for changes
1099 searching for changes
1100 no changes found
1100 no changes found
1101 comparing with issue1852c/sub/repo
1101 comparing with issue1852c/sub/repo
1102 searching for changes
1102 searching for changes
1103 no changes found
1103 no changes found
1104 [1]
1104 [1]
1105
1105
1106 Check that merge of a new subrepo doesn't write the uncommitted state to
1106 Check that merge of a new subrepo doesn't write the uncommitted state to
1107 .hgsubstate (issue4622)
1107 .hgsubstate (issue4622)
1108
1108
1109 $ hg init issue1852a/addedsub
1109 $ hg init issue1852a/addedsub
1110 $ echo zzz > issue1852a/addedsub/zz.txt
1110 $ echo zzz > issue1852a/addedsub/zz.txt
1111 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1111 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1112
1112
1113 $ hg clone issue1852a/addedsub issue1852d/addedsub
1113 $ hg clone issue1852a/addedsub issue1852d/addedsub
1114 updating to branch default
1114 updating to branch default
1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1116
1116
1117 $ echo def > issue1852a/sub/repo/foo
1117 $ echo def > issue1852a/sub/repo/foo
1118 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1118 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1119 adding tmp/sub/repo/foo_p
1119 adding tmp/sub/repo/foo_p
1120 committing subrepository sub/repo (glob)
1120 committing subrepository sub/repo (glob)
1121
1121
1122 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1122 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1123 $ echo xyz > issue1852d/sub/repo/foo
1123 $ echo xyz > issue1852d/sub/repo/foo
1124 $ hg -R issue1852d pull -u
1124 $ hg -R issue1852d pull -u
1125 pulling from $TESTTMP/issue1852a (glob)
1125 pulling from $TESTTMP/issue1852a (glob)
1126 searching for changes
1126 searching for changes
1127 adding changesets
1127 adding changesets
1128 adding manifests
1128 adding manifests
1129 adding file changes
1129 adding file changes
1130 added 1 changesets with 2 changes to 2 files
1130 added 1 changesets with 2 changes to 2 files
1131 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1131 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1132 (M)erge, keep (l)ocal or keep (r)emote? m
1132 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1133 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob)
1133 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob)
1134 searching for changes
1134 searching for changes
1135 adding changesets
1135 adding changesets
1136 adding manifests
1136 adding manifests
1137 adding file changes
1137 adding file changes
1138 added 1 changesets with 1 changes to 1 files
1138 added 1 changesets with 1 changes to 1 files
1139 subrepository sources for sub/repo differ (glob)
1139 subrepository sources for sub/repo differ (glob)
1140 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1140 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1142 $ cat issue1852d/.hgsubstate
1142 $ cat issue1852d/.hgsubstate
1143 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1143 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1144
1144
1145 Check status of files when none of them belong to the first
1145 Check status of files when none of them belong to the first
1146 subrepository:
1146 subrepository:
1147
1147
1148 $ hg init subrepo-status
1148 $ hg init subrepo-status
1149 $ cd subrepo-status
1149 $ cd subrepo-status
1150 $ hg init subrepo-1
1150 $ hg init subrepo-1
1151 $ hg init subrepo-2
1151 $ hg init subrepo-2
1152 $ cd subrepo-2
1152 $ cd subrepo-2
1153 $ touch file
1153 $ touch file
1154 $ hg add file
1154 $ hg add file
1155 $ cd ..
1155 $ cd ..
1156 $ echo subrepo-1 = subrepo-1 > .hgsub
1156 $ echo subrepo-1 = subrepo-1 > .hgsub
1157 $ echo subrepo-2 = subrepo-2 >> .hgsub
1157 $ echo subrepo-2 = subrepo-2 >> .hgsub
1158 $ hg add .hgsub
1158 $ hg add .hgsub
1159 $ hg ci -m 'Added subrepos'
1159 $ hg ci -m 'Added subrepos'
1160 committing subrepository subrepo-2
1160 committing subrepository subrepo-2
1161 $ hg st subrepo-2/file
1161 $ hg st subrepo-2/file
1162
1162
1163 Check that share works with subrepo
1163 Check that share works with subrepo
1164 $ hg --config extensions.share= share . ../shared
1164 $ hg --config extensions.share= share . ../shared
1165 updating working directory
1165 updating working directory
1166 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1166 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1167 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1167 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1168 $ test -f ../shared/subrepo-1/.hg/sharedpath
1168 $ test -f ../shared/subrepo-1/.hg/sharedpath
1169 [1]
1169 [1]
1170 $ hg -R ../shared in
1170 $ hg -R ../shared in
1171 abort: repository default not found!
1171 abort: repository default not found!
1172 [255]
1172 [255]
1173 $ hg -R ../shared/subrepo-2 showconfig paths
1173 $ hg -R ../shared/subrepo-2 showconfig paths
1174 paths.default=$TESTTMP/subrepo-status/subrepo-2
1174 paths.default=$TESTTMP/subrepo-status/subrepo-2
1175 $ hg -R ../shared/subrepo-1 sum --remote
1175 $ hg -R ../shared/subrepo-1 sum --remote
1176 parent: -1:000000000000 tip (empty repository)
1176 parent: -1:000000000000 tip (empty repository)
1177 branch: default
1177 branch: default
1178 commit: (clean)
1178 commit: (clean)
1179 update: (current)
1179 update: (current)
1180 remote: (synced)
1180 remote: (synced)
1181
1181
1182 Check hg update --clean
1182 Check hg update --clean
1183 $ cd $TESTTMP/t
1183 $ cd $TESTTMP/t
1184 $ rm -r t/t.orig
1184 $ rm -r t/t.orig
1185 $ hg status -S --all
1185 $ hg status -S --all
1186 C .hgsub
1186 C .hgsub
1187 C .hgsubstate
1187 C .hgsubstate
1188 C a
1188 C a
1189 C s/.hgsub
1189 C s/.hgsub
1190 C s/.hgsubstate
1190 C s/.hgsubstate
1191 C s/a
1191 C s/a
1192 C s/ss/a
1192 C s/ss/a
1193 C t/t
1193 C t/t
1194 $ echo c1 > s/a
1194 $ echo c1 > s/a
1195 $ cd s
1195 $ cd s
1196 $ echo c1 > b
1196 $ echo c1 > b
1197 $ echo c1 > c
1197 $ echo c1 > c
1198 $ hg add b
1198 $ hg add b
1199 $ cd ..
1199 $ cd ..
1200 $ hg status -S
1200 $ hg status -S
1201 M s/a
1201 M s/a
1202 A s/b
1202 A s/b
1203 ? s/c
1203 ? s/c
1204 $ hg update -C
1204 $ hg update -C
1205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1206 2 other heads for branch "default"
1206 2 other heads for branch "default"
1207 $ hg status -S
1207 $ hg status -S
1208 ? s/b
1208 ? s/b
1209 ? s/c
1209 ? s/c
1210
1210
1211 Sticky subrepositories, no changes
1211 Sticky subrepositories, no changes
1212 $ cd $TESTTMP/t
1212 $ cd $TESTTMP/t
1213 $ hg id
1213 $ hg id
1214 925c17564ef8 tip
1214 925c17564ef8 tip
1215 $ hg -R s id
1215 $ hg -R s id
1216 12a213df6fa9 tip
1216 12a213df6fa9 tip
1217 $ hg -R t id
1217 $ hg -R t id
1218 52c0adc0515a tip
1218 52c0adc0515a tip
1219 $ hg update 11
1219 $ hg update 11
1220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1221 $ hg id
1221 $ hg id
1222 365661e5936a
1222 365661e5936a
1223 $ hg -R s id
1223 $ hg -R s id
1224 fc627a69481f
1224 fc627a69481f
1225 $ hg -R t id
1225 $ hg -R t id
1226 e95bcfa18a35
1226 e95bcfa18a35
1227
1227
1228 Sticky subrepositories, file changes
1228 Sticky subrepositories, file changes
1229 $ touch s/f1
1229 $ touch s/f1
1230 $ touch t/f1
1230 $ touch t/f1
1231 $ hg add -S s/f1
1231 $ hg add -S s/f1
1232 $ hg add -S t/f1
1232 $ hg add -S t/f1
1233 $ hg id
1233 $ hg id
1234 365661e5936a+
1234 365661e5936a+
1235 $ hg -R s id
1235 $ hg -R s id
1236 fc627a69481f+
1236 fc627a69481f+
1237 $ hg -R t id
1237 $ hg -R t id
1238 e95bcfa18a35+
1238 e95bcfa18a35+
1239 $ hg update tip
1239 $ hg update tip
1240 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1240 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1241 (M)erge, keep (l)ocal or keep (r)emote? m
1241 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1242 subrepository sources for s differ
1242 subrepository sources for s differ
1243 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1243 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1244 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1244 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1245 (M)erge, keep (l)ocal or keep (r)emote? m
1245 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1246 subrepository sources for t differ
1246 subrepository sources for t differ
1247 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1247 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1248 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1248 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1249 $ hg id
1249 $ hg id
1250 925c17564ef8+ tip
1250 925c17564ef8+ tip
1251 $ hg -R s id
1251 $ hg -R s id
1252 fc627a69481f+
1252 fc627a69481f+
1253 $ hg -R t id
1253 $ hg -R t id
1254 e95bcfa18a35+
1254 e95bcfa18a35+
1255 $ hg update --clean tip
1255 $ hg update --clean tip
1256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1257
1257
1258 Sticky subrepository, revision updates
1258 Sticky subrepository, revision updates
1259 $ hg id
1259 $ hg id
1260 925c17564ef8 tip
1260 925c17564ef8 tip
1261 $ hg -R s id
1261 $ hg -R s id
1262 12a213df6fa9 tip
1262 12a213df6fa9 tip
1263 $ hg -R t id
1263 $ hg -R t id
1264 52c0adc0515a tip
1264 52c0adc0515a tip
1265 $ cd s
1265 $ cd s
1266 $ hg update -r -2
1266 $ hg update -r -2
1267 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1267 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1268 $ cd ../t
1268 $ cd ../t
1269 $ hg update -r 2
1269 $ hg update -r 2
1270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1271 $ cd ..
1271 $ cd ..
1272 $ hg update 10
1272 $ hg update 10
1273 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1273 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1274 (M)erge, keep (l)ocal or keep (r)emote? m
1274 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1275 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1275 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1276 (M)erge, keep (l)ocal or keep (r)emote? m
1276 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1277 subrepository sources for t differ (in checked out version)
1277 subrepository sources for t differ (in checked out version)
1278 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1278 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1279 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1279 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1280 $ hg id
1280 $ hg id
1281 e45c8b14af55+
1281 e45c8b14af55+
1282 $ hg -R s id
1282 $ hg -R s id
1283 02dcf1d70411
1283 02dcf1d70411
1284 $ hg -R t id
1284 $ hg -R t id
1285 7af322bc1198
1285 7af322bc1198
1286
1286
1287 Sticky subrepository, file changes and revision updates
1287 Sticky subrepository, file changes and revision updates
1288 $ touch s/f1
1288 $ touch s/f1
1289 $ touch t/f1
1289 $ touch t/f1
1290 $ hg add -S s/f1
1290 $ hg add -S s/f1
1291 $ hg add -S t/f1
1291 $ hg add -S t/f1
1292 $ hg id
1292 $ hg id
1293 e45c8b14af55+
1293 e45c8b14af55+
1294 $ hg -R s id
1294 $ hg -R s id
1295 02dcf1d70411+
1295 02dcf1d70411+
1296 $ hg -R t id
1296 $ hg -R t id
1297 7af322bc1198+
1297 7af322bc1198+
1298 $ hg update tip
1298 $ hg update tip
1299 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1299 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1300 (M)erge, keep (l)ocal or keep (r)emote? m
1300 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1301 subrepository sources for s differ
1301 subrepository sources for s differ
1302 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1302 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1303 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1303 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1304 (M)erge, keep (l)ocal or keep (r)emote? m
1304 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1305 subrepository sources for t differ
1305 subrepository sources for t differ
1306 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1306 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1308 $ hg id
1308 $ hg id
1309 925c17564ef8+ tip
1309 925c17564ef8+ tip
1310 $ hg -R s id
1310 $ hg -R s id
1311 02dcf1d70411+
1311 02dcf1d70411+
1312 $ hg -R t id
1312 $ hg -R t id
1313 7af322bc1198+
1313 7af322bc1198+
1314
1314
1315 Sticky repository, update --clean
1315 Sticky repository, update --clean
1316 $ hg update --clean tip
1316 $ hg update --clean tip
1317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1318 $ hg id
1318 $ hg id
1319 925c17564ef8 tip
1319 925c17564ef8 tip
1320 $ hg -R s id
1320 $ hg -R s id
1321 12a213df6fa9 tip
1321 12a213df6fa9 tip
1322 $ hg -R t id
1322 $ hg -R t id
1323 52c0adc0515a tip
1323 52c0adc0515a tip
1324
1324
1325 Test subrepo already at intended revision:
1325 Test subrepo already at intended revision:
1326 $ cd s
1326 $ cd s
1327 $ hg update fc627a69481f
1327 $ hg update fc627a69481f
1328 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1328 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1329 $ cd ..
1329 $ cd ..
1330 $ hg update 11
1330 $ hg update 11
1331 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1331 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1332 (M)erge, keep (l)ocal or keep (r)emote? m
1332 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1333 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1333 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1335 $ hg id -n
1335 $ hg id -n
1336 11+
1336 11+
1337 $ hg -R s id
1337 $ hg -R s id
1338 fc627a69481f
1338 fc627a69481f
1339 $ hg -R t id
1339 $ hg -R t id
1340 e95bcfa18a35
1340 e95bcfa18a35
1341
1341
1342 Test that removing .hgsubstate doesn't break anything:
1342 Test that removing .hgsubstate doesn't break anything:
1343
1343
1344 $ hg rm -f .hgsubstate
1344 $ hg rm -f .hgsubstate
1345 $ hg ci -mrm
1345 $ hg ci -mrm
1346 nothing changed
1346 nothing changed
1347 [1]
1347 [1]
1348 $ hg log -vr tip
1348 $ hg log -vr tip
1349 changeset: 13:925c17564ef8
1349 changeset: 13:925c17564ef8
1350 tag: tip
1350 tag: tip
1351 user: test
1351 user: test
1352 date: Thu Jan 01 00:00:00 1970 +0000
1352 date: Thu Jan 01 00:00:00 1970 +0000
1353 files: .hgsubstate
1353 files: .hgsubstate
1354 description:
1354 description:
1355 13
1355 13
1356
1356
1357
1357
1358
1358
1359 Test that removing .hgsub removes .hgsubstate:
1359 Test that removing .hgsub removes .hgsubstate:
1360
1360
1361 $ hg rm .hgsub
1361 $ hg rm .hgsub
1362 $ hg ci -mrm2
1362 $ hg ci -mrm2
1363 created new head
1363 created new head
1364 $ hg log -vr tip
1364 $ hg log -vr tip
1365 changeset: 14:2400bccd50af
1365 changeset: 14:2400bccd50af
1366 tag: tip
1366 tag: tip
1367 parent: 11:365661e5936a
1367 parent: 11:365661e5936a
1368 user: test
1368 user: test
1369 date: Thu Jan 01 00:00:00 1970 +0000
1369 date: Thu Jan 01 00:00:00 1970 +0000
1370 files: .hgsub .hgsubstate
1370 files: .hgsub .hgsubstate
1371 description:
1371 description:
1372 rm2
1372 rm2
1373
1373
1374
1374
1375 Test issue3153: diff -S with deleted subrepos
1375 Test issue3153: diff -S with deleted subrepos
1376
1376
1377 $ hg diff --nodates -S -c .
1377 $ hg diff --nodates -S -c .
1378 diff -r 365661e5936a -r 2400bccd50af .hgsub
1378 diff -r 365661e5936a -r 2400bccd50af .hgsub
1379 --- a/.hgsub
1379 --- a/.hgsub
1380 +++ /dev/null
1380 +++ /dev/null
1381 @@ -1,2 +0,0 @@
1381 @@ -1,2 +0,0 @@
1382 -s = s
1382 -s = s
1383 -t = t
1383 -t = t
1384 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1384 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1385 --- a/.hgsubstate
1385 --- a/.hgsubstate
1386 +++ /dev/null
1386 +++ /dev/null
1387 @@ -1,2 +0,0 @@
1387 @@ -1,2 +0,0 @@
1388 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1388 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1389 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1389 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1390
1390
1391 Test behavior of add for explicit path in subrepo:
1391 Test behavior of add for explicit path in subrepo:
1392 $ cd ..
1392 $ cd ..
1393 $ hg init explicit
1393 $ hg init explicit
1394 $ cd explicit
1394 $ cd explicit
1395 $ echo s = s > .hgsub
1395 $ echo s = s > .hgsub
1396 $ hg add .hgsub
1396 $ hg add .hgsub
1397 $ hg init s
1397 $ hg init s
1398 $ hg ci -m0
1398 $ hg ci -m0
1399 Adding with an explicit path in a subrepo adds the file
1399 Adding with an explicit path in a subrepo adds the file
1400 $ echo c1 > f1
1400 $ echo c1 > f1
1401 $ echo c2 > s/f2
1401 $ echo c2 > s/f2
1402 $ hg st -S
1402 $ hg st -S
1403 ? f1
1403 ? f1
1404 ? s/f2
1404 ? s/f2
1405 $ hg add s/f2
1405 $ hg add s/f2
1406 $ hg st -S
1406 $ hg st -S
1407 A s/f2
1407 A s/f2
1408 ? f1
1408 ? f1
1409 $ hg ci -R s -m0
1409 $ hg ci -R s -m0
1410 $ hg ci -Am1
1410 $ hg ci -Am1
1411 adding f1
1411 adding f1
1412 Adding with an explicit path in a subrepo with -S has the same behavior
1412 Adding with an explicit path in a subrepo with -S has the same behavior
1413 $ echo c3 > f3
1413 $ echo c3 > f3
1414 $ echo c4 > s/f4
1414 $ echo c4 > s/f4
1415 $ hg st -S
1415 $ hg st -S
1416 ? f3
1416 ? f3
1417 ? s/f4
1417 ? s/f4
1418 $ hg add -S s/f4
1418 $ hg add -S s/f4
1419 $ hg st -S
1419 $ hg st -S
1420 A s/f4
1420 A s/f4
1421 ? f3
1421 ? f3
1422 $ hg ci -R s -m1
1422 $ hg ci -R s -m1
1423 $ hg ci -Ama2
1423 $ hg ci -Ama2
1424 adding f3
1424 adding f3
1425 Adding without a path or pattern silently ignores subrepos
1425 Adding without a path or pattern silently ignores subrepos
1426 $ echo c5 > f5
1426 $ echo c5 > f5
1427 $ echo c6 > s/f6
1427 $ echo c6 > s/f6
1428 $ echo c7 > s/f7
1428 $ echo c7 > s/f7
1429 $ hg st -S
1429 $ hg st -S
1430 ? f5
1430 ? f5
1431 ? s/f6
1431 ? s/f6
1432 ? s/f7
1432 ? s/f7
1433 $ hg add
1433 $ hg add
1434 adding f5
1434 adding f5
1435 $ hg st -S
1435 $ hg st -S
1436 A f5
1436 A f5
1437 ? s/f6
1437 ? s/f6
1438 ? s/f7
1438 ? s/f7
1439 $ hg ci -R s -Am2
1439 $ hg ci -R s -Am2
1440 adding f6
1440 adding f6
1441 adding f7
1441 adding f7
1442 $ hg ci -m3
1442 $ hg ci -m3
1443 Adding without a path or pattern with -S also adds files in subrepos
1443 Adding without a path or pattern with -S also adds files in subrepos
1444 $ echo c8 > f8
1444 $ echo c8 > f8
1445 $ echo c9 > s/f9
1445 $ echo c9 > s/f9
1446 $ echo c10 > s/f10
1446 $ echo c10 > s/f10
1447 $ hg st -S
1447 $ hg st -S
1448 ? f8
1448 ? f8
1449 ? s/f10
1449 ? s/f10
1450 ? s/f9
1450 ? s/f9
1451 $ hg add -S
1451 $ hg add -S
1452 adding f8
1452 adding f8
1453 adding s/f10 (glob)
1453 adding s/f10 (glob)
1454 adding s/f9 (glob)
1454 adding s/f9 (glob)
1455 $ hg st -S
1455 $ hg st -S
1456 A f8
1456 A f8
1457 A s/f10
1457 A s/f10
1458 A s/f9
1458 A s/f9
1459 $ hg ci -R s -m3
1459 $ hg ci -R s -m3
1460 $ hg ci -m4
1460 $ hg ci -m4
1461 Adding with a pattern silently ignores subrepos
1461 Adding with a pattern silently ignores subrepos
1462 $ echo c11 > fm11
1462 $ echo c11 > fm11
1463 $ echo c12 > fn12
1463 $ echo c12 > fn12
1464 $ echo c13 > s/fm13
1464 $ echo c13 > s/fm13
1465 $ echo c14 > s/fn14
1465 $ echo c14 > s/fn14
1466 $ hg st -S
1466 $ hg st -S
1467 ? fm11
1467 ? fm11
1468 ? fn12
1468 ? fn12
1469 ? s/fm13
1469 ? s/fm13
1470 ? s/fn14
1470 ? s/fn14
1471 $ hg add 'glob:**fm*'
1471 $ hg add 'glob:**fm*'
1472 adding fm11
1472 adding fm11
1473 $ hg st -S
1473 $ hg st -S
1474 A fm11
1474 A fm11
1475 ? fn12
1475 ? fn12
1476 ? s/fm13
1476 ? s/fm13
1477 ? s/fn14
1477 ? s/fn14
1478 $ hg ci -R s -Am4
1478 $ hg ci -R s -Am4
1479 adding fm13
1479 adding fm13
1480 adding fn14
1480 adding fn14
1481 $ hg ci -Am5
1481 $ hg ci -Am5
1482 adding fn12
1482 adding fn12
1483 Adding with a pattern with -S also adds matches in subrepos
1483 Adding with a pattern with -S also adds matches in subrepos
1484 $ echo c15 > fm15
1484 $ echo c15 > fm15
1485 $ echo c16 > fn16
1485 $ echo c16 > fn16
1486 $ echo c17 > s/fm17
1486 $ echo c17 > s/fm17
1487 $ echo c18 > s/fn18
1487 $ echo c18 > s/fn18
1488 $ hg st -S
1488 $ hg st -S
1489 ? fm15
1489 ? fm15
1490 ? fn16
1490 ? fn16
1491 ? s/fm17
1491 ? s/fm17
1492 ? s/fn18
1492 ? s/fn18
1493 $ hg add -S 'glob:**fm*'
1493 $ hg add -S 'glob:**fm*'
1494 adding fm15
1494 adding fm15
1495 adding s/fm17 (glob)
1495 adding s/fm17 (glob)
1496 $ hg st -S
1496 $ hg st -S
1497 A fm15
1497 A fm15
1498 A s/fm17
1498 A s/fm17
1499 ? fn16
1499 ? fn16
1500 ? s/fn18
1500 ? s/fn18
1501 $ hg ci -R s -Am5
1501 $ hg ci -R s -Am5
1502 adding fn18
1502 adding fn18
1503 $ hg ci -Am6
1503 $ hg ci -Am6
1504 adding fn16
1504 adding fn16
1505
1505
1506 Test behavior of forget for explicit path in subrepo:
1506 Test behavior of forget for explicit path in subrepo:
1507 Forgetting an explicit path in a subrepo untracks the file
1507 Forgetting an explicit path in a subrepo untracks the file
1508 $ echo c19 > s/f19
1508 $ echo c19 > s/f19
1509 $ hg add s/f19
1509 $ hg add s/f19
1510 $ hg st -S
1510 $ hg st -S
1511 A s/f19
1511 A s/f19
1512 $ hg forget s/f19
1512 $ hg forget s/f19
1513 $ hg st -S
1513 $ hg st -S
1514 ? s/f19
1514 ? s/f19
1515 $ rm s/f19
1515 $ rm s/f19
1516 $ cd ..
1516 $ cd ..
1517
1517
1518 Courtesy phases synchronisation to publishing server does not block the push
1518 Courtesy phases synchronisation to publishing server does not block the push
1519 (issue3781)
1519 (issue3781)
1520
1520
1521 $ cp -r main issue3781
1521 $ cp -r main issue3781
1522 $ cp -r main issue3781-dest
1522 $ cp -r main issue3781-dest
1523 $ cd issue3781-dest/s
1523 $ cd issue3781-dest/s
1524 $ hg phase tip # show we have draft changeset
1524 $ hg phase tip # show we have draft changeset
1525 5: draft
1525 5: draft
1526 $ chmod a-w .hg/store/phaseroots # prevent phase push
1526 $ chmod a-w .hg/store/phaseroots # prevent phase push
1527 $ cd ../../issue3781
1527 $ cd ../../issue3781
1528 $ cat >> .hg/hgrc << EOF
1528 $ cat >> .hg/hgrc << EOF
1529 > [paths]
1529 > [paths]
1530 > default=../issue3781-dest/
1530 > default=../issue3781-dest/
1531 > EOF
1531 > EOF
1532 $ hg push --config devel.legacy.exchange=bundle1
1532 $ hg push --config devel.legacy.exchange=bundle1
1533 pushing to $TESTTMP/issue3781-dest (glob)
1533 pushing to $TESTTMP/issue3781-dest (glob)
1534 pushing subrepo s to $TESTTMP/issue3781-dest/s
1534 pushing subrepo s to $TESTTMP/issue3781-dest/s
1535 searching for changes
1535 searching for changes
1536 no changes found
1536 no changes found
1537 searching for changes
1537 searching for changes
1538 no changes found
1538 no changes found
1539 [1]
1539 [1]
1540 # clean the push cache
1540 # clean the push cache
1541 $ rm s/.hg/cache/storehash/*
1541 $ rm s/.hg/cache/storehash/*
1542 $ hg push # bundle2+
1542 $ hg push # bundle2+
1543 pushing to $TESTTMP/issue3781-dest (glob)
1543 pushing to $TESTTMP/issue3781-dest (glob)
1544 pushing subrepo s to $TESTTMP/issue3781-dest/s
1544 pushing subrepo s to $TESTTMP/issue3781-dest/s
1545 searching for changes
1545 searching for changes
1546 no changes found
1546 no changes found
1547 searching for changes
1547 searching for changes
1548 no changes found
1548 no changes found
1549 [1]
1549 [1]
1550 $ cd ..
1550 $ cd ..
1551
1551
1552 Test phase choice for newly created commit with "phases.subrepochecks"
1552 Test phase choice for newly created commit with "phases.subrepochecks"
1553 configuration
1553 configuration
1554
1554
1555 $ cd t
1555 $ cd t
1556 $ hg update -q -r 12
1556 $ hg update -q -r 12
1557
1557
1558 $ cat >> s/ss/.hg/hgrc <<EOF
1558 $ cat >> s/ss/.hg/hgrc <<EOF
1559 > [phases]
1559 > [phases]
1560 > new-commit = secret
1560 > new-commit = secret
1561 > EOF
1561 > EOF
1562 $ cat >> s/.hg/hgrc <<EOF
1562 $ cat >> s/.hg/hgrc <<EOF
1563 > [phases]
1563 > [phases]
1564 > new-commit = draft
1564 > new-commit = draft
1565 > EOF
1565 > EOF
1566 $ echo phasecheck1 >> s/ss/a
1566 $ echo phasecheck1 >> s/ss/a
1567 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1567 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1568 committing subrepository ss
1568 committing subrepository ss
1569 transaction abort!
1569 transaction abort!
1570 rollback completed
1570 rollback completed
1571 abort: can't commit in draft phase conflicting secret from subrepository ss
1571 abort: can't commit in draft phase conflicting secret from subrepository ss
1572 [255]
1572 [255]
1573 $ echo phasecheck2 >> s/ss/a
1573 $ echo phasecheck2 >> s/ss/a
1574 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1574 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1575 committing subrepository ss
1575 committing subrepository ss
1576 $ hg -R s/ss phase tip
1576 $ hg -R s/ss phase tip
1577 3: secret
1577 3: secret
1578 $ hg -R s phase tip
1578 $ hg -R s phase tip
1579 6: draft
1579 6: draft
1580 $ echo phasecheck3 >> s/ss/a
1580 $ echo phasecheck3 >> s/ss/a
1581 $ hg -R s commit -S -m phasecheck3
1581 $ hg -R s commit -S -m phasecheck3
1582 committing subrepository ss
1582 committing subrepository ss
1583 warning: changes are committed in secret phase from subrepository ss
1583 warning: changes are committed in secret phase from subrepository ss
1584 $ hg -R s/ss phase tip
1584 $ hg -R s/ss phase tip
1585 4: secret
1585 4: secret
1586 $ hg -R s phase tip
1586 $ hg -R s phase tip
1587 7: secret
1587 7: secret
1588
1588
1589 $ cat >> t/.hg/hgrc <<EOF
1589 $ cat >> t/.hg/hgrc <<EOF
1590 > [phases]
1590 > [phases]
1591 > new-commit = draft
1591 > new-commit = draft
1592 > EOF
1592 > EOF
1593 $ cat >> .hg/hgrc <<EOF
1593 $ cat >> .hg/hgrc <<EOF
1594 > [phases]
1594 > [phases]
1595 > new-commit = public
1595 > new-commit = public
1596 > EOF
1596 > EOF
1597 $ echo phasecheck4 >> s/ss/a
1597 $ echo phasecheck4 >> s/ss/a
1598 $ echo phasecheck4 >> t/t
1598 $ echo phasecheck4 >> t/t
1599 $ hg commit -S -m phasecheck4
1599 $ hg commit -S -m phasecheck4
1600 committing subrepository s
1600 committing subrepository s
1601 committing subrepository s/ss (glob)
1601 committing subrepository s/ss (glob)
1602 warning: changes are committed in secret phase from subrepository ss
1602 warning: changes are committed in secret phase from subrepository ss
1603 committing subrepository t
1603 committing subrepository t
1604 warning: changes are committed in secret phase from subrepository s
1604 warning: changes are committed in secret phase from subrepository s
1605 created new head
1605 created new head
1606 $ hg -R s/ss phase tip
1606 $ hg -R s/ss phase tip
1607 5: secret
1607 5: secret
1608 $ hg -R s phase tip
1608 $ hg -R s phase tip
1609 8: secret
1609 8: secret
1610 $ hg -R t phase tip
1610 $ hg -R t phase tip
1611 6: draft
1611 6: draft
1612 $ hg phase tip
1612 $ hg phase tip
1613 15: secret
1613 15: secret
1614
1614
1615 $ cd ..
1615 $ cd ..
1616
1616
1617
1617
1618 Test that commit --secret works on both repo and subrepo (issue4182)
1618 Test that commit --secret works on both repo and subrepo (issue4182)
1619
1619
1620 $ cd main
1620 $ cd main
1621 $ echo secret >> b
1621 $ echo secret >> b
1622 $ echo secret >> s/b
1622 $ echo secret >> s/b
1623 $ hg commit --secret --subrepo -m "secret"
1623 $ hg commit --secret --subrepo -m "secret"
1624 committing subrepository s
1624 committing subrepository s
1625 $ hg phase -r .
1625 $ hg phase -r .
1626 6: secret
1626 6: secret
1627 $ cd s
1627 $ cd s
1628 $ hg phase -r .
1628 $ hg phase -r .
1629 6: secret
1629 6: secret
1630 $ cd ../../
1630 $ cd ../../
1631
1631
1632 Test "subrepos" template keyword
1632 Test "subrepos" template keyword
1633
1633
1634 $ cd t
1634 $ cd t
1635 $ hg update -q 15
1635 $ hg update -q 15
1636 $ cat > .hgsub <<EOF
1636 $ cat > .hgsub <<EOF
1637 > s = s
1637 > s = s
1638 > EOF
1638 > EOF
1639 $ hg commit -m "16"
1639 $ hg commit -m "16"
1640 warning: changes are committed in secret phase from subrepository s
1640 warning: changes are committed in secret phase from subrepository s
1641
1641
1642 (addition of ".hgsub" itself)
1642 (addition of ".hgsub" itself)
1643
1643
1644 $ hg diff --nodates -c 1 .hgsubstate
1644 $ hg diff --nodates -c 1 .hgsubstate
1645 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1645 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1646 --- /dev/null
1646 --- /dev/null
1647 +++ b/.hgsubstate
1647 +++ b/.hgsubstate
1648 @@ -0,0 +1,1 @@
1648 @@ -0,0 +1,1 @@
1649 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1649 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1650 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1650 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1651 f7b1eb17ad24 000000000000
1651 f7b1eb17ad24 000000000000
1652 s
1652 s
1653
1653
1654 (modification of existing entry)
1654 (modification of existing entry)
1655
1655
1656 $ hg diff --nodates -c 2 .hgsubstate
1656 $ hg diff --nodates -c 2 .hgsubstate
1657 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1657 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1658 --- a/.hgsubstate
1658 --- a/.hgsubstate
1659 +++ b/.hgsubstate
1659 +++ b/.hgsubstate
1660 @@ -1,1 +1,1 @@
1660 @@ -1,1 +1,1 @@
1661 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1661 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1662 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1662 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1663 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1663 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1664 7cf8cfea66e4 000000000000
1664 7cf8cfea66e4 000000000000
1665 s
1665 s
1666
1666
1667 (addition of entry)
1667 (addition of entry)
1668
1668
1669 $ hg diff --nodates -c 5 .hgsubstate
1669 $ hg diff --nodates -c 5 .hgsubstate
1670 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1670 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1671 --- a/.hgsubstate
1671 --- a/.hgsubstate
1672 +++ b/.hgsubstate
1672 +++ b/.hgsubstate
1673 @@ -1,1 +1,2 @@
1673 @@ -1,1 +1,2 @@
1674 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1674 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1675 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1675 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1676 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1676 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1677 7cf8cfea66e4 000000000000
1677 7cf8cfea66e4 000000000000
1678 t
1678 t
1679
1679
1680 (removal of existing entry)
1680 (removal of existing entry)
1681
1681
1682 $ hg diff --nodates -c 16 .hgsubstate
1682 $ hg diff --nodates -c 16 .hgsubstate
1683 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1683 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1684 --- a/.hgsubstate
1684 --- a/.hgsubstate
1685 +++ b/.hgsubstate
1685 +++ b/.hgsubstate
1686 @@ -1,2 +1,1 @@
1686 @@ -1,2 +1,1 @@
1687 0731af8ca9423976d3743119d0865097c07bdc1b s
1687 0731af8ca9423976d3743119d0865097c07bdc1b s
1688 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1688 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1689 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1689 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1690 8bec38d2bd0b 000000000000
1690 8bec38d2bd0b 000000000000
1691 t
1691 t
1692
1692
1693 (merging)
1693 (merging)
1694
1694
1695 $ hg diff --nodates -c 9 .hgsubstate
1695 $ hg diff --nodates -c 9 .hgsubstate
1696 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1696 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1697 --- a/.hgsubstate
1697 --- a/.hgsubstate
1698 +++ b/.hgsubstate
1698 +++ b/.hgsubstate
1699 @@ -1,1 +1,2 @@
1699 @@ -1,1 +1,2 @@
1700 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1700 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1701 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1701 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1702 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1702 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1703 f6affe3fbfaa 1f14a2e2d3ec
1703 f6affe3fbfaa 1f14a2e2d3ec
1704 t
1704 t
1705
1705
1706 (removal of ".hgsub" itself)
1706 (removal of ".hgsub" itself)
1707
1707
1708 $ hg diff --nodates -c 8 .hgsubstate
1708 $ hg diff --nodates -c 8 .hgsubstate
1709 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1709 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1710 --- a/.hgsubstate
1710 --- a/.hgsubstate
1711 +++ /dev/null
1711 +++ /dev/null
1712 @@ -1,2 +0,0 @@
1712 @@ -1,2 +0,0 @@
1713 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1713 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1714 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1714 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1715 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1715 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1716 f94576341bcf 000000000000
1716 f94576341bcf 000000000000
1717
1717
1718 Test that '[paths]' is configured correctly at subrepo creation
1718 Test that '[paths]' is configured correctly at subrepo creation
1719
1719
1720 $ cd $TESTTMP/tc
1720 $ cd $TESTTMP/tc
1721 $ cat > .hgsub <<EOF
1721 $ cat > .hgsub <<EOF
1722 > # to clear bogus subrepo path 'bogus=[boguspath'
1722 > # to clear bogus subrepo path 'bogus=[boguspath'
1723 > s = s
1723 > s = s
1724 > t = t
1724 > t = t
1725 > EOF
1725 > EOF
1726 $ hg update -q --clean null
1726 $ hg update -q --clean null
1727 $ rm -rf s t
1727 $ rm -rf s t
1728 $ cat >> .hg/hgrc <<EOF
1728 $ cat >> .hg/hgrc <<EOF
1729 > [paths]
1729 > [paths]
1730 > default-push = /foo/bar
1730 > default-push = /foo/bar
1731 > EOF
1731 > EOF
1732 $ hg update -q
1732 $ hg update -q
1733 $ cat s/.hg/hgrc
1733 $ cat s/.hg/hgrc
1734 [paths]
1734 [paths]
1735 default = $TESTTMP/t/s
1735 default = $TESTTMP/t/s
1736 default-push = /foo/bar/s
1736 default-push = /foo/bar/s
1737 $ cat s/ss/.hg/hgrc
1737 $ cat s/ss/.hg/hgrc
1738 [paths]
1738 [paths]
1739 default = $TESTTMP/t/s/ss
1739 default = $TESTTMP/t/s/ss
1740 default-push = /foo/bar/s/ss
1740 default-push = /foo/bar/s/ss
1741 $ cat t/.hg/hgrc
1741 $ cat t/.hg/hgrc
1742 [paths]
1742 [paths]
1743 default = $TESTTMP/t/t
1743 default = $TESTTMP/t/t
1744 default-push = /foo/bar/t
1744 default-push = /foo/bar/t
1745
1745
1746 $ cd $TESTTMP/t
1746 $ cd $TESTTMP/t
1747 $ hg up -qC 0
1747 $ hg up -qC 0
1748 $ echo 'bar' > bar.txt
1748 $ echo 'bar' > bar.txt
1749 $ hg ci -Am 'branch before subrepo add'
1749 $ hg ci -Am 'branch before subrepo add'
1750 adding bar.txt
1750 adding bar.txt
1751 created new head
1751 created new head
1752 $ hg merge -r "first(subrepo('s'))"
1752 $ hg merge -r "first(subrepo('s'))"
1753 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1753 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1754 (branch merge, don't forget to commit)
1754 (branch merge, don't forget to commit)
1755 $ hg status -S -X '.hgsub*'
1755 $ hg status -S -X '.hgsub*'
1756 A s/a
1756 A s/a
1757 ? s/b
1757 ? s/b
1758 ? s/c
1758 ? s/c
1759 ? s/f1
1759 ? s/f1
1760 $ hg status -S --rev 'p2()'
1760 $ hg status -S --rev 'p2()'
1761 A bar.txt
1761 A bar.txt
1762 ? s/b
1762 ? s/b
1763 ? s/c
1763 ? s/c
1764 ? s/f1
1764 ? s/f1
1765 $ hg diff -S -X '.hgsub*' --nodates
1765 $ hg diff -S -X '.hgsub*' --nodates
1766 diff -r 000000000000 s/a
1766 diff -r 000000000000 s/a
1767 --- /dev/null
1767 --- /dev/null
1768 +++ b/s/a
1768 +++ b/s/a
1769 @@ -0,0 +1,1 @@
1769 @@ -0,0 +1,1 @@
1770 +a
1770 +a
1771 $ hg diff -S --rev 'p2()' --nodates
1771 $ hg diff -S --rev 'p2()' --nodates
1772 diff -r 7cf8cfea66e4 bar.txt
1772 diff -r 7cf8cfea66e4 bar.txt
1773 --- /dev/null
1773 --- /dev/null
1774 +++ b/bar.txt
1774 +++ b/bar.txt
1775 @@ -0,0 +1,1 @@
1775 @@ -0,0 +1,1 @@
1776 +bar
1776 +bar
1777
1777
1778 $ cd ..
1778 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now