##// END OF EJS Templates
subrepo: add dirtyreason to centralize composing dirty reason message...
FUJIWARA Katsunori -
r24470:76b0b0fe default
parent child Browse files
Show More
@@ -1,1923 +1,1923 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-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 from node import hex, nullid, short
7 from node import hex, nullid, short
8 from i18n import _
8 from i18n import _
9 import urllib
9 import urllib
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
12 import lock as lockmod
12 import lock as lockmod
13 import transaction, store, encoding, exchange, bundle2
13 import transaction, store, encoding, exchange, bundle2
14 import scmutil, util, extensions, hook, error, revset
14 import scmutil, util, extensions, hook, error, revset
15 import match as matchmod
15 import match as matchmod
16 import merge as mergemod
16 import merge as mergemod
17 import tags as tagsmod
17 import tags as tagsmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect
19 import weakref, errno, os, time, inspect
20 import branchmap, pathutil
20 import branchmap, pathutil
21 import namespaces
21 import namespaces
22 propertycache = util.propertycache
22 propertycache = util.propertycache
23 filecache = scmutil.filecache
23 filecache = scmutil.filecache
24
24
25 class repofilecache(filecache):
25 class repofilecache(filecache):
26 """All filecache usage on repo are done for logic that should be unfiltered
26 """All filecache usage on repo are done for logic that should be unfiltered
27 """
27 """
28
28
29 def __get__(self, repo, type=None):
29 def __get__(self, repo, type=None):
30 return super(repofilecache, self).__get__(repo.unfiltered(), type)
30 return super(repofilecache, self).__get__(repo.unfiltered(), type)
31 def __set__(self, repo, value):
31 def __set__(self, repo, value):
32 return super(repofilecache, self).__set__(repo.unfiltered(), value)
32 return super(repofilecache, self).__set__(repo.unfiltered(), value)
33 def __delete__(self, repo):
33 def __delete__(self, repo):
34 return super(repofilecache, self).__delete__(repo.unfiltered())
34 return super(repofilecache, self).__delete__(repo.unfiltered())
35
35
36 class storecache(repofilecache):
36 class storecache(repofilecache):
37 """filecache for files in the store"""
37 """filecache for files in the store"""
38 def join(self, obj, fname):
38 def join(self, obj, fname):
39 return obj.sjoin(fname)
39 return obj.sjoin(fname)
40
40
41 class unfilteredpropertycache(propertycache):
41 class unfilteredpropertycache(propertycache):
42 """propertycache that apply to unfiltered repo only"""
42 """propertycache that apply to unfiltered repo only"""
43
43
44 def __get__(self, repo, type=None):
44 def __get__(self, repo, type=None):
45 unfi = repo.unfiltered()
45 unfi = repo.unfiltered()
46 if unfi is repo:
46 if unfi is repo:
47 return super(unfilteredpropertycache, self).__get__(unfi)
47 return super(unfilteredpropertycache, self).__get__(unfi)
48 return getattr(unfi, self.name)
48 return getattr(unfi, self.name)
49
49
50 class filteredpropertycache(propertycache):
50 class filteredpropertycache(propertycache):
51 """propertycache that must take filtering in account"""
51 """propertycache that must take filtering in account"""
52
52
53 def cachevalue(self, obj, value):
53 def cachevalue(self, obj, value):
54 object.__setattr__(obj, self.name, value)
54 object.__setattr__(obj, self.name, value)
55
55
56
56
57 def hasunfilteredcache(repo, name):
57 def hasunfilteredcache(repo, name):
58 """check if a repo has an unfilteredpropertycache value for <name>"""
58 """check if a repo has an unfilteredpropertycache value for <name>"""
59 return name in vars(repo.unfiltered())
59 return name in vars(repo.unfiltered())
60
60
61 def unfilteredmethod(orig):
61 def unfilteredmethod(orig):
62 """decorate method that always need to be run on unfiltered version"""
62 """decorate method that always need to be run on unfiltered version"""
63 def wrapper(repo, *args, **kwargs):
63 def wrapper(repo, *args, **kwargs):
64 return orig(repo.unfiltered(), *args, **kwargs)
64 return orig(repo.unfiltered(), *args, **kwargs)
65 return wrapper
65 return wrapper
66
66
67 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
67 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
68 'unbundle'))
68 'unbundle'))
69 legacycaps = moderncaps.union(set(['changegroupsubset']))
69 legacycaps = moderncaps.union(set(['changegroupsubset']))
70
70
71 class localpeer(peer.peerrepository):
71 class localpeer(peer.peerrepository):
72 '''peer for a local repo; reflects only the most recent API'''
72 '''peer for a local repo; reflects only the most recent API'''
73
73
74 def __init__(self, repo, caps=moderncaps):
74 def __init__(self, repo, caps=moderncaps):
75 peer.peerrepository.__init__(self)
75 peer.peerrepository.__init__(self)
76 self._repo = repo.filtered('served')
76 self._repo = repo.filtered('served')
77 self.ui = repo.ui
77 self.ui = repo.ui
78 self._caps = repo._restrictcapabilities(caps)
78 self._caps = repo._restrictcapabilities(caps)
79 self.requirements = repo.requirements
79 self.requirements = repo.requirements
80 self.supportedformats = repo.supportedformats
80 self.supportedformats = repo.supportedformats
81
81
82 def close(self):
82 def close(self):
83 self._repo.close()
83 self._repo.close()
84
84
85 def _capabilities(self):
85 def _capabilities(self):
86 return self._caps
86 return self._caps
87
87
88 def local(self):
88 def local(self):
89 return self._repo
89 return self._repo
90
90
91 def canpush(self):
91 def canpush(self):
92 return True
92 return True
93
93
94 def url(self):
94 def url(self):
95 return self._repo.url()
95 return self._repo.url()
96
96
97 def lookup(self, key):
97 def lookup(self, key):
98 return self._repo.lookup(key)
98 return self._repo.lookup(key)
99
99
100 def branchmap(self):
100 def branchmap(self):
101 return self._repo.branchmap()
101 return self._repo.branchmap()
102
102
103 def heads(self):
103 def heads(self):
104 return self._repo.heads()
104 return self._repo.heads()
105
105
106 def known(self, nodes):
106 def known(self, nodes):
107 return self._repo.known(nodes)
107 return self._repo.known(nodes)
108
108
109 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
109 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
110 format='HG10', **kwargs):
110 format='HG10', **kwargs):
111 cg = exchange.getbundle(self._repo, source, heads=heads,
111 cg = exchange.getbundle(self._repo, source, heads=heads,
112 common=common, bundlecaps=bundlecaps, **kwargs)
112 common=common, bundlecaps=bundlecaps, **kwargs)
113 if bundlecaps is not None and 'HG2Y' in bundlecaps:
113 if bundlecaps is not None and 'HG2Y' in bundlecaps:
114 # When requesting a bundle2, getbundle returns a stream to make the
114 # When requesting a bundle2, getbundle returns a stream to make the
115 # wire level function happier. We need to build a proper object
115 # wire level function happier. We need to build a proper object
116 # from it in local peer.
116 # from it in local peer.
117 cg = bundle2.unbundle20(self.ui, cg)
117 cg = bundle2.unbundle20(self.ui, cg)
118 return cg
118 return cg
119
119
120 # TODO We might want to move the next two calls into legacypeer and add
120 # TODO We might want to move the next two calls into legacypeer and add
121 # unbundle instead.
121 # unbundle instead.
122
122
123 def unbundle(self, cg, heads, url):
123 def unbundle(self, cg, heads, url):
124 """apply a bundle on a repo
124 """apply a bundle on a repo
125
125
126 This function handles the repo locking itself."""
126 This function handles the repo locking itself."""
127 try:
127 try:
128 cg = exchange.readbundle(self.ui, cg, None)
128 cg = exchange.readbundle(self.ui, cg, None)
129 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
129 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
130 if util.safehasattr(ret, 'getchunks'):
130 if util.safehasattr(ret, 'getchunks'):
131 # This is a bundle20 object, turn it into an unbundler.
131 # This is a bundle20 object, turn it into an unbundler.
132 # This little dance should be dropped eventually when the API
132 # This little dance should be dropped eventually when the API
133 # is finally improved.
133 # is finally improved.
134 stream = util.chunkbuffer(ret.getchunks())
134 stream = util.chunkbuffer(ret.getchunks())
135 ret = bundle2.unbundle20(self.ui, stream)
135 ret = bundle2.unbundle20(self.ui, stream)
136 return ret
136 return ret
137 except error.PushRaced, exc:
137 except error.PushRaced, exc:
138 raise error.ResponseError(_('push failed:'), str(exc))
138 raise error.ResponseError(_('push failed:'), str(exc))
139
139
140 def lock(self):
140 def lock(self):
141 return self._repo.lock()
141 return self._repo.lock()
142
142
143 def addchangegroup(self, cg, source, url):
143 def addchangegroup(self, cg, source, url):
144 return changegroup.addchangegroup(self._repo, cg, source, url)
144 return changegroup.addchangegroup(self._repo, cg, source, url)
145
145
146 def pushkey(self, namespace, key, old, new):
146 def pushkey(self, namespace, key, old, new):
147 return self._repo.pushkey(namespace, key, old, new)
147 return self._repo.pushkey(namespace, key, old, new)
148
148
149 def listkeys(self, namespace):
149 def listkeys(self, namespace):
150 return self._repo.listkeys(namespace)
150 return self._repo.listkeys(namespace)
151
151
152 def debugwireargs(self, one, two, three=None, four=None, five=None):
152 def debugwireargs(self, one, two, three=None, four=None, five=None):
153 '''used to test argument passing over the wire'''
153 '''used to test argument passing over the wire'''
154 return "%s %s %s %s %s" % (one, two, three, four, five)
154 return "%s %s %s %s %s" % (one, two, three, four, five)
155
155
156 class locallegacypeer(localpeer):
156 class locallegacypeer(localpeer):
157 '''peer extension which implements legacy methods too; used for tests with
157 '''peer extension which implements legacy methods too; used for tests with
158 restricted capabilities'''
158 restricted capabilities'''
159
159
160 def __init__(self, repo):
160 def __init__(self, repo):
161 localpeer.__init__(self, repo, caps=legacycaps)
161 localpeer.__init__(self, repo, caps=legacycaps)
162
162
163 def branches(self, nodes):
163 def branches(self, nodes):
164 return self._repo.branches(nodes)
164 return self._repo.branches(nodes)
165
165
166 def between(self, pairs):
166 def between(self, pairs):
167 return self._repo.between(pairs)
167 return self._repo.between(pairs)
168
168
169 def changegroup(self, basenodes, source):
169 def changegroup(self, basenodes, source):
170 return changegroup.changegroup(self._repo, basenodes, source)
170 return changegroup.changegroup(self._repo, basenodes, source)
171
171
172 def changegroupsubset(self, bases, heads, source):
172 def changegroupsubset(self, bases, heads, source):
173 return changegroup.changegroupsubset(self._repo, bases, heads, source)
173 return changegroup.changegroupsubset(self._repo, bases, heads, source)
174
174
175 class localrepository(object):
175 class localrepository(object):
176
176
177 supportedformats = set(('revlogv1', 'generaldelta'))
177 supportedformats = set(('revlogv1', 'generaldelta'))
178 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
178 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
179 'dotencode'))
179 'dotencode'))
180 openerreqs = set(('revlogv1', 'generaldelta'))
180 openerreqs = set(('revlogv1', 'generaldelta'))
181 requirements = ['revlogv1']
181 requirements = ['revlogv1']
182 filtername = None
182 filtername = None
183
183
184 # a list of (ui, featureset) functions.
184 # a list of (ui, featureset) functions.
185 # only functions defined in module of enabled extensions are invoked
185 # only functions defined in module of enabled extensions are invoked
186 featuresetupfuncs = set()
186 featuresetupfuncs = set()
187
187
188 def _baserequirements(self, create):
188 def _baserequirements(self, create):
189 return self.requirements[:]
189 return self.requirements[:]
190
190
191 def __init__(self, baseui, path=None, create=False):
191 def __init__(self, baseui, path=None, create=False):
192 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
192 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
193 self.wopener = self.wvfs
193 self.wopener = self.wvfs
194 self.root = self.wvfs.base
194 self.root = self.wvfs.base
195 self.path = self.wvfs.join(".hg")
195 self.path = self.wvfs.join(".hg")
196 self.origroot = path
196 self.origroot = path
197 self.auditor = pathutil.pathauditor(self.root, self._checknested)
197 self.auditor = pathutil.pathauditor(self.root, self._checknested)
198 self.vfs = scmutil.vfs(self.path)
198 self.vfs = scmutil.vfs(self.path)
199 self.opener = self.vfs
199 self.opener = self.vfs
200 self.baseui = baseui
200 self.baseui = baseui
201 self.ui = baseui.copy()
201 self.ui = baseui.copy()
202 self.ui.copy = baseui.copy # prevent copying repo configuration
202 self.ui.copy = baseui.copy # prevent copying repo configuration
203 # A list of callback to shape the phase if no data were found.
203 # A list of callback to shape the phase if no data were found.
204 # Callback are in the form: func(repo, roots) --> processed root.
204 # Callback are in the form: func(repo, roots) --> processed root.
205 # This list it to be filled by extension during repo setup
205 # This list it to be filled by extension during repo setup
206 self._phasedefaults = []
206 self._phasedefaults = []
207 try:
207 try:
208 self.ui.readconfig(self.join("hgrc"), self.root)
208 self.ui.readconfig(self.join("hgrc"), self.root)
209 extensions.loadall(self.ui)
209 extensions.loadall(self.ui)
210 except IOError:
210 except IOError:
211 pass
211 pass
212
212
213 if self.featuresetupfuncs:
213 if self.featuresetupfuncs:
214 self.supported = set(self._basesupported) # use private copy
214 self.supported = set(self._basesupported) # use private copy
215 extmods = set(m.__name__ for n, m
215 extmods = set(m.__name__ for n, m
216 in extensions.extensions(self.ui))
216 in extensions.extensions(self.ui))
217 for setupfunc in self.featuresetupfuncs:
217 for setupfunc in self.featuresetupfuncs:
218 if setupfunc.__module__ in extmods:
218 if setupfunc.__module__ in extmods:
219 setupfunc(self.ui, self.supported)
219 setupfunc(self.ui, self.supported)
220 else:
220 else:
221 self.supported = self._basesupported
221 self.supported = self._basesupported
222
222
223 if not self.vfs.isdir():
223 if not self.vfs.isdir():
224 if create:
224 if create:
225 if not self.wvfs.exists():
225 if not self.wvfs.exists():
226 self.wvfs.makedirs()
226 self.wvfs.makedirs()
227 self.vfs.makedir(notindexed=True)
227 self.vfs.makedir(notindexed=True)
228 requirements = self._baserequirements(create)
228 requirements = self._baserequirements(create)
229 if self.ui.configbool('format', 'usestore', True):
229 if self.ui.configbool('format', 'usestore', True):
230 self.vfs.mkdir("store")
230 self.vfs.mkdir("store")
231 requirements.append("store")
231 requirements.append("store")
232 if self.ui.configbool('format', 'usefncache', True):
232 if self.ui.configbool('format', 'usefncache', True):
233 requirements.append("fncache")
233 requirements.append("fncache")
234 if self.ui.configbool('format', 'dotencode', True):
234 if self.ui.configbool('format', 'dotencode', True):
235 requirements.append('dotencode')
235 requirements.append('dotencode')
236 # create an invalid changelog
236 # create an invalid changelog
237 self.vfs.append(
237 self.vfs.append(
238 "00changelog.i",
238 "00changelog.i",
239 '\0\0\0\2' # represents revlogv2
239 '\0\0\0\2' # represents revlogv2
240 ' dummy changelog to prevent using the old repo layout'
240 ' dummy changelog to prevent using the old repo layout'
241 )
241 )
242 if self.ui.configbool('format', 'generaldelta', False):
242 if self.ui.configbool('format', 'generaldelta', False):
243 requirements.append("generaldelta")
243 requirements.append("generaldelta")
244 requirements = set(requirements)
244 requirements = set(requirements)
245 else:
245 else:
246 raise error.RepoError(_("repository %s not found") % path)
246 raise error.RepoError(_("repository %s not found") % path)
247 elif create:
247 elif create:
248 raise error.RepoError(_("repository %s already exists") % path)
248 raise error.RepoError(_("repository %s already exists") % path)
249 else:
249 else:
250 try:
250 try:
251 requirements = scmutil.readrequires(self.vfs, self.supported)
251 requirements = scmutil.readrequires(self.vfs, self.supported)
252 except IOError, inst:
252 except IOError, inst:
253 if inst.errno != errno.ENOENT:
253 if inst.errno != errno.ENOENT:
254 raise
254 raise
255 requirements = set()
255 requirements = set()
256
256
257 self.sharedpath = self.path
257 self.sharedpath = self.path
258 try:
258 try:
259 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
259 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
260 realpath=True)
260 realpath=True)
261 s = vfs.base
261 s = vfs.base
262 if not vfs.exists():
262 if not vfs.exists():
263 raise error.RepoError(
263 raise error.RepoError(
264 _('.hg/sharedpath points to nonexistent directory %s') % s)
264 _('.hg/sharedpath points to nonexistent directory %s') % s)
265 self.sharedpath = s
265 self.sharedpath = s
266 except IOError, inst:
266 except IOError, inst:
267 if inst.errno != errno.ENOENT:
267 if inst.errno != errno.ENOENT:
268 raise
268 raise
269
269
270 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
270 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
271 self.spath = self.store.path
271 self.spath = self.store.path
272 self.svfs = self.store.vfs
272 self.svfs = self.store.vfs
273 self.sopener = self.svfs
273 self.sopener = self.svfs
274 self.sjoin = self.store.join
274 self.sjoin = self.store.join
275 self.vfs.createmode = self.store.createmode
275 self.vfs.createmode = self.store.createmode
276 self._applyrequirements(requirements)
276 self._applyrequirements(requirements)
277 if create:
277 if create:
278 self._writerequirements()
278 self._writerequirements()
279
279
280
280
281 self._branchcaches = {}
281 self._branchcaches = {}
282 self._revbranchcache = None
282 self._revbranchcache = None
283 self.filterpats = {}
283 self.filterpats = {}
284 self._datafilters = {}
284 self._datafilters = {}
285 self._transref = self._lockref = self._wlockref = None
285 self._transref = self._lockref = self._wlockref = None
286
286
287 # A cache for various files under .hg/ that tracks file changes,
287 # A cache for various files under .hg/ that tracks file changes,
288 # (used by the filecache decorator)
288 # (used by the filecache decorator)
289 #
289 #
290 # Maps a property name to its util.filecacheentry
290 # Maps a property name to its util.filecacheentry
291 self._filecache = {}
291 self._filecache = {}
292
292
293 # hold sets of revision to be filtered
293 # hold sets of revision to be filtered
294 # should be cleared when something might have changed the filter value:
294 # should be cleared when something might have changed the filter value:
295 # - new changesets,
295 # - new changesets,
296 # - phase change,
296 # - phase change,
297 # - new obsolescence marker,
297 # - new obsolescence marker,
298 # - working directory parent change,
298 # - working directory parent change,
299 # - bookmark changes
299 # - bookmark changes
300 self.filteredrevcache = {}
300 self.filteredrevcache = {}
301
301
302 # generic mapping between names and nodes
302 # generic mapping between names and nodes
303 self.names = namespaces.namespaces()
303 self.names = namespaces.namespaces()
304
304
305 def close(self):
305 def close(self):
306 self._writecaches()
306 self._writecaches()
307
307
308 def _writecaches(self):
308 def _writecaches(self):
309 if self._revbranchcache:
309 if self._revbranchcache:
310 self._revbranchcache.write()
310 self._revbranchcache.write()
311
311
312 def _restrictcapabilities(self, caps):
312 def _restrictcapabilities(self, caps):
313 # bundle2 is not ready for prime time, drop it unless explicitly
313 # bundle2 is not ready for prime time, drop it unless explicitly
314 # required by the tests (or some brave tester)
314 # required by the tests (or some brave tester)
315 if self.ui.configbool('experimental', 'bundle2-exp', False):
315 if self.ui.configbool('experimental', 'bundle2-exp', False):
316 caps = set(caps)
316 caps = set(caps)
317 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
317 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
318 caps.add('bundle2-exp=' + urllib.quote(capsblob))
318 caps.add('bundle2-exp=' + urllib.quote(capsblob))
319 return caps
319 return caps
320
320
321 def _applyrequirements(self, requirements):
321 def _applyrequirements(self, requirements):
322 self.requirements = requirements
322 self.requirements = requirements
323 self.svfs.options = dict((r, 1) for r in requirements
323 self.svfs.options = dict((r, 1) for r in requirements
324 if r in self.openerreqs)
324 if r in self.openerreqs)
325 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
325 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
326 if chunkcachesize is not None:
326 if chunkcachesize is not None:
327 self.svfs.options['chunkcachesize'] = chunkcachesize
327 self.svfs.options['chunkcachesize'] = chunkcachesize
328 maxchainlen = self.ui.configint('format', 'maxchainlen')
328 maxchainlen = self.ui.configint('format', 'maxchainlen')
329 if maxchainlen is not None:
329 if maxchainlen is not None:
330 self.svfs.options['maxchainlen'] = maxchainlen
330 self.svfs.options['maxchainlen'] = maxchainlen
331 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
331 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
332 if manifestcachesize is not None:
332 if manifestcachesize is not None:
333 self.svfs.options['manifestcachesize'] = manifestcachesize
333 self.svfs.options['manifestcachesize'] = manifestcachesize
334 usetreemanifest = self.ui.configbool('experimental', 'treemanifest')
334 usetreemanifest = self.ui.configbool('experimental', 'treemanifest')
335 if usetreemanifest is not None:
335 if usetreemanifest is not None:
336 self.svfs.options['usetreemanifest'] = usetreemanifest
336 self.svfs.options['usetreemanifest'] = usetreemanifest
337
337
338 def _writerequirements(self):
338 def _writerequirements(self):
339 reqfile = self.vfs("requires", "w")
339 reqfile = self.vfs("requires", "w")
340 for r in sorted(self.requirements):
340 for r in sorted(self.requirements):
341 reqfile.write("%s\n" % r)
341 reqfile.write("%s\n" % r)
342 reqfile.close()
342 reqfile.close()
343
343
344 def _checknested(self, path):
344 def _checknested(self, path):
345 """Determine if path is a legal nested repository."""
345 """Determine if path is a legal nested repository."""
346 if not path.startswith(self.root):
346 if not path.startswith(self.root):
347 return False
347 return False
348 subpath = path[len(self.root) + 1:]
348 subpath = path[len(self.root) + 1:]
349 normsubpath = util.pconvert(subpath)
349 normsubpath = util.pconvert(subpath)
350
350
351 # XXX: Checking against the current working copy is wrong in
351 # XXX: Checking against the current working copy is wrong in
352 # the sense that it can reject things like
352 # the sense that it can reject things like
353 #
353 #
354 # $ hg cat -r 10 sub/x.txt
354 # $ hg cat -r 10 sub/x.txt
355 #
355 #
356 # if sub/ is no longer a subrepository in the working copy
356 # if sub/ is no longer a subrepository in the working copy
357 # parent revision.
357 # parent revision.
358 #
358 #
359 # However, it can of course also allow things that would have
359 # However, it can of course also allow things that would have
360 # been rejected before, such as the above cat command if sub/
360 # been rejected before, such as the above cat command if sub/
361 # is a subrepository now, but was a normal directory before.
361 # is a subrepository now, but was a normal directory before.
362 # The old path auditor would have rejected by mistake since it
362 # The old path auditor would have rejected by mistake since it
363 # panics when it sees sub/.hg/.
363 # panics when it sees sub/.hg/.
364 #
364 #
365 # All in all, checking against the working copy seems sensible
365 # All in all, checking against the working copy seems sensible
366 # since we want to prevent access to nested repositories on
366 # since we want to prevent access to nested repositories on
367 # the filesystem *now*.
367 # the filesystem *now*.
368 ctx = self[None]
368 ctx = self[None]
369 parts = util.splitpath(subpath)
369 parts = util.splitpath(subpath)
370 while parts:
370 while parts:
371 prefix = '/'.join(parts)
371 prefix = '/'.join(parts)
372 if prefix in ctx.substate:
372 if prefix in ctx.substate:
373 if prefix == normsubpath:
373 if prefix == normsubpath:
374 return True
374 return True
375 else:
375 else:
376 sub = ctx.sub(prefix)
376 sub = ctx.sub(prefix)
377 return sub.checknested(subpath[len(prefix) + 1:])
377 return sub.checknested(subpath[len(prefix) + 1:])
378 else:
378 else:
379 parts.pop()
379 parts.pop()
380 return False
380 return False
381
381
382 def peer(self):
382 def peer(self):
383 return localpeer(self) # not cached to avoid reference cycle
383 return localpeer(self) # not cached to avoid reference cycle
384
384
385 def unfiltered(self):
385 def unfiltered(self):
386 """Return unfiltered version of the repository
386 """Return unfiltered version of the repository
387
387
388 Intended to be overwritten by filtered repo."""
388 Intended to be overwritten by filtered repo."""
389 return self
389 return self
390
390
391 def filtered(self, name):
391 def filtered(self, name):
392 """Return a filtered version of a repository"""
392 """Return a filtered version of a repository"""
393 # build a new class with the mixin and the current class
393 # build a new class with the mixin and the current class
394 # (possibly subclass of the repo)
394 # (possibly subclass of the repo)
395 class proxycls(repoview.repoview, self.unfiltered().__class__):
395 class proxycls(repoview.repoview, self.unfiltered().__class__):
396 pass
396 pass
397 return proxycls(self, name)
397 return proxycls(self, name)
398
398
399 @repofilecache('bookmarks')
399 @repofilecache('bookmarks')
400 def _bookmarks(self):
400 def _bookmarks(self):
401 return bookmarks.bmstore(self)
401 return bookmarks.bmstore(self)
402
402
403 @repofilecache('bookmarks.current')
403 @repofilecache('bookmarks.current')
404 def _bookmarkcurrent(self):
404 def _bookmarkcurrent(self):
405 return bookmarks.readcurrent(self)
405 return bookmarks.readcurrent(self)
406
406
407 def bookmarkheads(self, bookmark):
407 def bookmarkheads(self, bookmark):
408 name = bookmark.split('@', 1)[0]
408 name = bookmark.split('@', 1)[0]
409 heads = []
409 heads = []
410 for mark, n in self._bookmarks.iteritems():
410 for mark, n in self._bookmarks.iteritems():
411 if mark.split('@', 1)[0] == name:
411 if mark.split('@', 1)[0] == name:
412 heads.append(n)
412 heads.append(n)
413 return heads
413 return heads
414
414
415 @storecache('phaseroots')
415 @storecache('phaseroots')
416 def _phasecache(self):
416 def _phasecache(self):
417 return phases.phasecache(self, self._phasedefaults)
417 return phases.phasecache(self, self._phasedefaults)
418
418
419 @storecache('obsstore')
419 @storecache('obsstore')
420 def obsstore(self):
420 def obsstore(self):
421 # read default format for new obsstore.
421 # read default format for new obsstore.
422 defaultformat = self.ui.configint('format', 'obsstore-version', None)
422 defaultformat = self.ui.configint('format', 'obsstore-version', None)
423 # rely on obsstore class default when possible.
423 # rely on obsstore class default when possible.
424 kwargs = {}
424 kwargs = {}
425 if defaultformat is not None:
425 if defaultformat is not None:
426 kwargs['defaultformat'] = defaultformat
426 kwargs['defaultformat'] = defaultformat
427 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
427 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
428 store = obsolete.obsstore(self.svfs, readonly=readonly,
428 store = obsolete.obsstore(self.svfs, readonly=readonly,
429 **kwargs)
429 **kwargs)
430 if store and readonly:
430 if store and readonly:
431 self.ui.warn(
431 self.ui.warn(
432 _('obsolete feature not enabled but %i markers found!\n')
432 _('obsolete feature not enabled but %i markers found!\n')
433 % len(list(store)))
433 % len(list(store)))
434 return store
434 return store
435
435
436 @storecache('00changelog.i')
436 @storecache('00changelog.i')
437 def changelog(self):
437 def changelog(self):
438 c = changelog.changelog(self.svfs)
438 c = changelog.changelog(self.svfs)
439 if 'HG_PENDING' in os.environ:
439 if 'HG_PENDING' in os.environ:
440 p = os.environ['HG_PENDING']
440 p = os.environ['HG_PENDING']
441 if p.startswith(self.root):
441 if p.startswith(self.root):
442 c.readpending('00changelog.i.a')
442 c.readpending('00changelog.i.a')
443 return c
443 return c
444
444
445 @storecache('00manifest.i')
445 @storecache('00manifest.i')
446 def manifest(self):
446 def manifest(self):
447 return manifest.manifest(self.svfs)
447 return manifest.manifest(self.svfs)
448
448
449 @repofilecache('dirstate')
449 @repofilecache('dirstate')
450 def dirstate(self):
450 def dirstate(self):
451 warned = [0]
451 warned = [0]
452 def validate(node):
452 def validate(node):
453 try:
453 try:
454 self.changelog.rev(node)
454 self.changelog.rev(node)
455 return node
455 return node
456 except error.LookupError:
456 except error.LookupError:
457 if not warned[0]:
457 if not warned[0]:
458 warned[0] = True
458 warned[0] = True
459 self.ui.warn(_("warning: ignoring unknown"
459 self.ui.warn(_("warning: ignoring unknown"
460 " working parent %s!\n") % short(node))
460 " working parent %s!\n") % short(node))
461 return nullid
461 return nullid
462
462
463 return dirstate.dirstate(self.vfs, self.ui, self.root, validate)
463 return dirstate.dirstate(self.vfs, self.ui, self.root, validate)
464
464
465 def __getitem__(self, changeid):
465 def __getitem__(self, changeid):
466 if changeid is None:
466 if changeid is None:
467 return context.workingctx(self)
467 return context.workingctx(self)
468 if isinstance(changeid, slice):
468 if isinstance(changeid, slice):
469 return [context.changectx(self, i)
469 return [context.changectx(self, i)
470 for i in xrange(*changeid.indices(len(self)))
470 for i in xrange(*changeid.indices(len(self)))
471 if i not in self.changelog.filteredrevs]
471 if i not in self.changelog.filteredrevs]
472 return context.changectx(self, changeid)
472 return context.changectx(self, changeid)
473
473
474 def __contains__(self, changeid):
474 def __contains__(self, changeid):
475 try:
475 try:
476 self[changeid]
476 self[changeid]
477 return True
477 return True
478 except error.RepoLookupError:
478 except error.RepoLookupError:
479 return False
479 return False
480
480
481 def __nonzero__(self):
481 def __nonzero__(self):
482 return True
482 return True
483
483
484 def __len__(self):
484 def __len__(self):
485 return len(self.changelog)
485 return len(self.changelog)
486
486
487 def __iter__(self):
487 def __iter__(self):
488 return iter(self.changelog)
488 return iter(self.changelog)
489
489
490 def revs(self, expr, *args):
490 def revs(self, expr, *args):
491 '''Return a list of revisions matching the given revset'''
491 '''Return a list of revisions matching the given revset'''
492 expr = revset.formatspec(expr, *args)
492 expr = revset.formatspec(expr, *args)
493 m = revset.match(None, expr)
493 m = revset.match(None, expr)
494 return m(self)
494 return m(self)
495
495
496 def set(self, expr, *args):
496 def set(self, expr, *args):
497 '''
497 '''
498 Yield a context for each matching revision, after doing arg
498 Yield a context for each matching revision, after doing arg
499 replacement via revset.formatspec
499 replacement via revset.formatspec
500 '''
500 '''
501 for r in self.revs(expr, *args):
501 for r in self.revs(expr, *args):
502 yield self[r]
502 yield self[r]
503
503
504 def url(self):
504 def url(self):
505 return 'file:' + self.root
505 return 'file:' + self.root
506
506
507 def hook(self, name, throw=False, **args):
507 def hook(self, name, throw=False, **args):
508 """Call a hook, passing this repo instance.
508 """Call a hook, passing this repo instance.
509
509
510 This a convenience method to aid invoking hooks. Extensions likely
510 This a convenience method to aid invoking hooks. Extensions likely
511 won't call this unless they have registered a custom hook or are
511 won't call this unless they have registered a custom hook or are
512 replacing code that is expected to call a hook.
512 replacing code that is expected to call a hook.
513 """
513 """
514 return hook.hook(self.ui, self, name, throw, **args)
514 return hook.hook(self.ui, self, name, throw, **args)
515
515
516 @unfilteredmethod
516 @unfilteredmethod
517 def _tag(self, names, node, message, local, user, date, extra={},
517 def _tag(self, names, node, message, local, user, date, extra={},
518 editor=False):
518 editor=False):
519 if isinstance(names, str):
519 if isinstance(names, str):
520 names = (names,)
520 names = (names,)
521
521
522 branches = self.branchmap()
522 branches = self.branchmap()
523 for name in names:
523 for name in names:
524 self.hook('pretag', throw=True, node=hex(node), tag=name,
524 self.hook('pretag', throw=True, node=hex(node), tag=name,
525 local=local)
525 local=local)
526 if name in branches:
526 if name in branches:
527 self.ui.warn(_("warning: tag %s conflicts with existing"
527 self.ui.warn(_("warning: tag %s conflicts with existing"
528 " branch name\n") % name)
528 " branch name\n") % name)
529
529
530 def writetags(fp, names, munge, prevtags):
530 def writetags(fp, names, munge, prevtags):
531 fp.seek(0, 2)
531 fp.seek(0, 2)
532 if prevtags and prevtags[-1] != '\n':
532 if prevtags and prevtags[-1] != '\n':
533 fp.write('\n')
533 fp.write('\n')
534 for name in names:
534 for name in names:
535 if munge:
535 if munge:
536 m = munge(name)
536 m = munge(name)
537 else:
537 else:
538 m = name
538 m = name
539
539
540 if (self._tagscache.tagtypes and
540 if (self._tagscache.tagtypes and
541 name in self._tagscache.tagtypes):
541 name in self._tagscache.tagtypes):
542 old = self.tags().get(name, nullid)
542 old = self.tags().get(name, nullid)
543 fp.write('%s %s\n' % (hex(old), m))
543 fp.write('%s %s\n' % (hex(old), m))
544 fp.write('%s %s\n' % (hex(node), m))
544 fp.write('%s %s\n' % (hex(node), m))
545 fp.close()
545 fp.close()
546
546
547 prevtags = ''
547 prevtags = ''
548 if local:
548 if local:
549 try:
549 try:
550 fp = self.vfs('localtags', 'r+')
550 fp = self.vfs('localtags', 'r+')
551 except IOError:
551 except IOError:
552 fp = self.vfs('localtags', 'a')
552 fp = self.vfs('localtags', 'a')
553 else:
553 else:
554 prevtags = fp.read()
554 prevtags = fp.read()
555
555
556 # local tags are stored in the current charset
556 # local tags are stored in the current charset
557 writetags(fp, names, None, prevtags)
557 writetags(fp, names, None, prevtags)
558 for name in names:
558 for name in names:
559 self.hook('tag', node=hex(node), tag=name, local=local)
559 self.hook('tag', node=hex(node), tag=name, local=local)
560 return
560 return
561
561
562 try:
562 try:
563 fp = self.wfile('.hgtags', 'rb+')
563 fp = self.wfile('.hgtags', 'rb+')
564 except IOError, e:
564 except IOError, e:
565 if e.errno != errno.ENOENT:
565 if e.errno != errno.ENOENT:
566 raise
566 raise
567 fp = self.wfile('.hgtags', 'ab')
567 fp = self.wfile('.hgtags', 'ab')
568 else:
568 else:
569 prevtags = fp.read()
569 prevtags = fp.read()
570
570
571 # committed tags are stored in UTF-8
571 # committed tags are stored in UTF-8
572 writetags(fp, names, encoding.fromlocal, prevtags)
572 writetags(fp, names, encoding.fromlocal, prevtags)
573
573
574 fp.close()
574 fp.close()
575
575
576 self.invalidatecaches()
576 self.invalidatecaches()
577
577
578 if '.hgtags' not in self.dirstate:
578 if '.hgtags' not in self.dirstate:
579 self[None].add(['.hgtags'])
579 self[None].add(['.hgtags'])
580
580
581 m = matchmod.exact(self.root, '', ['.hgtags'])
581 m = matchmod.exact(self.root, '', ['.hgtags'])
582 tagnode = self.commit(message, user, date, extra=extra, match=m,
582 tagnode = self.commit(message, user, date, extra=extra, match=m,
583 editor=editor)
583 editor=editor)
584
584
585 for name in names:
585 for name in names:
586 self.hook('tag', node=hex(node), tag=name, local=local)
586 self.hook('tag', node=hex(node), tag=name, local=local)
587
587
588 return tagnode
588 return tagnode
589
589
590 def tag(self, names, node, message, local, user, date, editor=False):
590 def tag(self, names, node, message, local, user, date, editor=False):
591 '''tag a revision with one or more symbolic names.
591 '''tag a revision with one or more symbolic names.
592
592
593 names is a list of strings or, when adding a single tag, names may be a
593 names is a list of strings or, when adding a single tag, names may be a
594 string.
594 string.
595
595
596 if local is True, the tags are stored in a per-repository file.
596 if local is True, the tags are stored in a per-repository file.
597 otherwise, they are stored in the .hgtags file, and a new
597 otherwise, they are stored in the .hgtags file, and a new
598 changeset is committed with the change.
598 changeset is committed with the change.
599
599
600 keyword arguments:
600 keyword arguments:
601
601
602 local: whether to store tags in non-version-controlled file
602 local: whether to store tags in non-version-controlled file
603 (default False)
603 (default False)
604
604
605 message: commit message to use if committing
605 message: commit message to use if committing
606
606
607 user: name of user to use if committing
607 user: name of user to use if committing
608
608
609 date: date tuple to use if committing'''
609 date: date tuple to use if committing'''
610
610
611 if not local:
611 if not local:
612 m = matchmod.exact(self.root, '', ['.hgtags'])
612 m = matchmod.exact(self.root, '', ['.hgtags'])
613 if util.any(self.status(match=m, unknown=True, ignored=True)):
613 if util.any(self.status(match=m, unknown=True, ignored=True)):
614 raise util.Abort(_('working copy of .hgtags is changed'),
614 raise util.Abort(_('working copy of .hgtags is changed'),
615 hint=_('please commit .hgtags manually'))
615 hint=_('please commit .hgtags manually'))
616
616
617 self.tags() # instantiate the cache
617 self.tags() # instantiate the cache
618 self._tag(names, node, message, local, user, date, editor=editor)
618 self._tag(names, node, message, local, user, date, editor=editor)
619
619
620 @filteredpropertycache
620 @filteredpropertycache
621 def _tagscache(self):
621 def _tagscache(self):
622 '''Returns a tagscache object that contains various tags related
622 '''Returns a tagscache object that contains various tags related
623 caches.'''
623 caches.'''
624
624
625 # This simplifies its cache management by having one decorated
625 # This simplifies its cache management by having one decorated
626 # function (this one) and the rest simply fetch things from it.
626 # function (this one) and the rest simply fetch things from it.
627 class tagscache(object):
627 class tagscache(object):
628 def __init__(self):
628 def __init__(self):
629 # These two define the set of tags for this repository. tags
629 # These two define the set of tags for this repository. tags
630 # maps tag name to node; tagtypes maps tag name to 'global' or
630 # maps tag name to node; tagtypes maps tag name to 'global' or
631 # 'local'. (Global tags are defined by .hgtags across all
631 # 'local'. (Global tags are defined by .hgtags across all
632 # heads, and local tags are defined in .hg/localtags.)
632 # heads, and local tags are defined in .hg/localtags.)
633 # They constitute the in-memory cache of tags.
633 # They constitute the in-memory cache of tags.
634 self.tags = self.tagtypes = None
634 self.tags = self.tagtypes = None
635
635
636 self.nodetagscache = self.tagslist = None
636 self.nodetagscache = self.tagslist = None
637
637
638 cache = tagscache()
638 cache = tagscache()
639 cache.tags, cache.tagtypes = self._findtags()
639 cache.tags, cache.tagtypes = self._findtags()
640
640
641 return cache
641 return cache
642
642
643 def tags(self):
643 def tags(self):
644 '''return a mapping of tag to node'''
644 '''return a mapping of tag to node'''
645 t = {}
645 t = {}
646 if self.changelog.filteredrevs:
646 if self.changelog.filteredrevs:
647 tags, tt = self._findtags()
647 tags, tt = self._findtags()
648 else:
648 else:
649 tags = self._tagscache.tags
649 tags = self._tagscache.tags
650 for k, v in tags.iteritems():
650 for k, v in tags.iteritems():
651 try:
651 try:
652 # ignore tags to unknown nodes
652 # ignore tags to unknown nodes
653 self.changelog.rev(v)
653 self.changelog.rev(v)
654 t[k] = v
654 t[k] = v
655 except (error.LookupError, ValueError):
655 except (error.LookupError, ValueError):
656 pass
656 pass
657 return t
657 return t
658
658
659 def _findtags(self):
659 def _findtags(self):
660 '''Do the hard work of finding tags. Return a pair of dicts
660 '''Do the hard work of finding tags. Return a pair of dicts
661 (tags, tagtypes) where tags maps tag name to node, and tagtypes
661 (tags, tagtypes) where tags maps tag name to node, and tagtypes
662 maps tag name to a string like \'global\' or \'local\'.
662 maps tag name to a string like \'global\' or \'local\'.
663 Subclasses or extensions are free to add their own tags, but
663 Subclasses or extensions are free to add their own tags, but
664 should be aware that the returned dicts will be retained for the
664 should be aware that the returned dicts will be retained for the
665 duration of the localrepo object.'''
665 duration of the localrepo object.'''
666
666
667 # XXX what tagtype should subclasses/extensions use? Currently
667 # XXX what tagtype should subclasses/extensions use? Currently
668 # mq and bookmarks add tags, but do not set the tagtype at all.
668 # mq and bookmarks add tags, but do not set the tagtype at all.
669 # Should each extension invent its own tag type? Should there
669 # Should each extension invent its own tag type? Should there
670 # be one tagtype for all such "virtual" tags? Or is the status
670 # be one tagtype for all such "virtual" tags? Or is the status
671 # quo fine?
671 # quo fine?
672
672
673 alltags = {} # map tag name to (node, hist)
673 alltags = {} # map tag name to (node, hist)
674 tagtypes = {}
674 tagtypes = {}
675
675
676 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
676 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
677 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
677 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
678
678
679 # Build the return dicts. Have to re-encode tag names because
679 # Build the return dicts. Have to re-encode tag names because
680 # the tags module always uses UTF-8 (in order not to lose info
680 # the tags module always uses UTF-8 (in order not to lose info
681 # writing to the cache), but the rest of Mercurial wants them in
681 # writing to the cache), but the rest of Mercurial wants them in
682 # local encoding.
682 # local encoding.
683 tags = {}
683 tags = {}
684 for (name, (node, hist)) in alltags.iteritems():
684 for (name, (node, hist)) in alltags.iteritems():
685 if node != nullid:
685 if node != nullid:
686 tags[encoding.tolocal(name)] = node
686 tags[encoding.tolocal(name)] = node
687 tags['tip'] = self.changelog.tip()
687 tags['tip'] = self.changelog.tip()
688 tagtypes = dict([(encoding.tolocal(name), value)
688 tagtypes = dict([(encoding.tolocal(name), value)
689 for (name, value) in tagtypes.iteritems()])
689 for (name, value) in tagtypes.iteritems()])
690 return (tags, tagtypes)
690 return (tags, tagtypes)
691
691
692 def tagtype(self, tagname):
692 def tagtype(self, tagname):
693 '''
693 '''
694 return the type of the given tag. result can be:
694 return the type of the given tag. result can be:
695
695
696 'local' : a local tag
696 'local' : a local tag
697 'global' : a global tag
697 'global' : a global tag
698 None : tag does not exist
698 None : tag does not exist
699 '''
699 '''
700
700
701 return self._tagscache.tagtypes.get(tagname)
701 return self._tagscache.tagtypes.get(tagname)
702
702
703 def tagslist(self):
703 def tagslist(self):
704 '''return a list of tags ordered by revision'''
704 '''return a list of tags ordered by revision'''
705 if not self._tagscache.tagslist:
705 if not self._tagscache.tagslist:
706 l = []
706 l = []
707 for t, n in self.tags().iteritems():
707 for t, n in self.tags().iteritems():
708 l.append((self.changelog.rev(n), t, n))
708 l.append((self.changelog.rev(n), t, n))
709 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
709 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
710
710
711 return self._tagscache.tagslist
711 return self._tagscache.tagslist
712
712
713 def nodetags(self, node):
713 def nodetags(self, node):
714 '''return the tags associated with a node'''
714 '''return the tags associated with a node'''
715 if not self._tagscache.nodetagscache:
715 if not self._tagscache.nodetagscache:
716 nodetagscache = {}
716 nodetagscache = {}
717 for t, n in self._tagscache.tags.iteritems():
717 for t, n in self._tagscache.tags.iteritems():
718 nodetagscache.setdefault(n, []).append(t)
718 nodetagscache.setdefault(n, []).append(t)
719 for tags in nodetagscache.itervalues():
719 for tags in nodetagscache.itervalues():
720 tags.sort()
720 tags.sort()
721 self._tagscache.nodetagscache = nodetagscache
721 self._tagscache.nodetagscache = nodetagscache
722 return self._tagscache.nodetagscache.get(node, [])
722 return self._tagscache.nodetagscache.get(node, [])
723
723
724 def nodebookmarks(self, node):
724 def nodebookmarks(self, node):
725 marks = []
725 marks = []
726 for bookmark, n in self._bookmarks.iteritems():
726 for bookmark, n in self._bookmarks.iteritems():
727 if n == node:
727 if n == node:
728 marks.append(bookmark)
728 marks.append(bookmark)
729 return sorted(marks)
729 return sorted(marks)
730
730
731 def branchmap(self):
731 def branchmap(self):
732 '''returns a dictionary {branch: [branchheads]} with branchheads
732 '''returns a dictionary {branch: [branchheads]} with branchheads
733 ordered by increasing revision number'''
733 ordered by increasing revision number'''
734 branchmap.updatecache(self)
734 branchmap.updatecache(self)
735 return self._branchcaches[self.filtername]
735 return self._branchcaches[self.filtername]
736
736
737 @unfilteredmethod
737 @unfilteredmethod
738 def revbranchcache(self):
738 def revbranchcache(self):
739 if not self._revbranchcache:
739 if not self._revbranchcache:
740 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
740 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
741 return self._revbranchcache
741 return self._revbranchcache
742
742
743 def branchtip(self, branch, ignoremissing=False):
743 def branchtip(self, branch, ignoremissing=False):
744 '''return the tip node for a given branch
744 '''return the tip node for a given branch
745
745
746 If ignoremissing is True, then this method will not raise an error.
746 If ignoremissing is True, then this method will not raise an error.
747 This is helpful for callers that only expect None for a missing branch
747 This is helpful for callers that only expect None for a missing branch
748 (e.g. namespace).
748 (e.g. namespace).
749
749
750 '''
750 '''
751 try:
751 try:
752 return self.branchmap().branchtip(branch)
752 return self.branchmap().branchtip(branch)
753 except KeyError:
753 except KeyError:
754 if not ignoremissing:
754 if not ignoremissing:
755 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
755 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
756 else:
756 else:
757 pass
757 pass
758
758
759 def lookup(self, key):
759 def lookup(self, key):
760 return self[key].node()
760 return self[key].node()
761
761
762 def lookupbranch(self, key, remote=None):
762 def lookupbranch(self, key, remote=None):
763 repo = remote or self
763 repo = remote or self
764 if key in repo.branchmap():
764 if key in repo.branchmap():
765 return key
765 return key
766
766
767 repo = (remote and remote.local()) and remote or self
767 repo = (remote and remote.local()) and remote or self
768 return repo[key].branch()
768 return repo[key].branch()
769
769
770 def known(self, nodes):
770 def known(self, nodes):
771 nm = self.changelog.nodemap
771 nm = self.changelog.nodemap
772 pc = self._phasecache
772 pc = self._phasecache
773 result = []
773 result = []
774 for n in nodes:
774 for n in nodes:
775 r = nm.get(n)
775 r = nm.get(n)
776 resp = not (r is None or pc.phase(self, r) >= phases.secret)
776 resp = not (r is None or pc.phase(self, r) >= phases.secret)
777 result.append(resp)
777 result.append(resp)
778 return result
778 return result
779
779
780 def local(self):
780 def local(self):
781 return self
781 return self
782
782
783 def cancopy(self):
783 def cancopy(self):
784 # so statichttprepo's override of local() works
784 # so statichttprepo's override of local() works
785 if not self.local():
785 if not self.local():
786 return False
786 return False
787 if not self.ui.configbool('phases', 'publish', True):
787 if not self.ui.configbool('phases', 'publish', True):
788 return True
788 return True
789 # if publishing we can't copy if there is filtered content
789 # if publishing we can't copy if there is filtered content
790 return not self.filtered('visible').changelog.filteredrevs
790 return not self.filtered('visible').changelog.filteredrevs
791
791
792 def shared(self):
792 def shared(self):
793 '''the type of shared repository (None if not shared)'''
793 '''the type of shared repository (None if not shared)'''
794 if self.sharedpath != self.path:
794 if self.sharedpath != self.path:
795 return 'store'
795 return 'store'
796 return None
796 return None
797
797
798 def join(self, f, *insidef):
798 def join(self, f, *insidef):
799 return self.vfs.join(os.path.join(f, *insidef))
799 return self.vfs.join(os.path.join(f, *insidef))
800
800
801 def wjoin(self, f, *insidef):
801 def wjoin(self, f, *insidef):
802 return self.vfs.reljoin(self.root, f, *insidef)
802 return self.vfs.reljoin(self.root, f, *insidef)
803
803
804 def file(self, f):
804 def file(self, f):
805 if f[0] == '/':
805 if f[0] == '/':
806 f = f[1:]
806 f = f[1:]
807 return filelog.filelog(self.svfs, f)
807 return filelog.filelog(self.svfs, f)
808
808
809 def changectx(self, changeid):
809 def changectx(self, changeid):
810 return self[changeid]
810 return self[changeid]
811
811
812 def parents(self, changeid=None):
812 def parents(self, changeid=None):
813 '''get list of changectxs for parents of changeid'''
813 '''get list of changectxs for parents of changeid'''
814 return self[changeid].parents()
814 return self[changeid].parents()
815
815
816 def setparents(self, p1, p2=nullid):
816 def setparents(self, p1, p2=nullid):
817 self.dirstate.beginparentchange()
817 self.dirstate.beginparentchange()
818 copies = self.dirstate.setparents(p1, p2)
818 copies = self.dirstate.setparents(p1, p2)
819 pctx = self[p1]
819 pctx = self[p1]
820 if copies:
820 if copies:
821 # Adjust copy records, the dirstate cannot do it, it
821 # Adjust copy records, the dirstate cannot do it, it
822 # requires access to parents manifests. Preserve them
822 # requires access to parents manifests. Preserve them
823 # only for entries added to first parent.
823 # only for entries added to first parent.
824 for f in copies:
824 for f in copies:
825 if f not in pctx and copies[f] in pctx:
825 if f not in pctx and copies[f] in pctx:
826 self.dirstate.copy(copies[f], f)
826 self.dirstate.copy(copies[f], f)
827 if p2 == nullid:
827 if p2 == nullid:
828 for f, s in sorted(self.dirstate.copies().items()):
828 for f, s in sorted(self.dirstate.copies().items()):
829 if f not in pctx and s not in pctx:
829 if f not in pctx and s not in pctx:
830 self.dirstate.copy(None, f)
830 self.dirstate.copy(None, f)
831 self.dirstate.endparentchange()
831 self.dirstate.endparentchange()
832
832
833 def filectx(self, path, changeid=None, fileid=None):
833 def filectx(self, path, changeid=None, fileid=None):
834 """changeid can be a changeset revision, node, or tag.
834 """changeid can be a changeset revision, node, or tag.
835 fileid can be a file revision or node."""
835 fileid can be a file revision or node."""
836 return context.filectx(self, path, changeid, fileid)
836 return context.filectx(self, path, changeid, fileid)
837
837
838 def getcwd(self):
838 def getcwd(self):
839 return self.dirstate.getcwd()
839 return self.dirstate.getcwd()
840
840
841 def pathto(self, f, cwd=None):
841 def pathto(self, f, cwd=None):
842 return self.dirstate.pathto(f, cwd)
842 return self.dirstate.pathto(f, cwd)
843
843
844 def wfile(self, f, mode='r'):
844 def wfile(self, f, mode='r'):
845 return self.wvfs(f, mode)
845 return self.wvfs(f, mode)
846
846
847 def _link(self, f):
847 def _link(self, f):
848 return self.wvfs.islink(f)
848 return self.wvfs.islink(f)
849
849
850 def _loadfilter(self, filter):
850 def _loadfilter(self, filter):
851 if filter not in self.filterpats:
851 if filter not in self.filterpats:
852 l = []
852 l = []
853 for pat, cmd in self.ui.configitems(filter):
853 for pat, cmd in self.ui.configitems(filter):
854 if cmd == '!':
854 if cmd == '!':
855 continue
855 continue
856 mf = matchmod.match(self.root, '', [pat])
856 mf = matchmod.match(self.root, '', [pat])
857 fn = None
857 fn = None
858 params = cmd
858 params = cmd
859 for name, filterfn in self._datafilters.iteritems():
859 for name, filterfn in self._datafilters.iteritems():
860 if cmd.startswith(name):
860 if cmd.startswith(name):
861 fn = filterfn
861 fn = filterfn
862 params = cmd[len(name):].lstrip()
862 params = cmd[len(name):].lstrip()
863 break
863 break
864 if not fn:
864 if not fn:
865 fn = lambda s, c, **kwargs: util.filter(s, c)
865 fn = lambda s, c, **kwargs: util.filter(s, c)
866 # Wrap old filters not supporting keyword arguments
866 # Wrap old filters not supporting keyword arguments
867 if not inspect.getargspec(fn)[2]:
867 if not inspect.getargspec(fn)[2]:
868 oldfn = fn
868 oldfn = fn
869 fn = lambda s, c, **kwargs: oldfn(s, c)
869 fn = lambda s, c, **kwargs: oldfn(s, c)
870 l.append((mf, fn, params))
870 l.append((mf, fn, params))
871 self.filterpats[filter] = l
871 self.filterpats[filter] = l
872 return self.filterpats[filter]
872 return self.filterpats[filter]
873
873
874 def _filter(self, filterpats, filename, data):
874 def _filter(self, filterpats, filename, data):
875 for mf, fn, cmd in filterpats:
875 for mf, fn, cmd in filterpats:
876 if mf(filename):
876 if mf(filename):
877 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
877 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
878 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
878 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
879 break
879 break
880
880
881 return data
881 return data
882
882
883 @unfilteredpropertycache
883 @unfilteredpropertycache
884 def _encodefilterpats(self):
884 def _encodefilterpats(self):
885 return self._loadfilter('encode')
885 return self._loadfilter('encode')
886
886
887 @unfilteredpropertycache
887 @unfilteredpropertycache
888 def _decodefilterpats(self):
888 def _decodefilterpats(self):
889 return self._loadfilter('decode')
889 return self._loadfilter('decode')
890
890
891 def adddatafilter(self, name, filter):
891 def adddatafilter(self, name, filter):
892 self._datafilters[name] = filter
892 self._datafilters[name] = filter
893
893
894 def wread(self, filename):
894 def wread(self, filename):
895 if self._link(filename):
895 if self._link(filename):
896 data = self.wvfs.readlink(filename)
896 data = self.wvfs.readlink(filename)
897 else:
897 else:
898 data = self.wvfs.read(filename)
898 data = self.wvfs.read(filename)
899 return self._filter(self._encodefilterpats, filename, data)
899 return self._filter(self._encodefilterpats, filename, data)
900
900
901 def wwrite(self, filename, data, flags):
901 def wwrite(self, filename, data, flags):
902 data = self._filter(self._decodefilterpats, filename, data)
902 data = self._filter(self._decodefilterpats, filename, data)
903 if 'l' in flags:
903 if 'l' in flags:
904 self.wvfs.symlink(data, filename)
904 self.wvfs.symlink(data, filename)
905 else:
905 else:
906 self.wvfs.write(filename, data)
906 self.wvfs.write(filename, data)
907 if 'x' in flags:
907 if 'x' in flags:
908 self.wvfs.setflags(filename, False, True)
908 self.wvfs.setflags(filename, False, True)
909
909
910 def wwritedata(self, filename, data):
910 def wwritedata(self, filename, data):
911 return self._filter(self._decodefilterpats, filename, data)
911 return self._filter(self._decodefilterpats, filename, data)
912
912
913 def currenttransaction(self):
913 def currenttransaction(self):
914 """return the current transaction or None if non exists"""
914 """return the current transaction or None if non exists"""
915 if self._transref:
915 if self._transref:
916 tr = self._transref()
916 tr = self._transref()
917 else:
917 else:
918 tr = None
918 tr = None
919
919
920 if tr and tr.running():
920 if tr and tr.running():
921 return tr
921 return tr
922 return None
922 return None
923
923
924 def transaction(self, desc, report=None):
924 def transaction(self, desc, report=None):
925 if (self.ui.configbool('devel', 'all')
925 if (self.ui.configbool('devel', 'all')
926 or self.ui.configbool('devel', 'check-locks')):
926 or self.ui.configbool('devel', 'check-locks')):
927 l = self._lockref and self._lockref()
927 l = self._lockref and self._lockref()
928 if l is None or not l.held:
928 if l is None or not l.held:
929 msg = 'transaction with no lock\n'
929 msg = 'transaction with no lock\n'
930 if self.ui.tracebackflag:
930 if self.ui.tracebackflag:
931 util.debugstacktrace(msg, 1)
931 util.debugstacktrace(msg, 1)
932 else:
932 else:
933 self.ui.write_err(msg)
933 self.ui.write_err(msg)
934 tr = self.currenttransaction()
934 tr = self.currenttransaction()
935 if tr is not None:
935 if tr is not None:
936 return tr.nest()
936 return tr.nest()
937
937
938 # abort here if the journal already exists
938 # abort here if the journal already exists
939 if self.svfs.exists("journal"):
939 if self.svfs.exists("journal"):
940 raise error.RepoError(
940 raise error.RepoError(
941 _("abandoned transaction found"),
941 _("abandoned transaction found"),
942 hint=_("run 'hg recover' to clean up transaction"))
942 hint=_("run 'hg recover' to clean up transaction"))
943
943
944 self.hook('pretxnopen', throw=True, txnname=desc)
944 self.hook('pretxnopen', throw=True, txnname=desc)
945
945
946 self._writejournal(desc)
946 self._writejournal(desc)
947 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
947 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
948 if report:
948 if report:
949 rp = report
949 rp = report
950 else:
950 else:
951 rp = self.ui.warn
951 rp = self.ui.warn
952 vfsmap = {'plain': self.vfs} # root of .hg/
952 vfsmap = {'plain': self.vfs} # root of .hg/
953 # we must avoid cyclic reference between repo and transaction.
953 # we must avoid cyclic reference between repo and transaction.
954 reporef = weakref.ref(self)
954 reporef = weakref.ref(self)
955 def validate(tr):
955 def validate(tr):
956 """will run pre-closing hooks"""
956 """will run pre-closing hooks"""
957 pending = lambda: tr.writepending() and self.root or ""
957 pending = lambda: tr.writepending() and self.root or ""
958 reporef().hook('pretxnclose', throw=True, pending=pending,
958 reporef().hook('pretxnclose', throw=True, pending=pending,
959 xnname=desc)
959 xnname=desc)
960
960
961 tr = transaction.transaction(rp, self.sopener, vfsmap,
961 tr = transaction.transaction(rp, self.sopener, vfsmap,
962 "journal",
962 "journal",
963 "undo",
963 "undo",
964 aftertrans(renames),
964 aftertrans(renames),
965 self.store.createmode,
965 self.store.createmode,
966 validator=validate)
966 validator=validate)
967 # note: writing the fncache only during finalize mean that the file is
967 # note: writing the fncache only during finalize mean that the file is
968 # outdated when running hooks. As fncache is used for streaming clone,
968 # outdated when running hooks. As fncache is used for streaming clone,
969 # this is not expected to break anything that happen during the hooks.
969 # this is not expected to break anything that happen during the hooks.
970 tr.addfinalize('flush-fncache', self.store.write)
970 tr.addfinalize('flush-fncache', self.store.write)
971 def txnclosehook(tr2):
971 def txnclosehook(tr2):
972 """To be run if transaction is successful, will schedule a hook run
972 """To be run if transaction is successful, will schedule a hook run
973 """
973 """
974 def hook():
974 def hook():
975 reporef().hook('txnclose', throw=False, txnname=desc,
975 reporef().hook('txnclose', throw=False, txnname=desc,
976 **tr2.hookargs)
976 **tr2.hookargs)
977 reporef()._afterlock(hook)
977 reporef()._afterlock(hook)
978 tr.addfinalize('txnclose-hook', txnclosehook)
978 tr.addfinalize('txnclose-hook', txnclosehook)
979 self._transref = weakref.ref(tr)
979 self._transref = weakref.ref(tr)
980 return tr
980 return tr
981
981
982 def _journalfiles(self):
982 def _journalfiles(self):
983 return ((self.svfs, 'journal'),
983 return ((self.svfs, 'journal'),
984 (self.vfs, 'journal.dirstate'),
984 (self.vfs, 'journal.dirstate'),
985 (self.vfs, 'journal.branch'),
985 (self.vfs, 'journal.branch'),
986 (self.vfs, 'journal.desc'),
986 (self.vfs, 'journal.desc'),
987 (self.vfs, 'journal.bookmarks'),
987 (self.vfs, 'journal.bookmarks'),
988 (self.svfs, 'journal.phaseroots'))
988 (self.svfs, 'journal.phaseroots'))
989
989
990 def undofiles(self):
990 def undofiles(self):
991 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
991 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
992
992
993 def _writejournal(self, desc):
993 def _writejournal(self, desc):
994 self.vfs.write("journal.dirstate",
994 self.vfs.write("journal.dirstate",
995 self.vfs.tryread("dirstate"))
995 self.vfs.tryread("dirstate"))
996 self.vfs.write("journal.branch",
996 self.vfs.write("journal.branch",
997 encoding.fromlocal(self.dirstate.branch()))
997 encoding.fromlocal(self.dirstate.branch()))
998 self.vfs.write("journal.desc",
998 self.vfs.write("journal.desc",
999 "%d\n%s\n" % (len(self), desc))
999 "%d\n%s\n" % (len(self), desc))
1000 self.vfs.write("journal.bookmarks",
1000 self.vfs.write("journal.bookmarks",
1001 self.vfs.tryread("bookmarks"))
1001 self.vfs.tryread("bookmarks"))
1002 self.svfs.write("journal.phaseroots",
1002 self.svfs.write("journal.phaseroots",
1003 self.svfs.tryread("phaseroots"))
1003 self.svfs.tryread("phaseroots"))
1004
1004
1005 def recover(self):
1005 def recover(self):
1006 lock = self.lock()
1006 lock = self.lock()
1007 try:
1007 try:
1008 if self.svfs.exists("journal"):
1008 if self.svfs.exists("journal"):
1009 self.ui.status(_("rolling back interrupted transaction\n"))
1009 self.ui.status(_("rolling back interrupted transaction\n"))
1010 vfsmap = {'': self.svfs,
1010 vfsmap = {'': self.svfs,
1011 'plain': self.vfs,}
1011 'plain': self.vfs,}
1012 transaction.rollback(self.svfs, vfsmap, "journal",
1012 transaction.rollback(self.svfs, vfsmap, "journal",
1013 self.ui.warn)
1013 self.ui.warn)
1014 self.invalidate()
1014 self.invalidate()
1015 return True
1015 return True
1016 else:
1016 else:
1017 self.ui.warn(_("no interrupted transaction available\n"))
1017 self.ui.warn(_("no interrupted transaction available\n"))
1018 return False
1018 return False
1019 finally:
1019 finally:
1020 lock.release()
1020 lock.release()
1021
1021
1022 def rollback(self, dryrun=False, force=False):
1022 def rollback(self, dryrun=False, force=False):
1023 wlock = lock = None
1023 wlock = lock = None
1024 try:
1024 try:
1025 wlock = self.wlock()
1025 wlock = self.wlock()
1026 lock = self.lock()
1026 lock = self.lock()
1027 if self.svfs.exists("undo"):
1027 if self.svfs.exists("undo"):
1028 return self._rollback(dryrun, force)
1028 return self._rollback(dryrun, force)
1029 else:
1029 else:
1030 self.ui.warn(_("no rollback information available\n"))
1030 self.ui.warn(_("no rollback information available\n"))
1031 return 1
1031 return 1
1032 finally:
1032 finally:
1033 release(lock, wlock)
1033 release(lock, wlock)
1034
1034
1035 @unfilteredmethod # Until we get smarter cache management
1035 @unfilteredmethod # Until we get smarter cache management
1036 def _rollback(self, dryrun, force):
1036 def _rollback(self, dryrun, force):
1037 ui = self.ui
1037 ui = self.ui
1038 try:
1038 try:
1039 args = self.vfs.read('undo.desc').splitlines()
1039 args = self.vfs.read('undo.desc').splitlines()
1040 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1040 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1041 if len(args) >= 3:
1041 if len(args) >= 3:
1042 detail = args[2]
1042 detail = args[2]
1043 oldtip = oldlen - 1
1043 oldtip = oldlen - 1
1044
1044
1045 if detail and ui.verbose:
1045 if detail and ui.verbose:
1046 msg = (_('repository tip rolled back to revision %s'
1046 msg = (_('repository tip rolled back to revision %s'
1047 ' (undo %s: %s)\n')
1047 ' (undo %s: %s)\n')
1048 % (oldtip, desc, detail))
1048 % (oldtip, desc, detail))
1049 else:
1049 else:
1050 msg = (_('repository tip rolled back to revision %s'
1050 msg = (_('repository tip rolled back to revision %s'
1051 ' (undo %s)\n')
1051 ' (undo %s)\n')
1052 % (oldtip, desc))
1052 % (oldtip, desc))
1053 except IOError:
1053 except IOError:
1054 msg = _('rolling back unknown transaction\n')
1054 msg = _('rolling back unknown transaction\n')
1055 desc = None
1055 desc = None
1056
1056
1057 if not force and self['.'] != self['tip'] and desc == 'commit':
1057 if not force and self['.'] != self['tip'] and desc == 'commit':
1058 raise util.Abort(
1058 raise util.Abort(
1059 _('rollback of last commit while not checked out '
1059 _('rollback of last commit while not checked out '
1060 'may lose data'), hint=_('use -f to force'))
1060 'may lose data'), hint=_('use -f to force'))
1061
1061
1062 ui.status(msg)
1062 ui.status(msg)
1063 if dryrun:
1063 if dryrun:
1064 return 0
1064 return 0
1065
1065
1066 parents = self.dirstate.parents()
1066 parents = self.dirstate.parents()
1067 self.destroying()
1067 self.destroying()
1068 vfsmap = {'plain': self.vfs, '': self.svfs}
1068 vfsmap = {'plain': self.vfs, '': self.svfs}
1069 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1069 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1070 if self.vfs.exists('undo.bookmarks'):
1070 if self.vfs.exists('undo.bookmarks'):
1071 self.vfs.rename('undo.bookmarks', 'bookmarks')
1071 self.vfs.rename('undo.bookmarks', 'bookmarks')
1072 if self.svfs.exists('undo.phaseroots'):
1072 if self.svfs.exists('undo.phaseroots'):
1073 self.svfs.rename('undo.phaseroots', 'phaseroots')
1073 self.svfs.rename('undo.phaseroots', 'phaseroots')
1074 self.invalidate()
1074 self.invalidate()
1075
1075
1076 parentgone = (parents[0] not in self.changelog.nodemap or
1076 parentgone = (parents[0] not in self.changelog.nodemap or
1077 parents[1] not in self.changelog.nodemap)
1077 parents[1] not in self.changelog.nodemap)
1078 if parentgone:
1078 if parentgone:
1079 self.vfs.rename('undo.dirstate', 'dirstate')
1079 self.vfs.rename('undo.dirstate', 'dirstate')
1080 try:
1080 try:
1081 branch = self.vfs.read('undo.branch')
1081 branch = self.vfs.read('undo.branch')
1082 self.dirstate.setbranch(encoding.tolocal(branch))
1082 self.dirstate.setbranch(encoding.tolocal(branch))
1083 except IOError:
1083 except IOError:
1084 ui.warn(_('named branch could not be reset: '
1084 ui.warn(_('named branch could not be reset: '
1085 'current branch is still \'%s\'\n')
1085 'current branch is still \'%s\'\n')
1086 % self.dirstate.branch())
1086 % self.dirstate.branch())
1087
1087
1088 self.dirstate.invalidate()
1088 self.dirstate.invalidate()
1089 parents = tuple([p.rev() for p in self.parents()])
1089 parents = tuple([p.rev() for p in self.parents()])
1090 if len(parents) > 1:
1090 if len(parents) > 1:
1091 ui.status(_('working directory now based on '
1091 ui.status(_('working directory now based on '
1092 'revisions %d and %d\n') % parents)
1092 'revisions %d and %d\n') % parents)
1093 else:
1093 else:
1094 ui.status(_('working directory now based on '
1094 ui.status(_('working directory now based on '
1095 'revision %d\n') % parents)
1095 'revision %d\n') % parents)
1096 # TODO: if we know which new heads may result from this rollback, pass
1096 # TODO: if we know which new heads may result from this rollback, pass
1097 # them to destroy(), which will prevent the branchhead cache from being
1097 # them to destroy(), which will prevent the branchhead cache from being
1098 # invalidated.
1098 # invalidated.
1099 self.destroyed()
1099 self.destroyed()
1100 return 0
1100 return 0
1101
1101
1102 def invalidatecaches(self):
1102 def invalidatecaches(self):
1103
1103
1104 if '_tagscache' in vars(self):
1104 if '_tagscache' in vars(self):
1105 # can't use delattr on proxy
1105 # can't use delattr on proxy
1106 del self.__dict__['_tagscache']
1106 del self.__dict__['_tagscache']
1107
1107
1108 self.unfiltered()._branchcaches.clear()
1108 self.unfiltered()._branchcaches.clear()
1109 self.invalidatevolatilesets()
1109 self.invalidatevolatilesets()
1110
1110
1111 def invalidatevolatilesets(self):
1111 def invalidatevolatilesets(self):
1112 self.filteredrevcache.clear()
1112 self.filteredrevcache.clear()
1113 obsolete.clearobscaches(self)
1113 obsolete.clearobscaches(self)
1114
1114
1115 def invalidatedirstate(self):
1115 def invalidatedirstate(self):
1116 '''Invalidates the dirstate, causing the next call to dirstate
1116 '''Invalidates the dirstate, causing the next call to dirstate
1117 to check if it was modified since the last time it was read,
1117 to check if it was modified since the last time it was read,
1118 rereading it if it has.
1118 rereading it if it has.
1119
1119
1120 This is different to dirstate.invalidate() that it doesn't always
1120 This is different to dirstate.invalidate() that it doesn't always
1121 rereads the dirstate. Use dirstate.invalidate() if you want to
1121 rereads the dirstate. Use dirstate.invalidate() if you want to
1122 explicitly read the dirstate again (i.e. restoring it to a previous
1122 explicitly read the dirstate again (i.e. restoring it to a previous
1123 known good state).'''
1123 known good state).'''
1124 if hasunfilteredcache(self, 'dirstate'):
1124 if hasunfilteredcache(self, 'dirstate'):
1125 for k in self.dirstate._filecache:
1125 for k in self.dirstate._filecache:
1126 try:
1126 try:
1127 delattr(self.dirstate, k)
1127 delattr(self.dirstate, k)
1128 except AttributeError:
1128 except AttributeError:
1129 pass
1129 pass
1130 delattr(self.unfiltered(), 'dirstate')
1130 delattr(self.unfiltered(), 'dirstate')
1131
1131
1132 def invalidate(self):
1132 def invalidate(self):
1133 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1133 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1134 for k in self._filecache:
1134 for k in self._filecache:
1135 # dirstate is invalidated separately in invalidatedirstate()
1135 # dirstate is invalidated separately in invalidatedirstate()
1136 if k == 'dirstate':
1136 if k == 'dirstate':
1137 continue
1137 continue
1138
1138
1139 try:
1139 try:
1140 delattr(unfiltered, k)
1140 delattr(unfiltered, k)
1141 except AttributeError:
1141 except AttributeError:
1142 pass
1142 pass
1143 self.invalidatecaches()
1143 self.invalidatecaches()
1144 self.store.invalidatecaches()
1144 self.store.invalidatecaches()
1145
1145
1146 def invalidateall(self):
1146 def invalidateall(self):
1147 '''Fully invalidates both store and non-store parts, causing the
1147 '''Fully invalidates both store and non-store parts, causing the
1148 subsequent operation to reread any outside changes.'''
1148 subsequent operation to reread any outside changes.'''
1149 # extension should hook this to invalidate its caches
1149 # extension should hook this to invalidate its caches
1150 self.invalidate()
1150 self.invalidate()
1151 self.invalidatedirstate()
1151 self.invalidatedirstate()
1152
1152
1153 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1153 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1154 try:
1154 try:
1155 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1155 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1156 except error.LockHeld, inst:
1156 except error.LockHeld, inst:
1157 if not wait:
1157 if not wait:
1158 raise
1158 raise
1159 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1159 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1160 (desc, inst.locker))
1160 (desc, inst.locker))
1161 # default to 600 seconds timeout
1161 # default to 600 seconds timeout
1162 l = lockmod.lock(vfs, lockname,
1162 l = lockmod.lock(vfs, lockname,
1163 int(self.ui.config("ui", "timeout", "600")),
1163 int(self.ui.config("ui", "timeout", "600")),
1164 releasefn, desc=desc)
1164 releasefn, desc=desc)
1165 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1165 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1166 if acquirefn:
1166 if acquirefn:
1167 acquirefn()
1167 acquirefn()
1168 return l
1168 return l
1169
1169
1170 def _afterlock(self, callback):
1170 def _afterlock(self, callback):
1171 """add a callback to the current repository lock.
1171 """add a callback to the current repository lock.
1172
1172
1173 The callback will be executed on lock release."""
1173 The callback will be executed on lock release."""
1174 l = self._lockref and self._lockref()
1174 l = self._lockref and self._lockref()
1175 if l:
1175 if l:
1176 l.postrelease.append(callback)
1176 l.postrelease.append(callback)
1177 else:
1177 else:
1178 callback()
1178 callback()
1179
1179
1180 def lock(self, wait=True):
1180 def lock(self, wait=True):
1181 '''Lock the repository store (.hg/store) and return a weak reference
1181 '''Lock the repository store (.hg/store) and return a weak reference
1182 to the lock. Use this before modifying the store (e.g. committing or
1182 to the lock. Use this before modifying the store (e.g. committing or
1183 stripping). If you are opening a transaction, get a lock as well.)'''
1183 stripping). If you are opening a transaction, get a lock as well.)'''
1184 l = self._lockref and self._lockref()
1184 l = self._lockref and self._lockref()
1185 if l is not None and l.held:
1185 if l is not None and l.held:
1186 l.lock()
1186 l.lock()
1187 return l
1187 return l
1188
1188
1189 def unlock():
1189 def unlock():
1190 for k, ce in self._filecache.items():
1190 for k, ce in self._filecache.items():
1191 if k == 'dirstate' or k not in self.__dict__:
1191 if k == 'dirstate' or k not in self.__dict__:
1192 continue
1192 continue
1193 ce.refresh()
1193 ce.refresh()
1194
1194
1195 l = self._lock(self.svfs, "lock", wait, unlock,
1195 l = self._lock(self.svfs, "lock", wait, unlock,
1196 self.invalidate, _('repository %s') % self.origroot)
1196 self.invalidate, _('repository %s') % self.origroot)
1197 self._lockref = weakref.ref(l)
1197 self._lockref = weakref.ref(l)
1198 return l
1198 return l
1199
1199
1200 def wlock(self, wait=True):
1200 def wlock(self, wait=True):
1201 '''Lock the non-store parts of the repository (everything under
1201 '''Lock the non-store parts of the repository (everything under
1202 .hg except .hg/store) and return a weak reference to the lock.
1202 .hg except .hg/store) and return a weak reference to the lock.
1203 Use this before modifying files in .hg.'''
1203 Use this before modifying files in .hg.'''
1204 if (self.ui.configbool('devel', 'all')
1204 if (self.ui.configbool('devel', 'all')
1205 or self.ui.configbool('devel', 'check-locks')):
1205 or self.ui.configbool('devel', 'check-locks')):
1206 l = self._lockref and self._lockref()
1206 l = self._lockref and self._lockref()
1207 if l is not None and l.held:
1207 if l is not None and l.held:
1208 msg = '"lock" taken before "wlock"\n'
1208 msg = '"lock" taken before "wlock"\n'
1209 if self.ui.tracebackflag:
1209 if self.ui.tracebackflag:
1210 util.debugstacktrace(msg, 1)
1210 util.debugstacktrace(msg, 1)
1211 else:
1211 else:
1212 self.ui.write_err(msg)
1212 self.ui.write_err(msg)
1213 l = self._wlockref and self._wlockref()
1213 l = self._wlockref and self._wlockref()
1214 if l is not None and l.held:
1214 if l is not None and l.held:
1215 l.lock()
1215 l.lock()
1216 return l
1216 return l
1217
1217
1218 def unlock():
1218 def unlock():
1219 if self.dirstate.pendingparentchange():
1219 if self.dirstate.pendingparentchange():
1220 self.dirstate.invalidate()
1220 self.dirstate.invalidate()
1221 else:
1221 else:
1222 self.dirstate.write()
1222 self.dirstate.write()
1223
1223
1224 self._filecache['dirstate'].refresh()
1224 self._filecache['dirstate'].refresh()
1225
1225
1226 l = self._lock(self.vfs, "wlock", wait, unlock,
1226 l = self._lock(self.vfs, "wlock", wait, unlock,
1227 self.invalidatedirstate, _('working directory of %s') %
1227 self.invalidatedirstate, _('working directory of %s') %
1228 self.origroot)
1228 self.origroot)
1229 self._wlockref = weakref.ref(l)
1229 self._wlockref = weakref.ref(l)
1230 return l
1230 return l
1231
1231
1232 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1232 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1233 """
1233 """
1234 commit an individual file as part of a larger transaction
1234 commit an individual file as part of a larger transaction
1235 """
1235 """
1236
1236
1237 fname = fctx.path()
1237 fname = fctx.path()
1238 fparent1 = manifest1.get(fname, nullid)
1238 fparent1 = manifest1.get(fname, nullid)
1239 fparent2 = manifest2.get(fname, nullid)
1239 fparent2 = manifest2.get(fname, nullid)
1240 if isinstance(fctx, context.filectx):
1240 if isinstance(fctx, context.filectx):
1241 node = fctx.filenode()
1241 node = fctx.filenode()
1242 if node in [fparent1, fparent2]:
1242 if node in [fparent1, fparent2]:
1243 self.ui.debug('reusing %s filelog entry\n' % fname)
1243 self.ui.debug('reusing %s filelog entry\n' % fname)
1244 return node
1244 return node
1245
1245
1246 flog = self.file(fname)
1246 flog = self.file(fname)
1247 meta = {}
1247 meta = {}
1248 copy = fctx.renamed()
1248 copy = fctx.renamed()
1249 if copy and copy[0] != fname:
1249 if copy and copy[0] != fname:
1250 # Mark the new revision of this file as a copy of another
1250 # Mark the new revision of this file as a copy of another
1251 # file. This copy data will effectively act as a parent
1251 # file. This copy data will effectively act as a parent
1252 # of this new revision. If this is a merge, the first
1252 # of this new revision. If this is a merge, the first
1253 # parent will be the nullid (meaning "look up the copy data")
1253 # parent will be the nullid (meaning "look up the copy data")
1254 # and the second one will be the other parent. For example:
1254 # and the second one will be the other parent. For example:
1255 #
1255 #
1256 # 0 --- 1 --- 3 rev1 changes file foo
1256 # 0 --- 1 --- 3 rev1 changes file foo
1257 # \ / rev2 renames foo to bar and changes it
1257 # \ / rev2 renames foo to bar and changes it
1258 # \- 2 -/ rev3 should have bar with all changes and
1258 # \- 2 -/ rev3 should have bar with all changes and
1259 # should record that bar descends from
1259 # should record that bar descends from
1260 # bar in rev2 and foo in rev1
1260 # bar in rev2 and foo in rev1
1261 #
1261 #
1262 # this allows this merge to succeed:
1262 # this allows this merge to succeed:
1263 #
1263 #
1264 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1264 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1265 # \ / merging rev3 and rev4 should use bar@rev2
1265 # \ / merging rev3 and rev4 should use bar@rev2
1266 # \- 2 --- 4 as the merge base
1266 # \- 2 --- 4 as the merge base
1267 #
1267 #
1268
1268
1269 cfname = copy[0]
1269 cfname = copy[0]
1270 crev = manifest1.get(cfname)
1270 crev = manifest1.get(cfname)
1271 newfparent = fparent2
1271 newfparent = fparent2
1272
1272
1273 if manifest2: # branch merge
1273 if manifest2: # branch merge
1274 if fparent2 == nullid or crev is None: # copied on remote side
1274 if fparent2 == nullid or crev is None: # copied on remote side
1275 if cfname in manifest2:
1275 if cfname in manifest2:
1276 crev = manifest2[cfname]
1276 crev = manifest2[cfname]
1277 newfparent = fparent1
1277 newfparent = fparent1
1278
1278
1279 # Here, we used to search backwards through history to try to find
1279 # Here, we used to search backwards through history to try to find
1280 # where the file copy came from if the source of a copy was not in
1280 # where the file copy came from if the source of a copy was not in
1281 # the parent directory. However, this doesn't actually make sense to
1281 # the parent directory. However, this doesn't actually make sense to
1282 # do (what does a copy from something not in your working copy even
1282 # do (what does a copy from something not in your working copy even
1283 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1283 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1284 # the user that copy information was dropped, so if they didn't
1284 # the user that copy information was dropped, so if they didn't
1285 # expect this outcome it can be fixed, but this is the correct
1285 # expect this outcome it can be fixed, but this is the correct
1286 # behavior in this circumstance.
1286 # behavior in this circumstance.
1287
1287
1288 if crev:
1288 if crev:
1289 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1289 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1290 meta["copy"] = cfname
1290 meta["copy"] = cfname
1291 meta["copyrev"] = hex(crev)
1291 meta["copyrev"] = hex(crev)
1292 fparent1, fparent2 = nullid, newfparent
1292 fparent1, fparent2 = nullid, newfparent
1293 else:
1293 else:
1294 self.ui.warn(_("warning: can't find ancestor for '%s' "
1294 self.ui.warn(_("warning: can't find ancestor for '%s' "
1295 "copied from '%s'!\n") % (fname, cfname))
1295 "copied from '%s'!\n") % (fname, cfname))
1296
1296
1297 elif fparent1 == nullid:
1297 elif fparent1 == nullid:
1298 fparent1, fparent2 = fparent2, nullid
1298 fparent1, fparent2 = fparent2, nullid
1299 elif fparent2 != nullid:
1299 elif fparent2 != nullid:
1300 # is one parent an ancestor of the other?
1300 # is one parent an ancestor of the other?
1301 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1301 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1302 if fparent1 in fparentancestors:
1302 if fparent1 in fparentancestors:
1303 fparent1, fparent2 = fparent2, nullid
1303 fparent1, fparent2 = fparent2, nullid
1304 elif fparent2 in fparentancestors:
1304 elif fparent2 in fparentancestors:
1305 fparent2 = nullid
1305 fparent2 = nullid
1306
1306
1307 # is the file changed?
1307 # is the file changed?
1308 text = fctx.data()
1308 text = fctx.data()
1309 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1309 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1310 changelist.append(fname)
1310 changelist.append(fname)
1311 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1311 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1312 # are just the flags changed during merge?
1312 # are just the flags changed during merge?
1313 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1313 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1314 changelist.append(fname)
1314 changelist.append(fname)
1315
1315
1316 return fparent1
1316 return fparent1
1317
1317
1318 @unfilteredmethod
1318 @unfilteredmethod
1319 def commit(self, text="", user=None, date=None, match=None, force=False,
1319 def commit(self, text="", user=None, date=None, match=None, force=False,
1320 editor=False, extra={}):
1320 editor=False, extra={}):
1321 """Add a new revision to current repository.
1321 """Add a new revision to current repository.
1322
1322
1323 Revision information is gathered from the working directory,
1323 Revision information is gathered from the working directory,
1324 match can be used to filter the committed files. If editor is
1324 match can be used to filter the committed files. If editor is
1325 supplied, it is called to get a commit message.
1325 supplied, it is called to get a commit message.
1326 """
1326 """
1327
1327
1328 def fail(f, msg):
1328 def fail(f, msg):
1329 raise util.Abort('%s: %s' % (f, msg))
1329 raise util.Abort('%s: %s' % (f, msg))
1330
1330
1331 if not match:
1331 if not match:
1332 match = matchmod.always(self.root, '')
1332 match = matchmod.always(self.root, '')
1333
1333
1334 if not force:
1334 if not force:
1335 vdirs = []
1335 vdirs = []
1336 match.explicitdir = vdirs.append
1336 match.explicitdir = vdirs.append
1337 match.bad = fail
1337 match.bad = fail
1338
1338
1339 wlock = self.wlock()
1339 wlock = self.wlock()
1340 try:
1340 try:
1341 wctx = self[None]
1341 wctx = self[None]
1342 merge = len(wctx.parents()) > 1
1342 merge = len(wctx.parents()) > 1
1343
1343
1344 if not force and merge and not match.always():
1344 if not force and merge and not match.always():
1345 raise util.Abort(_('cannot partially commit a merge '
1345 raise util.Abort(_('cannot partially commit a merge '
1346 '(do not specify files or patterns)'))
1346 '(do not specify files or patterns)'))
1347
1347
1348 status = self.status(match=match, clean=force)
1348 status = self.status(match=match, clean=force)
1349 if force:
1349 if force:
1350 status.modified.extend(status.clean) # mq may commit clean files
1350 status.modified.extend(status.clean) # mq may commit clean files
1351
1351
1352 # check subrepos
1352 # check subrepos
1353 subs = []
1353 subs = []
1354 commitsubs = set()
1354 commitsubs = set()
1355 newstate = wctx.substate.copy()
1355 newstate = wctx.substate.copy()
1356 # only manage subrepos and .hgsubstate if .hgsub is present
1356 # only manage subrepos and .hgsubstate if .hgsub is present
1357 if '.hgsub' in wctx:
1357 if '.hgsub' in wctx:
1358 # we'll decide whether to track this ourselves, thanks
1358 # we'll decide whether to track this ourselves, thanks
1359 for c in status.modified, status.added, status.removed:
1359 for c in status.modified, status.added, status.removed:
1360 if '.hgsubstate' in c:
1360 if '.hgsubstate' in c:
1361 c.remove('.hgsubstate')
1361 c.remove('.hgsubstate')
1362
1362
1363 # compare current state to last committed state
1363 # compare current state to last committed state
1364 # build new substate based on last committed state
1364 # build new substate based on last committed state
1365 oldstate = wctx.p1().substate
1365 oldstate = wctx.p1().substate
1366 for s in sorted(newstate.keys()):
1366 for s in sorted(newstate.keys()):
1367 if not match(s):
1367 if not match(s):
1368 # ignore working copy, use old state if present
1368 # ignore working copy, use old state if present
1369 if s in oldstate:
1369 if s in oldstate:
1370 newstate[s] = oldstate[s]
1370 newstate[s] = oldstate[s]
1371 continue
1371 continue
1372 if not force:
1372 if not force:
1373 raise util.Abort(
1373 raise util.Abort(
1374 _("commit with new subrepo %s excluded") % s)
1374 _("commit with new subrepo %s excluded") % s)
1375 if wctx.sub(s).dirty(True):
1375 dirtyreason = wctx.sub(s).dirtyreason(True)
1376 if dirtyreason:
1376 if not self.ui.configbool('ui', 'commitsubrepos'):
1377 if not self.ui.configbool('ui', 'commitsubrepos'):
1377 raise util.Abort(
1378 raise util.Abort(dirtyreason,
1378 _("uncommitted changes in subrepo %s") % s,
1379 hint=_("use --subrepos for recursive commit"))
1379 hint=_("use --subrepos for recursive commit"))
1380 subs.append(s)
1380 subs.append(s)
1381 commitsubs.add(s)
1381 commitsubs.add(s)
1382 else:
1382 else:
1383 bs = wctx.sub(s).basestate()
1383 bs = wctx.sub(s).basestate()
1384 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1384 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1385 if oldstate.get(s, (None, None, None))[1] != bs:
1385 if oldstate.get(s, (None, None, None))[1] != bs:
1386 subs.append(s)
1386 subs.append(s)
1387
1387
1388 # check for removed subrepos
1388 # check for removed subrepos
1389 for p in wctx.parents():
1389 for p in wctx.parents():
1390 r = [s for s in p.substate if s not in newstate]
1390 r = [s for s in p.substate if s not in newstate]
1391 subs += [s for s in r if match(s)]
1391 subs += [s for s in r if match(s)]
1392 if subs:
1392 if subs:
1393 if (not match('.hgsub') and
1393 if (not match('.hgsub') and
1394 '.hgsub' in (wctx.modified() + wctx.added())):
1394 '.hgsub' in (wctx.modified() + wctx.added())):
1395 raise util.Abort(
1395 raise util.Abort(
1396 _("can't commit subrepos without .hgsub"))
1396 _("can't commit subrepos without .hgsub"))
1397 status.modified.insert(0, '.hgsubstate')
1397 status.modified.insert(0, '.hgsubstate')
1398
1398
1399 elif '.hgsub' in status.removed:
1399 elif '.hgsub' in status.removed:
1400 # clean up .hgsubstate when .hgsub is removed
1400 # clean up .hgsubstate when .hgsub is removed
1401 if ('.hgsubstate' in wctx and
1401 if ('.hgsubstate' in wctx and
1402 '.hgsubstate' not in (status.modified + status.added +
1402 '.hgsubstate' not in (status.modified + status.added +
1403 status.removed)):
1403 status.removed)):
1404 status.removed.insert(0, '.hgsubstate')
1404 status.removed.insert(0, '.hgsubstate')
1405
1405
1406 # make sure all explicit patterns are matched
1406 # make sure all explicit patterns are matched
1407 if not force and match.files():
1407 if not force and match.files():
1408 matched = set(status.modified + status.added + status.removed)
1408 matched = set(status.modified + status.added + status.removed)
1409
1409
1410 for f in match.files():
1410 for f in match.files():
1411 f = self.dirstate.normalize(f)
1411 f = self.dirstate.normalize(f)
1412 if f == '.' or f in matched or f in wctx.substate:
1412 if f == '.' or f in matched or f in wctx.substate:
1413 continue
1413 continue
1414 if f in status.deleted:
1414 if f in status.deleted:
1415 fail(f, _('file not found!'))
1415 fail(f, _('file not found!'))
1416 if f in vdirs: # visited directory
1416 if f in vdirs: # visited directory
1417 d = f + '/'
1417 d = f + '/'
1418 for mf in matched:
1418 for mf in matched:
1419 if mf.startswith(d):
1419 if mf.startswith(d):
1420 break
1420 break
1421 else:
1421 else:
1422 fail(f, _("no match under directory!"))
1422 fail(f, _("no match under directory!"))
1423 elif f not in self.dirstate:
1423 elif f not in self.dirstate:
1424 fail(f, _("file not tracked!"))
1424 fail(f, _("file not tracked!"))
1425
1425
1426 cctx = context.workingcommitctx(self, status,
1426 cctx = context.workingcommitctx(self, status,
1427 text, user, date, extra)
1427 text, user, date, extra)
1428
1428
1429 if (not force and not extra.get("close") and not merge
1429 if (not force and not extra.get("close") and not merge
1430 and not cctx.files()
1430 and not cctx.files()
1431 and wctx.branch() == wctx.p1().branch()):
1431 and wctx.branch() == wctx.p1().branch()):
1432 return None
1432 return None
1433
1433
1434 if merge and cctx.deleted():
1434 if merge and cctx.deleted():
1435 raise util.Abort(_("cannot commit merge with missing files"))
1435 raise util.Abort(_("cannot commit merge with missing files"))
1436
1436
1437 ms = mergemod.mergestate(self)
1437 ms = mergemod.mergestate(self)
1438 for f in status.modified:
1438 for f in status.modified:
1439 if f in ms and ms[f] == 'u':
1439 if f in ms and ms[f] == 'u':
1440 raise util.Abort(_('unresolved merge conflicts '
1440 raise util.Abort(_('unresolved merge conflicts '
1441 '(see "hg help resolve")'))
1441 '(see "hg help resolve")'))
1442
1442
1443 if editor:
1443 if editor:
1444 cctx._text = editor(self, cctx, subs)
1444 cctx._text = editor(self, cctx, subs)
1445 edited = (text != cctx._text)
1445 edited = (text != cctx._text)
1446
1446
1447 # Save commit message in case this transaction gets rolled back
1447 # Save commit message in case this transaction gets rolled back
1448 # (e.g. by a pretxncommit hook). Leave the content alone on
1448 # (e.g. by a pretxncommit hook). Leave the content alone on
1449 # the assumption that the user will use the same editor again.
1449 # the assumption that the user will use the same editor again.
1450 msgfn = self.savecommitmessage(cctx._text)
1450 msgfn = self.savecommitmessage(cctx._text)
1451
1451
1452 # commit subs and write new state
1452 # commit subs and write new state
1453 if subs:
1453 if subs:
1454 for s in sorted(commitsubs):
1454 for s in sorted(commitsubs):
1455 sub = wctx.sub(s)
1455 sub = wctx.sub(s)
1456 self.ui.status(_('committing subrepository %s\n') %
1456 self.ui.status(_('committing subrepository %s\n') %
1457 subrepo.subrelpath(sub))
1457 subrepo.subrelpath(sub))
1458 sr = sub.commit(cctx._text, user, date)
1458 sr = sub.commit(cctx._text, user, date)
1459 newstate[s] = (newstate[s][0], sr)
1459 newstate[s] = (newstate[s][0], sr)
1460 subrepo.writestate(self, newstate)
1460 subrepo.writestate(self, newstate)
1461
1461
1462 p1, p2 = self.dirstate.parents()
1462 p1, p2 = self.dirstate.parents()
1463 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1463 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1464 try:
1464 try:
1465 self.hook("precommit", throw=True, parent1=hookp1,
1465 self.hook("precommit", throw=True, parent1=hookp1,
1466 parent2=hookp2)
1466 parent2=hookp2)
1467 ret = self.commitctx(cctx, True)
1467 ret = self.commitctx(cctx, True)
1468 except: # re-raises
1468 except: # re-raises
1469 if edited:
1469 if edited:
1470 self.ui.write(
1470 self.ui.write(
1471 _('note: commit message saved in %s\n') % msgfn)
1471 _('note: commit message saved in %s\n') % msgfn)
1472 raise
1472 raise
1473
1473
1474 # update bookmarks, dirstate and mergestate
1474 # update bookmarks, dirstate and mergestate
1475 bookmarks.update(self, [p1, p2], ret)
1475 bookmarks.update(self, [p1, p2], ret)
1476 cctx.markcommitted(ret)
1476 cctx.markcommitted(ret)
1477 ms.reset()
1477 ms.reset()
1478 finally:
1478 finally:
1479 wlock.release()
1479 wlock.release()
1480
1480
1481 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1481 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1482 # hack for command that use a temporary commit (eg: histedit)
1482 # hack for command that use a temporary commit (eg: histedit)
1483 # temporary commit got stripped before hook release
1483 # temporary commit got stripped before hook release
1484 if node in self:
1484 if node in self:
1485 self.hook("commit", node=node, parent1=parent1,
1485 self.hook("commit", node=node, parent1=parent1,
1486 parent2=parent2)
1486 parent2=parent2)
1487 self._afterlock(commithook)
1487 self._afterlock(commithook)
1488 return ret
1488 return ret
1489
1489
1490 @unfilteredmethod
1490 @unfilteredmethod
1491 def commitctx(self, ctx, error=False):
1491 def commitctx(self, ctx, error=False):
1492 """Add a new revision to current repository.
1492 """Add a new revision to current repository.
1493 Revision information is passed via the context argument.
1493 Revision information is passed via the context argument.
1494 """
1494 """
1495
1495
1496 tr = None
1496 tr = None
1497 p1, p2 = ctx.p1(), ctx.p2()
1497 p1, p2 = ctx.p1(), ctx.p2()
1498 user = ctx.user()
1498 user = ctx.user()
1499
1499
1500 lock = self.lock()
1500 lock = self.lock()
1501 try:
1501 try:
1502 tr = self.transaction("commit")
1502 tr = self.transaction("commit")
1503 trp = weakref.proxy(tr)
1503 trp = weakref.proxy(tr)
1504
1504
1505 if ctx.files():
1505 if ctx.files():
1506 m1 = p1.manifest()
1506 m1 = p1.manifest()
1507 m2 = p2.manifest()
1507 m2 = p2.manifest()
1508 m = m1.copy()
1508 m = m1.copy()
1509
1509
1510 # check in files
1510 # check in files
1511 added = []
1511 added = []
1512 changed = []
1512 changed = []
1513 removed = list(ctx.removed())
1513 removed = list(ctx.removed())
1514 linkrev = len(self)
1514 linkrev = len(self)
1515 self.ui.note(_("committing files:\n"))
1515 self.ui.note(_("committing files:\n"))
1516 for f in sorted(ctx.modified() + ctx.added()):
1516 for f in sorted(ctx.modified() + ctx.added()):
1517 self.ui.note(f + "\n")
1517 self.ui.note(f + "\n")
1518 try:
1518 try:
1519 fctx = ctx[f]
1519 fctx = ctx[f]
1520 if fctx is None:
1520 if fctx is None:
1521 removed.append(f)
1521 removed.append(f)
1522 else:
1522 else:
1523 added.append(f)
1523 added.append(f)
1524 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1524 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1525 trp, changed)
1525 trp, changed)
1526 m.setflag(f, fctx.flags())
1526 m.setflag(f, fctx.flags())
1527 except OSError, inst:
1527 except OSError, inst:
1528 self.ui.warn(_("trouble committing %s!\n") % f)
1528 self.ui.warn(_("trouble committing %s!\n") % f)
1529 raise
1529 raise
1530 except IOError, inst:
1530 except IOError, inst:
1531 errcode = getattr(inst, 'errno', errno.ENOENT)
1531 errcode = getattr(inst, 'errno', errno.ENOENT)
1532 if error or errcode and errcode != errno.ENOENT:
1532 if error or errcode and errcode != errno.ENOENT:
1533 self.ui.warn(_("trouble committing %s!\n") % f)
1533 self.ui.warn(_("trouble committing %s!\n") % f)
1534 raise
1534 raise
1535
1535
1536 # update manifest
1536 # update manifest
1537 self.ui.note(_("committing manifest\n"))
1537 self.ui.note(_("committing manifest\n"))
1538 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1538 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1539 drop = [f for f in removed if f in m]
1539 drop = [f for f in removed if f in m]
1540 for f in drop:
1540 for f in drop:
1541 del m[f]
1541 del m[f]
1542 mn = self.manifest.add(m, trp, linkrev,
1542 mn = self.manifest.add(m, trp, linkrev,
1543 p1.manifestnode(), p2.manifestnode(),
1543 p1.manifestnode(), p2.manifestnode(),
1544 added, drop)
1544 added, drop)
1545 files = changed + removed
1545 files = changed + removed
1546 else:
1546 else:
1547 mn = p1.manifestnode()
1547 mn = p1.manifestnode()
1548 files = []
1548 files = []
1549
1549
1550 # update changelog
1550 # update changelog
1551 self.ui.note(_("committing changelog\n"))
1551 self.ui.note(_("committing changelog\n"))
1552 self.changelog.delayupdate(tr)
1552 self.changelog.delayupdate(tr)
1553 n = self.changelog.add(mn, files, ctx.description(),
1553 n = self.changelog.add(mn, files, ctx.description(),
1554 trp, p1.node(), p2.node(),
1554 trp, p1.node(), p2.node(),
1555 user, ctx.date(), ctx.extra().copy())
1555 user, ctx.date(), ctx.extra().copy())
1556 p = lambda: tr.writepending() and self.root or ""
1556 p = lambda: tr.writepending() and self.root or ""
1557 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1557 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1558 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1558 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1559 parent2=xp2, pending=p)
1559 parent2=xp2, pending=p)
1560 # set the new commit is proper phase
1560 # set the new commit is proper phase
1561 targetphase = subrepo.newcommitphase(self.ui, ctx)
1561 targetphase = subrepo.newcommitphase(self.ui, ctx)
1562 if targetphase:
1562 if targetphase:
1563 # retract boundary do not alter parent changeset.
1563 # retract boundary do not alter parent changeset.
1564 # if a parent have higher the resulting phase will
1564 # if a parent have higher the resulting phase will
1565 # be compliant anyway
1565 # be compliant anyway
1566 #
1566 #
1567 # if minimal phase was 0 we don't need to retract anything
1567 # if minimal phase was 0 we don't need to retract anything
1568 phases.retractboundary(self, tr, targetphase, [n])
1568 phases.retractboundary(self, tr, targetphase, [n])
1569 tr.close()
1569 tr.close()
1570 branchmap.updatecache(self.filtered('served'))
1570 branchmap.updatecache(self.filtered('served'))
1571 return n
1571 return n
1572 finally:
1572 finally:
1573 if tr:
1573 if tr:
1574 tr.release()
1574 tr.release()
1575 lock.release()
1575 lock.release()
1576
1576
1577 @unfilteredmethod
1577 @unfilteredmethod
1578 def destroying(self):
1578 def destroying(self):
1579 '''Inform the repository that nodes are about to be destroyed.
1579 '''Inform the repository that nodes are about to be destroyed.
1580 Intended for use by strip and rollback, so there's a common
1580 Intended for use by strip and rollback, so there's a common
1581 place for anything that has to be done before destroying history.
1581 place for anything that has to be done before destroying history.
1582
1582
1583 This is mostly useful for saving state that is in memory and waiting
1583 This is mostly useful for saving state that is in memory and waiting
1584 to be flushed when the current lock is released. Because a call to
1584 to be flushed when the current lock is released. Because a call to
1585 destroyed is imminent, the repo will be invalidated causing those
1585 destroyed is imminent, the repo will be invalidated causing those
1586 changes to stay in memory (waiting for the next unlock), or vanish
1586 changes to stay in memory (waiting for the next unlock), or vanish
1587 completely.
1587 completely.
1588 '''
1588 '''
1589 # When using the same lock to commit and strip, the phasecache is left
1589 # When using the same lock to commit and strip, the phasecache is left
1590 # dirty after committing. Then when we strip, the repo is invalidated,
1590 # dirty after committing. Then when we strip, the repo is invalidated,
1591 # causing those changes to disappear.
1591 # causing those changes to disappear.
1592 if '_phasecache' in vars(self):
1592 if '_phasecache' in vars(self):
1593 self._phasecache.write()
1593 self._phasecache.write()
1594
1594
1595 @unfilteredmethod
1595 @unfilteredmethod
1596 def destroyed(self):
1596 def destroyed(self):
1597 '''Inform the repository that nodes have been destroyed.
1597 '''Inform the repository that nodes have been destroyed.
1598 Intended for use by strip and rollback, so there's a common
1598 Intended for use by strip and rollback, so there's a common
1599 place for anything that has to be done after destroying history.
1599 place for anything that has to be done after destroying history.
1600 '''
1600 '''
1601 # When one tries to:
1601 # When one tries to:
1602 # 1) destroy nodes thus calling this method (e.g. strip)
1602 # 1) destroy nodes thus calling this method (e.g. strip)
1603 # 2) use phasecache somewhere (e.g. commit)
1603 # 2) use phasecache somewhere (e.g. commit)
1604 #
1604 #
1605 # then 2) will fail because the phasecache contains nodes that were
1605 # then 2) will fail because the phasecache contains nodes that were
1606 # removed. We can either remove phasecache from the filecache,
1606 # removed. We can either remove phasecache from the filecache,
1607 # causing it to reload next time it is accessed, or simply filter
1607 # causing it to reload next time it is accessed, or simply filter
1608 # the removed nodes now and write the updated cache.
1608 # the removed nodes now and write the updated cache.
1609 self._phasecache.filterunknown(self)
1609 self._phasecache.filterunknown(self)
1610 self._phasecache.write()
1610 self._phasecache.write()
1611
1611
1612 # update the 'served' branch cache to help read only server process
1612 # update the 'served' branch cache to help read only server process
1613 # Thanks to branchcache collaboration this is done from the nearest
1613 # Thanks to branchcache collaboration this is done from the nearest
1614 # filtered subset and it is expected to be fast.
1614 # filtered subset and it is expected to be fast.
1615 branchmap.updatecache(self.filtered('served'))
1615 branchmap.updatecache(self.filtered('served'))
1616
1616
1617 # Ensure the persistent tag cache is updated. Doing it now
1617 # Ensure the persistent tag cache is updated. Doing it now
1618 # means that the tag cache only has to worry about destroyed
1618 # means that the tag cache only has to worry about destroyed
1619 # heads immediately after a strip/rollback. That in turn
1619 # heads immediately after a strip/rollback. That in turn
1620 # guarantees that "cachetip == currenttip" (comparing both rev
1620 # guarantees that "cachetip == currenttip" (comparing both rev
1621 # and node) always means no nodes have been added or destroyed.
1621 # and node) always means no nodes have been added or destroyed.
1622
1622
1623 # XXX this is suboptimal when qrefresh'ing: we strip the current
1623 # XXX this is suboptimal when qrefresh'ing: we strip the current
1624 # head, refresh the tag cache, then immediately add a new head.
1624 # head, refresh the tag cache, then immediately add a new head.
1625 # But I think doing it this way is necessary for the "instant
1625 # But I think doing it this way is necessary for the "instant
1626 # tag cache retrieval" case to work.
1626 # tag cache retrieval" case to work.
1627 self.invalidate()
1627 self.invalidate()
1628
1628
1629 def walk(self, match, node=None):
1629 def walk(self, match, node=None):
1630 '''
1630 '''
1631 walk recursively through the directory tree or a given
1631 walk recursively through the directory tree or a given
1632 changeset, finding all files matched by the match
1632 changeset, finding all files matched by the match
1633 function
1633 function
1634 '''
1634 '''
1635 return self[node].walk(match)
1635 return self[node].walk(match)
1636
1636
1637 def status(self, node1='.', node2=None, match=None,
1637 def status(self, node1='.', node2=None, match=None,
1638 ignored=False, clean=False, unknown=False,
1638 ignored=False, clean=False, unknown=False,
1639 listsubrepos=False):
1639 listsubrepos=False):
1640 '''a convenience method that calls node1.status(node2)'''
1640 '''a convenience method that calls node1.status(node2)'''
1641 return self[node1].status(node2, match, ignored, clean, unknown,
1641 return self[node1].status(node2, match, ignored, clean, unknown,
1642 listsubrepos)
1642 listsubrepos)
1643
1643
1644 def heads(self, start=None):
1644 def heads(self, start=None):
1645 heads = self.changelog.heads(start)
1645 heads = self.changelog.heads(start)
1646 # sort the output in rev descending order
1646 # sort the output in rev descending order
1647 return sorted(heads, key=self.changelog.rev, reverse=True)
1647 return sorted(heads, key=self.changelog.rev, reverse=True)
1648
1648
1649 def branchheads(self, branch=None, start=None, closed=False):
1649 def branchheads(self, branch=None, start=None, closed=False):
1650 '''return a (possibly filtered) list of heads for the given branch
1650 '''return a (possibly filtered) list of heads for the given branch
1651
1651
1652 Heads are returned in topological order, from newest to oldest.
1652 Heads are returned in topological order, from newest to oldest.
1653 If branch is None, use the dirstate branch.
1653 If branch is None, use the dirstate branch.
1654 If start is not None, return only heads reachable from start.
1654 If start is not None, return only heads reachable from start.
1655 If closed is True, return heads that are marked as closed as well.
1655 If closed is True, return heads that are marked as closed as well.
1656 '''
1656 '''
1657 if branch is None:
1657 if branch is None:
1658 branch = self[None].branch()
1658 branch = self[None].branch()
1659 branches = self.branchmap()
1659 branches = self.branchmap()
1660 if branch not in branches:
1660 if branch not in branches:
1661 return []
1661 return []
1662 # the cache returns heads ordered lowest to highest
1662 # the cache returns heads ordered lowest to highest
1663 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1663 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1664 if start is not None:
1664 if start is not None:
1665 # filter out the heads that cannot be reached from startrev
1665 # filter out the heads that cannot be reached from startrev
1666 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1666 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1667 bheads = [h for h in bheads if h in fbheads]
1667 bheads = [h for h in bheads if h in fbheads]
1668 return bheads
1668 return bheads
1669
1669
1670 def branches(self, nodes):
1670 def branches(self, nodes):
1671 if not nodes:
1671 if not nodes:
1672 nodes = [self.changelog.tip()]
1672 nodes = [self.changelog.tip()]
1673 b = []
1673 b = []
1674 for n in nodes:
1674 for n in nodes:
1675 t = n
1675 t = n
1676 while True:
1676 while True:
1677 p = self.changelog.parents(n)
1677 p = self.changelog.parents(n)
1678 if p[1] != nullid or p[0] == nullid:
1678 if p[1] != nullid or p[0] == nullid:
1679 b.append((t, n, p[0], p[1]))
1679 b.append((t, n, p[0], p[1]))
1680 break
1680 break
1681 n = p[0]
1681 n = p[0]
1682 return b
1682 return b
1683
1683
1684 def between(self, pairs):
1684 def between(self, pairs):
1685 r = []
1685 r = []
1686
1686
1687 for top, bottom in pairs:
1687 for top, bottom in pairs:
1688 n, l, i = top, [], 0
1688 n, l, i = top, [], 0
1689 f = 1
1689 f = 1
1690
1690
1691 while n != bottom and n != nullid:
1691 while n != bottom and n != nullid:
1692 p = self.changelog.parents(n)[0]
1692 p = self.changelog.parents(n)[0]
1693 if i == f:
1693 if i == f:
1694 l.append(n)
1694 l.append(n)
1695 f = f * 2
1695 f = f * 2
1696 n = p
1696 n = p
1697 i += 1
1697 i += 1
1698
1698
1699 r.append(l)
1699 r.append(l)
1700
1700
1701 return r
1701 return r
1702
1702
1703 def checkpush(self, pushop):
1703 def checkpush(self, pushop):
1704 """Extensions can override this function if additional checks have
1704 """Extensions can override this function if additional checks have
1705 to be performed before pushing, or call it if they override push
1705 to be performed before pushing, or call it if they override push
1706 command.
1706 command.
1707 """
1707 """
1708 pass
1708 pass
1709
1709
1710 @unfilteredpropertycache
1710 @unfilteredpropertycache
1711 def prepushoutgoinghooks(self):
1711 def prepushoutgoinghooks(self):
1712 """Return util.hooks consists of "(repo, remote, outgoing)"
1712 """Return util.hooks consists of "(repo, remote, outgoing)"
1713 functions, which are called before pushing changesets.
1713 functions, which are called before pushing changesets.
1714 """
1714 """
1715 return util.hooks()
1715 return util.hooks()
1716
1716
1717 def stream_in(self, remote, requirements):
1717 def stream_in(self, remote, requirements):
1718 lock = self.lock()
1718 lock = self.lock()
1719 try:
1719 try:
1720 # Save remote branchmap. We will use it later
1720 # Save remote branchmap. We will use it later
1721 # to speed up branchcache creation
1721 # to speed up branchcache creation
1722 rbranchmap = None
1722 rbranchmap = None
1723 if remote.capable("branchmap"):
1723 if remote.capable("branchmap"):
1724 rbranchmap = remote.branchmap()
1724 rbranchmap = remote.branchmap()
1725
1725
1726 fp = remote.stream_out()
1726 fp = remote.stream_out()
1727 l = fp.readline()
1727 l = fp.readline()
1728 try:
1728 try:
1729 resp = int(l)
1729 resp = int(l)
1730 except ValueError:
1730 except ValueError:
1731 raise error.ResponseError(
1731 raise error.ResponseError(
1732 _('unexpected response from remote server:'), l)
1732 _('unexpected response from remote server:'), l)
1733 if resp == 1:
1733 if resp == 1:
1734 raise util.Abort(_('operation forbidden by server'))
1734 raise util.Abort(_('operation forbidden by server'))
1735 elif resp == 2:
1735 elif resp == 2:
1736 raise util.Abort(_('locking the remote repository failed'))
1736 raise util.Abort(_('locking the remote repository failed'))
1737 elif resp != 0:
1737 elif resp != 0:
1738 raise util.Abort(_('the server sent an unknown error code'))
1738 raise util.Abort(_('the server sent an unknown error code'))
1739 self.ui.status(_('streaming all changes\n'))
1739 self.ui.status(_('streaming all changes\n'))
1740 l = fp.readline()
1740 l = fp.readline()
1741 try:
1741 try:
1742 total_files, total_bytes = map(int, l.split(' ', 1))
1742 total_files, total_bytes = map(int, l.split(' ', 1))
1743 except (ValueError, TypeError):
1743 except (ValueError, TypeError):
1744 raise error.ResponseError(
1744 raise error.ResponseError(
1745 _('unexpected response from remote server:'), l)
1745 _('unexpected response from remote server:'), l)
1746 self.ui.status(_('%d files to transfer, %s of data\n') %
1746 self.ui.status(_('%d files to transfer, %s of data\n') %
1747 (total_files, util.bytecount(total_bytes)))
1747 (total_files, util.bytecount(total_bytes)))
1748 handled_bytes = 0
1748 handled_bytes = 0
1749 self.ui.progress(_('clone'), 0, total=total_bytes)
1749 self.ui.progress(_('clone'), 0, total=total_bytes)
1750 start = time.time()
1750 start = time.time()
1751
1751
1752 tr = self.transaction(_('clone'))
1752 tr = self.transaction(_('clone'))
1753 try:
1753 try:
1754 for i in xrange(total_files):
1754 for i in xrange(total_files):
1755 # XXX doesn't support '\n' or '\r' in filenames
1755 # XXX doesn't support '\n' or '\r' in filenames
1756 l = fp.readline()
1756 l = fp.readline()
1757 try:
1757 try:
1758 name, size = l.split('\0', 1)
1758 name, size = l.split('\0', 1)
1759 size = int(size)
1759 size = int(size)
1760 except (ValueError, TypeError):
1760 except (ValueError, TypeError):
1761 raise error.ResponseError(
1761 raise error.ResponseError(
1762 _('unexpected response from remote server:'), l)
1762 _('unexpected response from remote server:'), l)
1763 if self.ui.debugflag:
1763 if self.ui.debugflag:
1764 self.ui.debug('adding %s (%s)\n' %
1764 self.ui.debug('adding %s (%s)\n' %
1765 (name, util.bytecount(size)))
1765 (name, util.bytecount(size)))
1766 # for backwards compat, name was partially encoded
1766 # for backwards compat, name was partially encoded
1767 ofp = self.svfs(store.decodedir(name), 'w')
1767 ofp = self.svfs(store.decodedir(name), 'w')
1768 for chunk in util.filechunkiter(fp, limit=size):
1768 for chunk in util.filechunkiter(fp, limit=size):
1769 handled_bytes += len(chunk)
1769 handled_bytes += len(chunk)
1770 self.ui.progress(_('clone'), handled_bytes,
1770 self.ui.progress(_('clone'), handled_bytes,
1771 total=total_bytes)
1771 total=total_bytes)
1772 ofp.write(chunk)
1772 ofp.write(chunk)
1773 ofp.close()
1773 ofp.close()
1774 tr.close()
1774 tr.close()
1775 finally:
1775 finally:
1776 tr.release()
1776 tr.release()
1777
1777
1778 # Writing straight to files circumvented the inmemory caches
1778 # Writing straight to files circumvented the inmemory caches
1779 self.invalidate()
1779 self.invalidate()
1780
1780
1781 elapsed = time.time() - start
1781 elapsed = time.time() - start
1782 if elapsed <= 0:
1782 if elapsed <= 0:
1783 elapsed = 0.001
1783 elapsed = 0.001
1784 self.ui.progress(_('clone'), None)
1784 self.ui.progress(_('clone'), None)
1785 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1785 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1786 (util.bytecount(total_bytes), elapsed,
1786 (util.bytecount(total_bytes), elapsed,
1787 util.bytecount(total_bytes / elapsed)))
1787 util.bytecount(total_bytes / elapsed)))
1788
1788
1789 # new requirements = old non-format requirements +
1789 # new requirements = old non-format requirements +
1790 # new format-related
1790 # new format-related
1791 # requirements from the streamed-in repository
1791 # requirements from the streamed-in repository
1792 requirements.update(set(self.requirements) - self.supportedformats)
1792 requirements.update(set(self.requirements) - self.supportedformats)
1793 self._applyrequirements(requirements)
1793 self._applyrequirements(requirements)
1794 self._writerequirements()
1794 self._writerequirements()
1795
1795
1796 if rbranchmap:
1796 if rbranchmap:
1797 rbheads = []
1797 rbheads = []
1798 closed = []
1798 closed = []
1799 for bheads in rbranchmap.itervalues():
1799 for bheads in rbranchmap.itervalues():
1800 rbheads.extend(bheads)
1800 rbheads.extend(bheads)
1801 for h in bheads:
1801 for h in bheads:
1802 r = self.changelog.rev(h)
1802 r = self.changelog.rev(h)
1803 b, c = self.changelog.branchinfo(r)
1803 b, c = self.changelog.branchinfo(r)
1804 if c:
1804 if c:
1805 closed.append(h)
1805 closed.append(h)
1806
1806
1807 if rbheads:
1807 if rbheads:
1808 rtiprev = max((int(self.changelog.rev(node))
1808 rtiprev = max((int(self.changelog.rev(node))
1809 for node in rbheads))
1809 for node in rbheads))
1810 cache = branchmap.branchcache(rbranchmap,
1810 cache = branchmap.branchcache(rbranchmap,
1811 self[rtiprev].node(),
1811 self[rtiprev].node(),
1812 rtiprev,
1812 rtiprev,
1813 closednodes=closed)
1813 closednodes=closed)
1814 # Try to stick it as low as possible
1814 # Try to stick it as low as possible
1815 # filter above served are unlikely to be fetch from a clone
1815 # filter above served are unlikely to be fetch from a clone
1816 for candidate in ('base', 'immutable', 'served'):
1816 for candidate in ('base', 'immutable', 'served'):
1817 rview = self.filtered(candidate)
1817 rview = self.filtered(candidate)
1818 if cache.validfor(rview):
1818 if cache.validfor(rview):
1819 self._branchcaches[candidate] = cache
1819 self._branchcaches[candidate] = cache
1820 cache.write(rview)
1820 cache.write(rview)
1821 break
1821 break
1822 self.invalidate()
1822 self.invalidate()
1823 return len(self.heads()) + 1
1823 return len(self.heads()) + 1
1824 finally:
1824 finally:
1825 lock.release()
1825 lock.release()
1826
1826
1827 def clone(self, remote, heads=[], stream=None):
1827 def clone(self, remote, heads=[], stream=None):
1828 '''clone remote repository.
1828 '''clone remote repository.
1829
1829
1830 keyword arguments:
1830 keyword arguments:
1831 heads: list of revs to clone (forces use of pull)
1831 heads: list of revs to clone (forces use of pull)
1832 stream: use streaming clone if possible'''
1832 stream: use streaming clone if possible'''
1833
1833
1834 # now, all clients that can request uncompressed clones can
1834 # now, all clients that can request uncompressed clones can
1835 # read repo formats supported by all servers that can serve
1835 # read repo formats supported by all servers that can serve
1836 # them.
1836 # them.
1837
1837
1838 # if revlog format changes, client will have to check version
1838 # if revlog format changes, client will have to check version
1839 # and format flags on "stream" capability, and use
1839 # and format flags on "stream" capability, and use
1840 # uncompressed only if compatible.
1840 # uncompressed only if compatible.
1841
1841
1842 if stream is None:
1842 if stream is None:
1843 # if the server explicitly prefers to stream (for fast LANs)
1843 # if the server explicitly prefers to stream (for fast LANs)
1844 stream = remote.capable('stream-preferred')
1844 stream = remote.capable('stream-preferred')
1845
1845
1846 if stream and not heads:
1846 if stream and not heads:
1847 # 'stream' means remote revlog format is revlogv1 only
1847 # 'stream' means remote revlog format is revlogv1 only
1848 if remote.capable('stream'):
1848 if remote.capable('stream'):
1849 self.stream_in(remote, set(('revlogv1',)))
1849 self.stream_in(remote, set(('revlogv1',)))
1850 else:
1850 else:
1851 # otherwise, 'streamreqs' contains the remote revlog format
1851 # otherwise, 'streamreqs' contains the remote revlog format
1852 streamreqs = remote.capable('streamreqs')
1852 streamreqs = remote.capable('streamreqs')
1853 if streamreqs:
1853 if streamreqs:
1854 streamreqs = set(streamreqs.split(','))
1854 streamreqs = set(streamreqs.split(','))
1855 # if we support it, stream in and adjust our requirements
1855 # if we support it, stream in and adjust our requirements
1856 if not streamreqs - self.supportedformats:
1856 if not streamreqs - self.supportedformats:
1857 self.stream_in(remote, streamreqs)
1857 self.stream_in(remote, streamreqs)
1858
1858
1859 quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
1859 quiet = self.ui.backupconfig('ui', 'quietbookmarkmove')
1860 try:
1860 try:
1861 self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
1861 self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone')
1862 ret = exchange.pull(self, remote, heads).cgresult
1862 ret = exchange.pull(self, remote, heads).cgresult
1863 finally:
1863 finally:
1864 self.ui.restoreconfig(quiet)
1864 self.ui.restoreconfig(quiet)
1865 return ret
1865 return ret
1866
1866
1867 def pushkey(self, namespace, key, old, new):
1867 def pushkey(self, namespace, key, old, new):
1868 try:
1868 try:
1869 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1869 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1870 old=old, new=new)
1870 old=old, new=new)
1871 except error.HookAbort, exc:
1871 except error.HookAbort, exc:
1872 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1872 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1873 if exc.hint:
1873 if exc.hint:
1874 self.ui.write_err(_("(%s)\n") % exc.hint)
1874 self.ui.write_err(_("(%s)\n") % exc.hint)
1875 return False
1875 return False
1876 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1876 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1877 ret = pushkey.push(self, namespace, key, old, new)
1877 ret = pushkey.push(self, namespace, key, old, new)
1878 def runhook():
1878 def runhook():
1879 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1879 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1880 ret=ret)
1880 ret=ret)
1881 self._afterlock(runhook)
1881 self._afterlock(runhook)
1882 return ret
1882 return ret
1883
1883
1884 def listkeys(self, namespace):
1884 def listkeys(self, namespace):
1885 self.hook('prelistkeys', throw=True, namespace=namespace)
1885 self.hook('prelistkeys', throw=True, namespace=namespace)
1886 self.ui.debug('listing keys for "%s"\n' % namespace)
1886 self.ui.debug('listing keys for "%s"\n' % namespace)
1887 values = pushkey.list(self, namespace)
1887 values = pushkey.list(self, namespace)
1888 self.hook('listkeys', namespace=namespace, values=values)
1888 self.hook('listkeys', namespace=namespace, values=values)
1889 return values
1889 return values
1890
1890
1891 def debugwireargs(self, one, two, three=None, four=None, five=None):
1891 def debugwireargs(self, one, two, three=None, four=None, five=None):
1892 '''used to test argument passing over the wire'''
1892 '''used to test argument passing over the wire'''
1893 return "%s %s %s %s %s" % (one, two, three, four, five)
1893 return "%s %s %s %s %s" % (one, two, three, four, five)
1894
1894
1895 def savecommitmessage(self, text):
1895 def savecommitmessage(self, text):
1896 fp = self.vfs('last-message.txt', 'wb')
1896 fp = self.vfs('last-message.txt', 'wb')
1897 try:
1897 try:
1898 fp.write(text)
1898 fp.write(text)
1899 finally:
1899 finally:
1900 fp.close()
1900 fp.close()
1901 return self.pathto(fp.name[len(self.root) + 1:])
1901 return self.pathto(fp.name[len(self.root) + 1:])
1902
1902
1903 # used to avoid circular references so destructors work
1903 # used to avoid circular references so destructors work
1904 def aftertrans(files):
1904 def aftertrans(files):
1905 renamefiles = [tuple(t) for t in files]
1905 renamefiles = [tuple(t) for t in files]
1906 def a():
1906 def a():
1907 for vfs, src, dest in renamefiles:
1907 for vfs, src, dest in renamefiles:
1908 try:
1908 try:
1909 vfs.rename(src, dest)
1909 vfs.rename(src, dest)
1910 except OSError: # journal file does not yet exist
1910 except OSError: # journal file does not yet exist
1911 pass
1911 pass
1912 return a
1912 return a
1913
1913
1914 def undoname(fn):
1914 def undoname(fn):
1915 base, name = os.path.split(fn)
1915 base, name = os.path.split(fn)
1916 assert name.startswith('journal')
1916 assert name.startswith('journal')
1917 return os.path.join(base, name.replace('journal', 'undo', 1))
1917 return os.path.join(base, name.replace('journal', 'undo', 1))
1918
1918
1919 def instance(ui, path, create):
1919 def instance(ui, path, create):
1920 return localrepository(ui, util.urllocalpath(path), create)
1920 return localrepository(ui, util.urllocalpath(path), create)
1921
1921
1922 def islocal(path):
1922 def islocal(path):
1923 return True
1923 return True
@@ -1,1791 +1,1803 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 import copy
8 import copy
9 import errno, os, re, shutil, posixpath, sys
9 import errno, os, re, shutil, posixpath, sys
10 import xml.dom.minidom
10 import xml.dom.minidom
11 import stat, subprocess, tarfile
11 import stat, subprocess, tarfile
12 from i18n import _
12 from i18n import _
13 import config, util, node, error, cmdutil, scmutil, match as matchmod
13 import config, util, node, error, cmdutil, scmutil, match as matchmod
14 import phases
14 import phases
15 import pathutil
15 import pathutil
16 import exchange
16 import exchange
17 hg = None
17 hg = None
18 propertycache = util.propertycache
18 propertycache = util.propertycache
19
19
20 nullstate = ('', '', 'empty')
20 nullstate = ('', '', 'empty')
21
21
22 def _expandedabspath(path):
22 def _expandedabspath(path):
23 '''
23 '''
24 get a path or url and if it is a path expand it and return an absolute path
24 get a path or url and if it is a path expand it and return an absolute path
25 '''
25 '''
26 expandedpath = util.urllocalpath(util.expandpath(path))
26 expandedpath = util.urllocalpath(util.expandpath(path))
27 u = util.url(expandedpath)
27 u = util.url(expandedpath)
28 if not u.scheme:
28 if not u.scheme:
29 path = util.normpath(os.path.abspath(u.path))
29 path = util.normpath(os.path.abspath(u.path))
30 return path
30 return path
31
31
32 def _getstorehashcachename(remotepath):
32 def _getstorehashcachename(remotepath):
33 '''get a unique filename for the store hash cache of a remote repository'''
33 '''get a unique filename for the store hash cache of a remote repository'''
34 return util.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
34 return util.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
35
35
36 class SubrepoAbort(error.Abort):
36 class SubrepoAbort(error.Abort):
37 """Exception class used to avoid handling a subrepo error more than once"""
37 """Exception class used to avoid handling a subrepo error more than once"""
38 def __init__(self, *args, **kw):
38 def __init__(self, *args, **kw):
39 error.Abort.__init__(self, *args, **kw)
39 error.Abort.__init__(self, *args, **kw)
40 self.subrepo = kw.get('subrepo')
40 self.subrepo = kw.get('subrepo')
41 self.cause = kw.get('cause')
41 self.cause = kw.get('cause')
42
42
43 def annotatesubrepoerror(func):
43 def annotatesubrepoerror(func):
44 def decoratedmethod(self, *args, **kargs):
44 def decoratedmethod(self, *args, **kargs):
45 try:
45 try:
46 res = func(self, *args, **kargs)
46 res = func(self, *args, **kargs)
47 except SubrepoAbort, ex:
47 except SubrepoAbort, ex:
48 # This exception has already been handled
48 # This exception has already been handled
49 raise ex
49 raise ex
50 except error.Abort, ex:
50 except error.Abort, ex:
51 subrepo = subrelpath(self)
51 subrepo = subrelpath(self)
52 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
52 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
53 # avoid handling this exception by raising a SubrepoAbort exception
53 # avoid handling this exception by raising a SubrepoAbort exception
54 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
54 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
55 cause=sys.exc_info())
55 cause=sys.exc_info())
56 return res
56 return res
57 return decoratedmethod
57 return decoratedmethod
58
58
59 def state(ctx, ui):
59 def state(ctx, ui):
60 """return a state dict, mapping subrepo paths configured in .hgsub
60 """return a state dict, mapping subrepo paths configured in .hgsub
61 to tuple: (source from .hgsub, revision from .hgsubstate, kind
61 to tuple: (source from .hgsub, revision from .hgsubstate, kind
62 (key in types dict))
62 (key in types dict))
63 """
63 """
64 p = config.config()
64 p = config.config()
65 def read(f, sections=None, remap=None):
65 def read(f, sections=None, remap=None):
66 if f in ctx:
66 if f in ctx:
67 try:
67 try:
68 data = ctx[f].data()
68 data = ctx[f].data()
69 except IOError, err:
69 except IOError, err:
70 if err.errno != errno.ENOENT:
70 if err.errno != errno.ENOENT:
71 raise
71 raise
72 # handle missing subrepo spec files as removed
72 # handle missing subrepo spec files as removed
73 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
73 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
74 return
74 return
75 p.parse(f, data, sections, remap, read)
75 p.parse(f, data, sections, remap, read)
76 else:
76 else:
77 raise util.Abort(_("subrepo spec file %s not found") % f)
77 raise util.Abort(_("subrepo spec file %s not found") % f)
78
78
79 if '.hgsub' in ctx:
79 if '.hgsub' in ctx:
80 read('.hgsub')
80 read('.hgsub')
81
81
82 for path, src in ui.configitems('subpaths'):
82 for path, src in ui.configitems('subpaths'):
83 p.set('subpaths', path, src, ui.configsource('subpaths', path))
83 p.set('subpaths', path, src, ui.configsource('subpaths', path))
84
84
85 rev = {}
85 rev = {}
86 if '.hgsubstate' in ctx:
86 if '.hgsubstate' in ctx:
87 try:
87 try:
88 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
88 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
89 l = l.lstrip()
89 l = l.lstrip()
90 if not l:
90 if not l:
91 continue
91 continue
92 try:
92 try:
93 revision, path = l.split(" ", 1)
93 revision, path = l.split(" ", 1)
94 except ValueError:
94 except ValueError:
95 raise util.Abort(_("invalid subrepository revision "
95 raise util.Abort(_("invalid subrepository revision "
96 "specifier in .hgsubstate line %d")
96 "specifier in .hgsubstate line %d")
97 % (i + 1))
97 % (i + 1))
98 rev[path] = revision
98 rev[path] = revision
99 except IOError, err:
99 except IOError, err:
100 if err.errno != errno.ENOENT:
100 if err.errno != errno.ENOENT:
101 raise
101 raise
102
102
103 def remap(src):
103 def remap(src):
104 for pattern, repl in p.items('subpaths'):
104 for pattern, repl in p.items('subpaths'):
105 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
105 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
106 # does a string decode.
106 # does a string decode.
107 repl = repl.encode('string-escape')
107 repl = repl.encode('string-escape')
108 # However, we still want to allow back references to go
108 # However, we still want to allow back references to go
109 # through unharmed, so we turn r'\\1' into r'\1'. Again,
109 # through unharmed, so we turn r'\\1' into r'\1'. Again,
110 # extra escapes are needed because re.sub string decodes.
110 # extra escapes are needed because re.sub string decodes.
111 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
111 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
112 try:
112 try:
113 src = re.sub(pattern, repl, src, 1)
113 src = re.sub(pattern, repl, src, 1)
114 except re.error, e:
114 except re.error, e:
115 raise util.Abort(_("bad subrepository pattern in %s: %s")
115 raise util.Abort(_("bad subrepository pattern in %s: %s")
116 % (p.source('subpaths', pattern), e))
116 % (p.source('subpaths', pattern), e))
117 return src
117 return src
118
118
119 state = {}
119 state = {}
120 for path, src in p[''].items():
120 for path, src in p[''].items():
121 kind = 'hg'
121 kind = 'hg'
122 if src.startswith('['):
122 if src.startswith('['):
123 if ']' not in src:
123 if ']' not in src:
124 raise util.Abort(_('missing ] in subrepo source'))
124 raise util.Abort(_('missing ] in subrepo source'))
125 kind, src = src.split(']', 1)
125 kind, src = src.split(']', 1)
126 kind = kind[1:]
126 kind = kind[1:]
127 src = src.lstrip() # strip any extra whitespace after ']'
127 src = src.lstrip() # strip any extra whitespace after ']'
128
128
129 if not util.url(src).isabs():
129 if not util.url(src).isabs():
130 parent = _abssource(ctx.repo(), abort=False)
130 parent = _abssource(ctx.repo(), abort=False)
131 if parent:
131 if parent:
132 parent = util.url(parent)
132 parent = util.url(parent)
133 parent.path = posixpath.join(parent.path or '', src)
133 parent.path = posixpath.join(parent.path or '', src)
134 parent.path = posixpath.normpath(parent.path)
134 parent.path = posixpath.normpath(parent.path)
135 joined = str(parent)
135 joined = str(parent)
136 # Remap the full joined path and use it if it changes,
136 # Remap the full joined path and use it if it changes,
137 # else remap the original source.
137 # else remap the original source.
138 remapped = remap(joined)
138 remapped = remap(joined)
139 if remapped == joined:
139 if remapped == joined:
140 src = remap(src)
140 src = remap(src)
141 else:
141 else:
142 src = remapped
142 src = remapped
143
143
144 src = remap(src)
144 src = remap(src)
145 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
145 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
146
146
147 return state
147 return state
148
148
149 def writestate(repo, state):
149 def writestate(repo, state):
150 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
150 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
151 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
151 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
152 repo.wwrite('.hgsubstate', ''.join(lines), '')
152 repo.wwrite('.hgsubstate', ''.join(lines), '')
153
153
154 def submerge(repo, wctx, mctx, actx, overwrite):
154 def submerge(repo, wctx, mctx, actx, overwrite):
155 """delegated from merge.applyupdates: merging of .hgsubstate file
155 """delegated from merge.applyupdates: merging of .hgsubstate file
156 in working context, merging context and ancestor context"""
156 in working context, merging context and ancestor context"""
157 if mctx == actx: # backwards?
157 if mctx == actx: # backwards?
158 actx = wctx.p1()
158 actx = wctx.p1()
159 s1 = wctx.substate
159 s1 = wctx.substate
160 s2 = mctx.substate
160 s2 = mctx.substate
161 sa = actx.substate
161 sa = actx.substate
162 sm = {}
162 sm = {}
163
163
164 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
164 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
165
165
166 def debug(s, msg, r=""):
166 def debug(s, msg, r=""):
167 if r:
167 if r:
168 r = "%s:%s:%s" % r
168 r = "%s:%s:%s" % r
169 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
169 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
170
170
171 for s, l in sorted(s1.iteritems()):
171 for s, l in sorted(s1.iteritems()):
172 a = sa.get(s, nullstate)
172 a = sa.get(s, nullstate)
173 ld = l # local state with possible dirty flag for compares
173 ld = l # local state with possible dirty flag for compares
174 if wctx.sub(s).dirty():
174 if wctx.sub(s).dirty():
175 ld = (l[0], l[1] + "+")
175 ld = (l[0], l[1] + "+")
176 if wctx == actx: # overwrite
176 if wctx == actx: # overwrite
177 a = ld
177 a = ld
178
178
179 if s in s2:
179 if s in s2:
180 r = s2[s]
180 r = s2[s]
181 if ld == r or r == a: # no change or local is newer
181 if ld == r or r == a: # no change or local is newer
182 sm[s] = l
182 sm[s] = l
183 continue
183 continue
184 elif ld == a: # other side changed
184 elif ld == a: # other side changed
185 debug(s, "other changed, get", r)
185 debug(s, "other changed, get", r)
186 wctx.sub(s).get(r, overwrite)
186 wctx.sub(s).get(r, overwrite)
187 sm[s] = r
187 sm[s] = r
188 elif ld[0] != r[0]: # sources differ
188 elif ld[0] != r[0]: # sources differ
189 if repo.ui.promptchoice(
189 if repo.ui.promptchoice(
190 _(' subrepository sources for %s differ\n'
190 _(' subrepository sources for %s differ\n'
191 'use (l)ocal source (%s) or (r)emote source (%s)?'
191 'use (l)ocal source (%s) or (r)emote source (%s)?'
192 '$$ &Local $$ &Remote') % (s, l[0], r[0]), 0):
192 '$$ &Local $$ &Remote') % (s, l[0], r[0]), 0):
193 debug(s, "prompt changed, get", r)
193 debug(s, "prompt changed, get", r)
194 wctx.sub(s).get(r, overwrite)
194 wctx.sub(s).get(r, overwrite)
195 sm[s] = r
195 sm[s] = r
196 elif ld[1] == a[1]: # local side is unchanged
196 elif ld[1] == a[1]: # local side is unchanged
197 debug(s, "other side changed, get", r)
197 debug(s, "other side changed, get", r)
198 wctx.sub(s).get(r, overwrite)
198 wctx.sub(s).get(r, overwrite)
199 sm[s] = r
199 sm[s] = r
200 else:
200 else:
201 debug(s, "both sides changed")
201 debug(s, "both sides changed")
202 srepo = wctx.sub(s)
202 srepo = wctx.sub(s)
203 option = repo.ui.promptchoice(
203 option = repo.ui.promptchoice(
204 _(' subrepository %s diverged (local revision: %s, '
204 _(' subrepository %s diverged (local revision: %s, '
205 'remote revision: %s)\n'
205 'remote revision: %s)\n'
206 '(M)erge, keep (l)ocal or keep (r)emote?'
206 '(M)erge, keep (l)ocal or keep (r)emote?'
207 '$$ &Merge $$ &Local $$ &Remote')
207 '$$ &Merge $$ &Local $$ &Remote')
208 % (s, srepo.shortid(l[1]), srepo.shortid(r[1])), 0)
208 % (s, srepo.shortid(l[1]), srepo.shortid(r[1])), 0)
209 if option == 0:
209 if option == 0:
210 wctx.sub(s).merge(r)
210 wctx.sub(s).merge(r)
211 sm[s] = l
211 sm[s] = l
212 debug(s, "merge with", r)
212 debug(s, "merge with", r)
213 elif option == 1:
213 elif option == 1:
214 sm[s] = l
214 sm[s] = l
215 debug(s, "keep local subrepo revision", l)
215 debug(s, "keep local subrepo revision", l)
216 else:
216 else:
217 wctx.sub(s).get(r, overwrite)
217 wctx.sub(s).get(r, overwrite)
218 sm[s] = r
218 sm[s] = r
219 debug(s, "get remote subrepo revision", r)
219 debug(s, "get remote subrepo revision", r)
220 elif ld == a: # remote removed, local unchanged
220 elif ld == a: # remote removed, local unchanged
221 debug(s, "remote removed, remove")
221 debug(s, "remote removed, remove")
222 wctx.sub(s).remove()
222 wctx.sub(s).remove()
223 elif a == nullstate: # not present in remote or ancestor
223 elif a == nullstate: # not present in remote or ancestor
224 debug(s, "local added, keep")
224 debug(s, "local added, keep")
225 sm[s] = l
225 sm[s] = l
226 continue
226 continue
227 else:
227 else:
228 if repo.ui.promptchoice(
228 if repo.ui.promptchoice(
229 _(' local changed subrepository %s which remote removed\n'
229 _(' local changed subrepository %s which remote removed\n'
230 'use (c)hanged version or (d)elete?'
230 'use (c)hanged version or (d)elete?'
231 '$$ &Changed $$ &Delete') % s, 0):
231 '$$ &Changed $$ &Delete') % s, 0):
232 debug(s, "prompt remove")
232 debug(s, "prompt remove")
233 wctx.sub(s).remove()
233 wctx.sub(s).remove()
234
234
235 for s, r in sorted(s2.items()):
235 for s, r in sorted(s2.items()):
236 if s in s1:
236 if s in s1:
237 continue
237 continue
238 elif s not in sa:
238 elif s not in sa:
239 debug(s, "remote added, get", r)
239 debug(s, "remote added, get", r)
240 mctx.sub(s).get(r)
240 mctx.sub(s).get(r)
241 sm[s] = r
241 sm[s] = r
242 elif r != sa[s]:
242 elif r != sa[s]:
243 if repo.ui.promptchoice(
243 if repo.ui.promptchoice(
244 _(' remote changed subrepository %s which local removed\n'
244 _(' remote changed subrepository %s which local removed\n'
245 'use (c)hanged version or (d)elete?'
245 'use (c)hanged version or (d)elete?'
246 '$$ &Changed $$ &Delete') % s, 0) == 0:
246 '$$ &Changed $$ &Delete') % s, 0) == 0:
247 debug(s, "prompt recreate", r)
247 debug(s, "prompt recreate", r)
248 mctx.sub(s).get(r)
248 mctx.sub(s).get(r)
249 sm[s] = r
249 sm[s] = r
250
250
251 # record merged .hgsubstate
251 # record merged .hgsubstate
252 writestate(repo, sm)
252 writestate(repo, sm)
253 return sm
253 return sm
254
254
255 def _updateprompt(ui, sub, dirty, local, remote):
255 def _updateprompt(ui, sub, dirty, local, remote):
256 if dirty:
256 if dirty:
257 msg = (_(' subrepository sources for %s differ\n'
257 msg = (_(' subrepository sources for %s differ\n'
258 'use (l)ocal source (%s) or (r)emote source (%s)?'
258 'use (l)ocal source (%s) or (r)emote source (%s)?'
259 '$$ &Local $$ &Remote')
259 '$$ &Local $$ &Remote')
260 % (subrelpath(sub), local, remote))
260 % (subrelpath(sub), local, remote))
261 else:
261 else:
262 msg = (_(' subrepository sources for %s differ (in checked out '
262 msg = (_(' subrepository sources for %s differ (in checked out '
263 'version)\n'
263 'version)\n'
264 'use (l)ocal source (%s) or (r)emote source (%s)?'
264 'use (l)ocal source (%s) or (r)emote source (%s)?'
265 '$$ &Local $$ &Remote')
265 '$$ &Local $$ &Remote')
266 % (subrelpath(sub), local, remote))
266 % (subrelpath(sub), local, remote))
267 return ui.promptchoice(msg, 0)
267 return ui.promptchoice(msg, 0)
268
268
269 def reporelpath(repo):
269 def reporelpath(repo):
270 """return path to this (sub)repo as seen from outermost repo"""
270 """return path to this (sub)repo as seen from outermost repo"""
271 parent = repo
271 parent = repo
272 while util.safehasattr(parent, '_subparent'):
272 while util.safehasattr(parent, '_subparent'):
273 parent = parent._subparent
273 parent = parent._subparent
274 return repo.root[len(pathutil.normasprefix(parent.root)):]
274 return repo.root[len(pathutil.normasprefix(parent.root)):]
275
275
276 def subrelpath(sub):
276 def subrelpath(sub):
277 """return path to this subrepo as seen from outermost repo"""
277 """return path to this subrepo as seen from outermost repo"""
278 if util.safehasattr(sub, '_relpath'):
278 if util.safehasattr(sub, '_relpath'):
279 return sub._relpath
279 return sub._relpath
280 if not util.safehasattr(sub, '_repo'):
280 if not util.safehasattr(sub, '_repo'):
281 return sub._path
281 return sub._path
282 return reporelpath(sub._repo)
282 return reporelpath(sub._repo)
283
283
284 def _abssource(repo, push=False, abort=True):
284 def _abssource(repo, push=False, abort=True):
285 """return pull/push path of repo - either based on parent repo .hgsub info
285 """return pull/push path of repo - either based on parent repo .hgsub info
286 or on the top repo config. Abort or return None if no source found."""
286 or on the top repo config. Abort or return None if no source found."""
287 if util.safehasattr(repo, '_subparent'):
287 if util.safehasattr(repo, '_subparent'):
288 source = util.url(repo._subsource)
288 source = util.url(repo._subsource)
289 if source.isabs():
289 if source.isabs():
290 return str(source)
290 return str(source)
291 source.path = posixpath.normpath(source.path)
291 source.path = posixpath.normpath(source.path)
292 parent = _abssource(repo._subparent, push, abort=False)
292 parent = _abssource(repo._subparent, push, abort=False)
293 if parent:
293 if parent:
294 parent = util.url(util.pconvert(parent))
294 parent = util.url(util.pconvert(parent))
295 parent.path = posixpath.join(parent.path or '', source.path)
295 parent.path = posixpath.join(parent.path or '', source.path)
296 parent.path = posixpath.normpath(parent.path)
296 parent.path = posixpath.normpath(parent.path)
297 return str(parent)
297 return str(parent)
298 else: # recursion reached top repo
298 else: # recursion reached top repo
299 if util.safehasattr(repo, '_subtoppath'):
299 if util.safehasattr(repo, '_subtoppath'):
300 return repo._subtoppath
300 return repo._subtoppath
301 if push and repo.ui.config('paths', 'default-push'):
301 if push and repo.ui.config('paths', 'default-push'):
302 return repo.ui.config('paths', 'default-push')
302 return repo.ui.config('paths', 'default-push')
303 if repo.ui.config('paths', 'default'):
303 if repo.ui.config('paths', 'default'):
304 return repo.ui.config('paths', 'default')
304 return repo.ui.config('paths', 'default')
305 if repo.shared():
305 if repo.shared():
306 # chop off the .hg component to get the default path form
306 # chop off the .hg component to get the default path form
307 return os.path.dirname(repo.sharedpath)
307 return os.path.dirname(repo.sharedpath)
308 if abort:
308 if abort:
309 raise util.Abort(_("default path for subrepository not found"))
309 raise util.Abort(_("default path for subrepository not found"))
310
310
311 def _sanitize(ui, path, ignore):
311 def _sanitize(ui, path, ignore):
312 for dirname, dirs, names in os.walk(path):
312 for dirname, dirs, names in os.walk(path):
313 for i, d in enumerate(dirs):
313 for i, d in enumerate(dirs):
314 if d.lower() == ignore:
314 if d.lower() == ignore:
315 del dirs[i]
315 del dirs[i]
316 break
316 break
317 if os.path.basename(dirname).lower() != '.hg':
317 if os.path.basename(dirname).lower() != '.hg':
318 continue
318 continue
319 for f in names:
319 for f in names:
320 if f.lower() == 'hgrc':
320 if f.lower() == 'hgrc':
321 ui.warn(_("warning: removing potentially hostile 'hgrc' "
321 ui.warn(_("warning: removing potentially hostile 'hgrc' "
322 "in '%s'\n") % dirname)
322 "in '%s'\n") % dirname)
323 os.unlink(os.path.join(dirname, f))
323 os.unlink(os.path.join(dirname, f))
324
324
325 def subrepo(ctx, path):
325 def subrepo(ctx, path):
326 """return instance of the right subrepo class for subrepo in path"""
326 """return instance of the right subrepo class for subrepo in path"""
327 # subrepo inherently violates our import layering rules
327 # subrepo inherently violates our import layering rules
328 # because it wants to make repo objects from deep inside the stack
328 # because it wants to make repo objects from deep inside the stack
329 # so we manually delay the circular imports to not break
329 # so we manually delay the circular imports to not break
330 # scripts that don't use our demand-loading
330 # scripts that don't use our demand-loading
331 global hg
331 global hg
332 import hg as h
332 import hg as h
333 hg = h
333 hg = h
334
334
335 pathutil.pathauditor(ctx.repo().root)(path)
335 pathutil.pathauditor(ctx.repo().root)(path)
336 state = ctx.substate[path]
336 state = ctx.substate[path]
337 if state[2] not in types:
337 if state[2] not in types:
338 raise util.Abort(_('unknown subrepo type %s') % state[2])
338 raise util.Abort(_('unknown subrepo type %s') % state[2])
339 return types[state[2]](ctx, path, state[:2])
339 return types[state[2]](ctx, path, state[:2])
340
340
341 def newcommitphase(ui, ctx):
341 def newcommitphase(ui, ctx):
342 commitphase = phases.newcommitphase(ui)
342 commitphase = phases.newcommitphase(ui)
343 substate = getattr(ctx, "substate", None)
343 substate = getattr(ctx, "substate", None)
344 if not substate:
344 if not substate:
345 return commitphase
345 return commitphase
346 check = ui.config('phases', 'checksubrepos', 'follow')
346 check = ui.config('phases', 'checksubrepos', 'follow')
347 if check not in ('ignore', 'follow', 'abort'):
347 if check not in ('ignore', 'follow', 'abort'):
348 raise util.Abort(_('invalid phases.checksubrepos configuration: %s')
348 raise util.Abort(_('invalid phases.checksubrepos configuration: %s')
349 % (check))
349 % (check))
350 if check == 'ignore':
350 if check == 'ignore':
351 return commitphase
351 return commitphase
352 maxphase = phases.public
352 maxphase = phases.public
353 maxsub = None
353 maxsub = None
354 for s in sorted(substate):
354 for s in sorted(substate):
355 sub = ctx.sub(s)
355 sub = ctx.sub(s)
356 subphase = sub.phase(substate[s][1])
356 subphase = sub.phase(substate[s][1])
357 if maxphase < subphase:
357 if maxphase < subphase:
358 maxphase = subphase
358 maxphase = subphase
359 maxsub = s
359 maxsub = s
360 if commitphase < maxphase:
360 if commitphase < maxphase:
361 if check == 'abort':
361 if check == 'abort':
362 raise util.Abort(_("can't commit in %s phase"
362 raise util.Abort(_("can't commit in %s phase"
363 " conflicting %s from subrepository %s") %
363 " conflicting %s from subrepository %s") %
364 (phases.phasenames[commitphase],
364 (phases.phasenames[commitphase],
365 phases.phasenames[maxphase], maxsub))
365 phases.phasenames[maxphase], maxsub))
366 ui.warn(_("warning: changes are committed in"
366 ui.warn(_("warning: changes are committed in"
367 " %s phase from subrepository %s\n") %
367 " %s phase from subrepository %s\n") %
368 (phases.phasenames[maxphase], maxsub))
368 (phases.phasenames[maxphase], maxsub))
369 return maxphase
369 return maxphase
370 return commitphase
370 return commitphase
371
371
372 # subrepo classes need to implement the following abstract class:
372 # subrepo classes need to implement the following abstract class:
373
373
374 class abstractsubrepo(object):
374 class abstractsubrepo(object):
375
375
376 def __init__(self, ui):
376 def __init__(self, ui):
377 self.ui = ui
377 self.ui = ui
378
378
379 def storeclean(self, path):
379 def storeclean(self, path):
380 """
380 """
381 returns true if the repository has not changed since it was last
381 returns true if the repository has not changed since it was last
382 cloned from or pushed to a given repository.
382 cloned from or pushed to a given repository.
383 """
383 """
384 return False
384 return False
385
385
386 def dirty(self, ignoreupdate=False):
386 def dirty(self, ignoreupdate=False):
387 """returns true if the dirstate of the subrepo is dirty or does not
387 """returns true if the dirstate of the subrepo is dirty or does not
388 match current stored state. If ignoreupdate is true, only check
388 match current stored state. If ignoreupdate is true, only check
389 whether the subrepo has uncommitted changes in its dirstate.
389 whether the subrepo has uncommitted changes in its dirstate.
390 """
390 """
391 raise NotImplementedError
391 raise NotImplementedError
392
392
393 def dirtyreason(self, ignoreupdate=False):
394 """return reason string if it is ``dirty()``
395
396 Returned string should have enough information for the message
397 of exception.
398
399 This returns None, otherwise.
400 """
401 if self.dirty(ignoreupdate=ignoreupdate):
402 return _("uncommitted changes in subrepository '%s'"
403 ) % subrelpath(self)
404
393 def basestate(self):
405 def basestate(self):
394 """current working directory base state, disregarding .hgsubstate
406 """current working directory base state, disregarding .hgsubstate
395 state and working directory modifications"""
407 state and working directory modifications"""
396 raise NotImplementedError
408 raise NotImplementedError
397
409
398 def checknested(self, path):
410 def checknested(self, path):
399 """check if path is a subrepository within this repository"""
411 """check if path is a subrepository within this repository"""
400 return False
412 return False
401
413
402 def commit(self, text, user, date):
414 def commit(self, text, user, date):
403 """commit the current changes to the subrepo with the given
415 """commit the current changes to the subrepo with the given
404 log message. Use given user and date if possible. Return the
416 log message. Use given user and date if possible. Return the
405 new state of the subrepo.
417 new state of the subrepo.
406 """
418 """
407 raise NotImplementedError
419 raise NotImplementedError
408
420
409 def phase(self, state):
421 def phase(self, state):
410 """returns phase of specified state in the subrepository.
422 """returns phase of specified state in the subrepository.
411 """
423 """
412 return phases.public
424 return phases.public
413
425
414 def remove(self):
426 def remove(self):
415 """remove the subrepo
427 """remove the subrepo
416
428
417 (should verify the dirstate is not dirty first)
429 (should verify the dirstate is not dirty first)
418 """
430 """
419 raise NotImplementedError
431 raise NotImplementedError
420
432
421 def get(self, state, overwrite=False):
433 def get(self, state, overwrite=False):
422 """run whatever commands are needed to put the subrepo into
434 """run whatever commands are needed to put the subrepo into
423 this state
435 this state
424 """
436 """
425 raise NotImplementedError
437 raise NotImplementedError
426
438
427 def merge(self, state):
439 def merge(self, state):
428 """merge currently-saved state with the new state."""
440 """merge currently-saved state with the new state."""
429 raise NotImplementedError
441 raise NotImplementedError
430
442
431 def push(self, opts):
443 def push(self, opts):
432 """perform whatever action is analogous to 'hg push'
444 """perform whatever action is analogous to 'hg push'
433
445
434 This may be a no-op on some systems.
446 This may be a no-op on some systems.
435 """
447 """
436 raise NotImplementedError
448 raise NotImplementedError
437
449
438 def add(self, ui, match, prefix, explicitonly, **opts):
450 def add(self, ui, match, prefix, explicitonly, **opts):
439 return []
451 return []
440
452
441 def addremove(self, matcher, prefix, opts, dry_run, similarity):
453 def addremove(self, matcher, prefix, opts, dry_run, similarity):
442 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
454 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
443 return 1
455 return 1
444
456
445 def cat(self, match, prefix, **opts):
457 def cat(self, match, prefix, **opts):
446 return 1
458 return 1
447
459
448 def status(self, rev2, **opts):
460 def status(self, rev2, **opts):
449 return scmutil.status([], [], [], [], [], [], [])
461 return scmutil.status([], [], [], [], [], [], [])
450
462
451 def diff(self, ui, diffopts, node2, match, prefix, **opts):
463 def diff(self, ui, diffopts, node2, match, prefix, **opts):
452 pass
464 pass
453
465
454 def outgoing(self, ui, dest, opts):
466 def outgoing(self, ui, dest, opts):
455 return 1
467 return 1
456
468
457 def incoming(self, ui, source, opts):
469 def incoming(self, ui, source, opts):
458 return 1
470 return 1
459
471
460 def files(self):
472 def files(self):
461 """return filename iterator"""
473 """return filename iterator"""
462 raise NotImplementedError
474 raise NotImplementedError
463
475
464 def filedata(self, name):
476 def filedata(self, name):
465 """return file data"""
477 """return file data"""
466 raise NotImplementedError
478 raise NotImplementedError
467
479
468 def fileflags(self, name):
480 def fileflags(self, name):
469 """return file flags"""
481 """return file flags"""
470 return ''
482 return ''
471
483
472 def printfiles(self, ui, m, fm, fmt):
484 def printfiles(self, ui, m, fm, fmt):
473 """handle the files command for this subrepo"""
485 """handle the files command for this subrepo"""
474 return 1
486 return 1
475
487
476 def archive(self, archiver, prefix, match=None):
488 def archive(self, archiver, prefix, match=None):
477 if match is not None:
489 if match is not None:
478 files = [f for f in self.files() if match(f)]
490 files = [f for f in self.files() if match(f)]
479 else:
491 else:
480 files = self.files()
492 files = self.files()
481 total = len(files)
493 total = len(files)
482 relpath = subrelpath(self)
494 relpath = subrelpath(self)
483 self.ui.progress(_('archiving (%s)') % relpath, 0,
495 self.ui.progress(_('archiving (%s)') % relpath, 0,
484 unit=_('files'), total=total)
496 unit=_('files'), total=total)
485 for i, name in enumerate(files):
497 for i, name in enumerate(files):
486 flags = self.fileflags(name)
498 flags = self.fileflags(name)
487 mode = 'x' in flags and 0755 or 0644
499 mode = 'x' in flags and 0755 or 0644
488 symlink = 'l' in flags
500 symlink = 'l' in flags
489 archiver.addfile(os.path.join(prefix, self._path, name),
501 archiver.addfile(os.path.join(prefix, self._path, name),
490 mode, symlink, self.filedata(name))
502 mode, symlink, self.filedata(name))
491 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
503 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
492 unit=_('files'), total=total)
504 unit=_('files'), total=total)
493 self.ui.progress(_('archiving (%s)') % relpath, None)
505 self.ui.progress(_('archiving (%s)') % relpath, None)
494 return total
506 return total
495
507
496 def walk(self, match):
508 def walk(self, match):
497 '''
509 '''
498 walk recursively through the directory tree, finding all files
510 walk recursively through the directory tree, finding all files
499 matched by the match function
511 matched by the match function
500 '''
512 '''
501 pass
513 pass
502
514
503 def forget(self, match, prefix):
515 def forget(self, match, prefix):
504 return ([], [])
516 return ([], [])
505
517
506 def removefiles(self, matcher, prefix, after, force, subrepos):
518 def removefiles(self, matcher, prefix, after, force, subrepos):
507 """remove the matched files from the subrepository and the filesystem,
519 """remove the matched files from the subrepository and the filesystem,
508 possibly by force and/or after the file has been removed from the
520 possibly by force and/or after the file has been removed from the
509 filesystem. Return 0 on success, 1 on any warning.
521 filesystem. Return 0 on success, 1 on any warning.
510 """
522 """
511 return 1
523 return 1
512
524
513 def revert(self, substate, *pats, **opts):
525 def revert(self, substate, *pats, **opts):
514 self.ui.warn('%s: reverting %s subrepos is unsupported\n' \
526 self.ui.warn('%s: reverting %s subrepos is unsupported\n' \
515 % (substate[0], substate[2]))
527 % (substate[0], substate[2]))
516 return []
528 return []
517
529
518 def shortid(self, revid):
530 def shortid(self, revid):
519 return revid
531 return revid
520
532
521 class hgsubrepo(abstractsubrepo):
533 class hgsubrepo(abstractsubrepo):
522 def __init__(self, ctx, path, state):
534 def __init__(self, ctx, path, state):
523 super(hgsubrepo, self).__init__(ctx.repo().ui)
535 super(hgsubrepo, self).__init__(ctx.repo().ui)
524 self._path = path
536 self._path = path
525 self._state = state
537 self._state = state
526 self._ctx = ctx
538 self._ctx = ctx
527 r = ctx.repo()
539 r = ctx.repo()
528 root = r.wjoin(path)
540 root = r.wjoin(path)
529 create = not r.wvfs.exists('%s/.hg' % path)
541 create = not r.wvfs.exists('%s/.hg' % path)
530 self._repo = hg.repository(r.baseui, root, create=create)
542 self._repo = hg.repository(r.baseui, root, create=create)
531 self.ui = self._repo.ui
543 self.ui = self._repo.ui
532 for s, k in [('ui', 'commitsubrepos')]:
544 for s, k in [('ui', 'commitsubrepos')]:
533 v = r.ui.config(s, k)
545 v = r.ui.config(s, k)
534 if v:
546 if v:
535 self.ui.setconfig(s, k, v, 'subrepo')
547 self.ui.setconfig(s, k, v, 'subrepo')
536 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
548 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
537 self._initrepo(r, state[0], create)
549 self._initrepo(r, state[0], create)
538
550
539 def storeclean(self, path):
551 def storeclean(self, path):
540 lock = self._repo.lock()
552 lock = self._repo.lock()
541 try:
553 try:
542 return self._storeclean(path)
554 return self._storeclean(path)
543 finally:
555 finally:
544 lock.release()
556 lock.release()
545
557
546 def _storeclean(self, path):
558 def _storeclean(self, path):
547 clean = True
559 clean = True
548 itercache = self._calcstorehash(path)
560 itercache = self._calcstorehash(path)
549 try:
561 try:
550 for filehash in self._readstorehashcache(path):
562 for filehash in self._readstorehashcache(path):
551 if filehash != itercache.next():
563 if filehash != itercache.next():
552 clean = False
564 clean = False
553 break
565 break
554 except StopIteration:
566 except StopIteration:
555 # the cached and current pull states have a different size
567 # the cached and current pull states have a different size
556 clean = False
568 clean = False
557 if clean:
569 if clean:
558 try:
570 try:
559 itercache.next()
571 itercache.next()
560 # the cached and current pull states have a different size
572 # the cached and current pull states have a different size
561 clean = False
573 clean = False
562 except StopIteration:
574 except StopIteration:
563 pass
575 pass
564 return clean
576 return clean
565
577
566 def _calcstorehash(self, remotepath):
578 def _calcstorehash(self, remotepath):
567 '''calculate a unique "store hash"
579 '''calculate a unique "store hash"
568
580
569 This method is used to to detect when there are changes that may
581 This method is used to to detect when there are changes that may
570 require a push to a given remote path.'''
582 require a push to a given remote path.'''
571 # sort the files that will be hashed in increasing (likely) file size
583 # sort the files that will be hashed in increasing (likely) file size
572 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
584 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
573 yield '# %s\n' % _expandedabspath(remotepath)
585 yield '# %s\n' % _expandedabspath(remotepath)
574 vfs = self._repo.vfs
586 vfs = self._repo.vfs
575 for relname in filelist:
587 for relname in filelist:
576 filehash = util.sha1(vfs.tryread(relname)).hexdigest()
588 filehash = util.sha1(vfs.tryread(relname)).hexdigest()
577 yield '%s = %s\n' % (relname, filehash)
589 yield '%s = %s\n' % (relname, filehash)
578
590
579 @propertycache
591 @propertycache
580 def _cachestorehashvfs(self):
592 def _cachestorehashvfs(self):
581 return scmutil.vfs(self._repo.join('cache/storehash'))
593 return scmutil.vfs(self._repo.join('cache/storehash'))
582
594
583 def _readstorehashcache(self, remotepath):
595 def _readstorehashcache(self, remotepath):
584 '''read the store hash cache for a given remote repository'''
596 '''read the store hash cache for a given remote repository'''
585 cachefile = _getstorehashcachename(remotepath)
597 cachefile = _getstorehashcachename(remotepath)
586 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
598 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
587
599
588 def _cachestorehash(self, remotepath):
600 def _cachestorehash(self, remotepath):
589 '''cache the current store hash
601 '''cache the current store hash
590
602
591 Each remote repo requires its own store hash cache, because a subrepo
603 Each remote repo requires its own store hash cache, because a subrepo
592 store may be "clean" versus a given remote repo, but not versus another
604 store may be "clean" versus a given remote repo, but not versus another
593 '''
605 '''
594 cachefile = _getstorehashcachename(remotepath)
606 cachefile = _getstorehashcachename(remotepath)
595 lock = self._repo.lock()
607 lock = self._repo.lock()
596 try:
608 try:
597 storehash = list(self._calcstorehash(remotepath))
609 storehash = list(self._calcstorehash(remotepath))
598 vfs = self._cachestorehashvfs
610 vfs = self._cachestorehashvfs
599 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
611 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
600 finally:
612 finally:
601 lock.release()
613 lock.release()
602
614
603 @annotatesubrepoerror
615 @annotatesubrepoerror
604 def _initrepo(self, parentrepo, source, create):
616 def _initrepo(self, parentrepo, source, create):
605 self._repo._subparent = parentrepo
617 self._repo._subparent = parentrepo
606 self._repo._subsource = source
618 self._repo._subsource = source
607
619
608 if create:
620 if create:
609 lines = ['[paths]\n']
621 lines = ['[paths]\n']
610
622
611 def addpathconfig(key, value):
623 def addpathconfig(key, value):
612 if value:
624 if value:
613 lines.append('%s = %s\n' % (key, value))
625 lines.append('%s = %s\n' % (key, value))
614 self.ui.setconfig('paths', key, value, 'subrepo')
626 self.ui.setconfig('paths', key, value, 'subrepo')
615
627
616 defpath = _abssource(self._repo, abort=False)
628 defpath = _abssource(self._repo, abort=False)
617 defpushpath = _abssource(self._repo, True, abort=False)
629 defpushpath = _abssource(self._repo, True, abort=False)
618 addpathconfig('default', defpath)
630 addpathconfig('default', defpath)
619 if defpath != defpushpath:
631 if defpath != defpushpath:
620 addpathconfig('default-push', defpushpath)
632 addpathconfig('default-push', defpushpath)
621
633
622 fp = self._repo.vfs("hgrc", "w", text=True)
634 fp = self._repo.vfs("hgrc", "w", text=True)
623 try:
635 try:
624 fp.write(''.join(lines))
636 fp.write(''.join(lines))
625 finally:
637 finally:
626 fp.close()
638 fp.close()
627
639
628 @annotatesubrepoerror
640 @annotatesubrepoerror
629 def add(self, ui, match, prefix, explicitonly, **opts):
641 def add(self, ui, match, prefix, explicitonly, **opts):
630 return cmdutil.add(ui, self._repo, match,
642 return cmdutil.add(ui, self._repo, match,
631 os.path.join(prefix, self._path), explicitonly,
643 os.path.join(prefix, self._path), explicitonly,
632 **opts)
644 **opts)
633
645
634 @annotatesubrepoerror
646 @annotatesubrepoerror
635 def addremove(self, m, prefix, opts, dry_run, similarity):
647 def addremove(self, m, prefix, opts, dry_run, similarity):
636 # In the same way as sub directories are processed, once in a subrepo,
648 # In the same way as sub directories are processed, once in a subrepo,
637 # always entry any of its subrepos. Don't corrupt the options that will
649 # always entry any of its subrepos. Don't corrupt the options that will
638 # be used to process sibling subrepos however.
650 # be used to process sibling subrepos however.
639 opts = copy.copy(opts)
651 opts = copy.copy(opts)
640 opts['subrepos'] = True
652 opts['subrepos'] = True
641 return scmutil.addremove(self._repo, m,
653 return scmutil.addremove(self._repo, m,
642 os.path.join(prefix, self._path), opts,
654 os.path.join(prefix, self._path), opts,
643 dry_run, similarity)
655 dry_run, similarity)
644
656
645 @annotatesubrepoerror
657 @annotatesubrepoerror
646 def cat(self, match, prefix, **opts):
658 def cat(self, match, prefix, **opts):
647 rev = self._state[1]
659 rev = self._state[1]
648 ctx = self._repo[rev]
660 ctx = self._repo[rev]
649 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
661 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
650
662
651 @annotatesubrepoerror
663 @annotatesubrepoerror
652 def status(self, rev2, **opts):
664 def status(self, rev2, **opts):
653 try:
665 try:
654 rev1 = self._state[1]
666 rev1 = self._state[1]
655 ctx1 = self._repo[rev1]
667 ctx1 = self._repo[rev1]
656 ctx2 = self._repo[rev2]
668 ctx2 = self._repo[rev2]
657 return self._repo.status(ctx1, ctx2, **opts)
669 return self._repo.status(ctx1, ctx2, **opts)
658 except error.RepoLookupError, inst:
670 except error.RepoLookupError, inst:
659 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
671 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
660 % (inst, subrelpath(self)))
672 % (inst, subrelpath(self)))
661 return scmutil.status([], [], [], [], [], [], [])
673 return scmutil.status([], [], [], [], [], [], [])
662
674
663 @annotatesubrepoerror
675 @annotatesubrepoerror
664 def diff(self, ui, diffopts, node2, match, prefix, **opts):
676 def diff(self, ui, diffopts, node2, match, prefix, **opts):
665 try:
677 try:
666 node1 = node.bin(self._state[1])
678 node1 = node.bin(self._state[1])
667 # We currently expect node2 to come from substate and be
679 # We currently expect node2 to come from substate and be
668 # in hex format
680 # in hex format
669 if node2 is not None:
681 if node2 is not None:
670 node2 = node.bin(node2)
682 node2 = node.bin(node2)
671 cmdutil.diffordiffstat(ui, self._repo, diffopts,
683 cmdutil.diffordiffstat(ui, self._repo, diffopts,
672 node1, node2, match,
684 node1, node2, match,
673 prefix=posixpath.join(prefix, self._path),
685 prefix=posixpath.join(prefix, self._path),
674 listsubrepos=True, **opts)
686 listsubrepos=True, **opts)
675 except error.RepoLookupError, inst:
687 except error.RepoLookupError, inst:
676 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
688 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
677 % (inst, subrelpath(self)))
689 % (inst, subrelpath(self)))
678
690
679 @annotatesubrepoerror
691 @annotatesubrepoerror
680 def archive(self, archiver, prefix, match=None):
692 def archive(self, archiver, prefix, match=None):
681 self._get(self._state + ('hg',))
693 self._get(self._state + ('hg',))
682 total = abstractsubrepo.archive(self, archiver, prefix, match)
694 total = abstractsubrepo.archive(self, archiver, prefix, match)
683 rev = self._state[1]
695 rev = self._state[1]
684 ctx = self._repo[rev]
696 ctx = self._repo[rev]
685 for subpath in ctx.substate:
697 for subpath in ctx.substate:
686 s = subrepo(ctx, subpath)
698 s = subrepo(ctx, subpath)
687 submatch = matchmod.narrowmatcher(subpath, match)
699 submatch = matchmod.narrowmatcher(subpath, match)
688 total += s.archive(
700 total += s.archive(
689 archiver, os.path.join(prefix, self._path), submatch)
701 archiver, os.path.join(prefix, self._path), submatch)
690 return total
702 return total
691
703
692 @annotatesubrepoerror
704 @annotatesubrepoerror
693 def dirty(self, ignoreupdate=False):
705 def dirty(self, ignoreupdate=False):
694 r = self._state[1]
706 r = self._state[1]
695 if r == '' and not ignoreupdate: # no state recorded
707 if r == '' and not ignoreupdate: # no state recorded
696 return True
708 return True
697 w = self._repo[None]
709 w = self._repo[None]
698 if r != w.p1().hex() and not ignoreupdate:
710 if r != w.p1().hex() and not ignoreupdate:
699 # different version checked out
711 # different version checked out
700 return True
712 return True
701 return w.dirty() # working directory changed
713 return w.dirty() # working directory changed
702
714
703 def basestate(self):
715 def basestate(self):
704 return self._repo['.'].hex()
716 return self._repo['.'].hex()
705
717
706 def checknested(self, path):
718 def checknested(self, path):
707 return self._repo._checknested(self._repo.wjoin(path))
719 return self._repo._checknested(self._repo.wjoin(path))
708
720
709 @annotatesubrepoerror
721 @annotatesubrepoerror
710 def commit(self, text, user, date):
722 def commit(self, text, user, date):
711 # don't bother committing in the subrepo if it's only been
723 # don't bother committing in the subrepo if it's only been
712 # updated
724 # updated
713 if not self.dirty(True):
725 if not self.dirty(True):
714 return self._repo['.'].hex()
726 return self._repo['.'].hex()
715 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
727 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
716 n = self._repo.commit(text, user, date)
728 n = self._repo.commit(text, user, date)
717 if not n:
729 if not n:
718 return self._repo['.'].hex() # different version checked out
730 return self._repo['.'].hex() # different version checked out
719 return node.hex(n)
731 return node.hex(n)
720
732
721 @annotatesubrepoerror
733 @annotatesubrepoerror
722 def phase(self, state):
734 def phase(self, state):
723 return self._repo[state].phase()
735 return self._repo[state].phase()
724
736
725 @annotatesubrepoerror
737 @annotatesubrepoerror
726 def remove(self):
738 def remove(self):
727 # we can't fully delete the repository as it may contain
739 # we can't fully delete the repository as it may contain
728 # local-only history
740 # local-only history
729 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
741 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
730 hg.clean(self._repo, node.nullid, False)
742 hg.clean(self._repo, node.nullid, False)
731
743
732 def _get(self, state):
744 def _get(self, state):
733 source, revision, kind = state
745 source, revision, kind = state
734 if revision in self._repo.unfiltered():
746 if revision in self._repo.unfiltered():
735 return True
747 return True
736 self._repo._subsource = source
748 self._repo._subsource = source
737 srcurl = _abssource(self._repo)
749 srcurl = _abssource(self._repo)
738 other = hg.peer(self._repo, {}, srcurl)
750 other = hg.peer(self._repo, {}, srcurl)
739 if len(self._repo) == 0:
751 if len(self._repo) == 0:
740 self.ui.status(_('cloning subrepo %s from %s\n')
752 self.ui.status(_('cloning subrepo %s from %s\n')
741 % (subrelpath(self), srcurl))
753 % (subrelpath(self), srcurl))
742 parentrepo = self._repo._subparent
754 parentrepo = self._repo._subparent
743 shutil.rmtree(self._repo.path)
755 shutil.rmtree(self._repo.path)
744 other, cloned = hg.clone(self._repo._subparent.baseui, {},
756 other, cloned = hg.clone(self._repo._subparent.baseui, {},
745 other, self._repo.root,
757 other, self._repo.root,
746 update=False)
758 update=False)
747 self._repo = cloned.local()
759 self._repo = cloned.local()
748 self._initrepo(parentrepo, source, create=True)
760 self._initrepo(parentrepo, source, create=True)
749 self._cachestorehash(srcurl)
761 self._cachestorehash(srcurl)
750 else:
762 else:
751 self.ui.status(_('pulling subrepo %s from %s\n')
763 self.ui.status(_('pulling subrepo %s from %s\n')
752 % (subrelpath(self), srcurl))
764 % (subrelpath(self), srcurl))
753 cleansub = self.storeclean(srcurl)
765 cleansub = self.storeclean(srcurl)
754 exchange.pull(self._repo, other)
766 exchange.pull(self._repo, other)
755 if cleansub:
767 if cleansub:
756 # keep the repo clean after pull
768 # keep the repo clean after pull
757 self._cachestorehash(srcurl)
769 self._cachestorehash(srcurl)
758 return False
770 return False
759
771
760 @annotatesubrepoerror
772 @annotatesubrepoerror
761 def get(self, state, overwrite=False):
773 def get(self, state, overwrite=False):
762 inrepo = self._get(state)
774 inrepo = self._get(state)
763 source, revision, kind = state
775 source, revision, kind = state
764 repo = self._repo
776 repo = self._repo
765 repo.ui.debug("getting subrepo %s\n" % self._path)
777 repo.ui.debug("getting subrepo %s\n" % self._path)
766 if inrepo:
778 if inrepo:
767 urepo = repo.unfiltered()
779 urepo = repo.unfiltered()
768 ctx = urepo[revision]
780 ctx = urepo[revision]
769 if ctx.hidden():
781 if ctx.hidden():
770 urepo.ui.warn(
782 urepo.ui.warn(
771 _('revision %s in subrepo %s is hidden\n') \
783 _('revision %s in subrepo %s is hidden\n') \
772 % (revision[0:12], self._path))
784 % (revision[0:12], self._path))
773 repo = urepo
785 repo = urepo
774 hg.updaterepo(repo, revision, overwrite)
786 hg.updaterepo(repo, revision, overwrite)
775
787
776 @annotatesubrepoerror
788 @annotatesubrepoerror
777 def merge(self, state):
789 def merge(self, state):
778 self._get(state)
790 self._get(state)
779 cur = self._repo['.']
791 cur = self._repo['.']
780 dst = self._repo[state[1]]
792 dst = self._repo[state[1]]
781 anc = dst.ancestor(cur)
793 anc = dst.ancestor(cur)
782
794
783 def mergefunc():
795 def mergefunc():
784 if anc == cur and dst.branch() == cur.branch():
796 if anc == cur and dst.branch() == cur.branch():
785 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
797 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
786 hg.update(self._repo, state[1])
798 hg.update(self._repo, state[1])
787 elif anc == dst:
799 elif anc == dst:
788 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
800 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
789 else:
801 else:
790 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
802 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
791 hg.merge(self._repo, state[1], remind=False)
803 hg.merge(self._repo, state[1], remind=False)
792
804
793 wctx = self._repo[None]
805 wctx = self._repo[None]
794 if self.dirty():
806 if self.dirty():
795 if anc != dst:
807 if anc != dst:
796 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
808 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
797 mergefunc()
809 mergefunc()
798 else:
810 else:
799 mergefunc()
811 mergefunc()
800 else:
812 else:
801 mergefunc()
813 mergefunc()
802
814
803 @annotatesubrepoerror
815 @annotatesubrepoerror
804 def push(self, opts):
816 def push(self, opts):
805 force = opts.get('force')
817 force = opts.get('force')
806 newbranch = opts.get('new_branch')
818 newbranch = opts.get('new_branch')
807 ssh = opts.get('ssh')
819 ssh = opts.get('ssh')
808
820
809 # push subrepos depth-first for coherent ordering
821 # push subrepos depth-first for coherent ordering
810 c = self._repo['']
822 c = self._repo['']
811 subs = c.substate # only repos that are committed
823 subs = c.substate # only repos that are committed
812 for s in sorted(subs):
824 for s in sorted(subs):
813 if c.sub(s).push(opts) == 0:
825 if c.sub(s).push(opts) == 0:
814 return False
826 return False
815
827
816 dsturl = _abssource(self._repo, True)
828 dsturl = _abssource(self._repo, True)
817 if not force:
829 if not force:
818 if self.storeclean(dsturl):
830 if self.storeclean(dsturl):
819 self.ui.status(
831 self.ui.status(
820 _('no changes made to subrepo %s since last push to %s\n')
832 _('no changes made to subrepo %s since last push to %s\n')
821 % (subrelpath(self), dsturl))
833 % (subrelpath(self), dsturl))
822 return None
834 return None
823 self.ui.status(_('pushing subrepo %s to %s\n') %
835 self.ui.status(_('pushing subrepo %s to %s\n') %
824 (subrelpath(self), dsturl))
836 (subrelpath(self), dsturl))
825 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
837 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
826 res = exchange.push(self._repo, other, force, newbranch=newbranch)
838 res = exchange.push(self._repo, other, force, newbranch=newbranch)
827
839
828 # the repo is now clean
840 # the repo is now clean
829 self._cachestorehash(dsturl)
841 self._cachestorehash(dsturl)
830 return res.cgresult
842 return res.cgresult
831
843
832 @annotatesubrepoerror
844 @annotatesubrepoerror
833 def outgoing(self, ui, dest, opts):
845 def outgoing(self, ui, dest, opts):
834 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
846 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
835
847
836 @annotatesubrepoerror
848 @annotatesubrepoerror
837 def incoming(self, ui, source, opts):
849 def incoming(self, ui, source, opts):
838 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
850 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
839
851
840 @annotatesubrepoerror
852 @annotatesubrepoerror
841 def files(self):
853 def files(self):
842 rev = self._state[1]
854 rev = self._state[1]
843 ctx = self._repo[rev]
855 ctx = self._repo[rev]
844 return ctx.manifest().keys()
856 return ctx.manifest().keys()
845
857
846 def filedata(self, name):
858 def filedata(self, name):
847 rev = self._state[1]
859 rev = self._state[1]
848 return self._repo[rev][name].data()
860 return self._repo[rev][name].data()
849
861
850 def fileflags(self, name):
862 def fileflags(self, name):
851 rev = self._state[1]
863 rev = self._state[1]
852 ctx = self._repo[rev]
864 ctx = self._repo[rev]
853 return ctx.flags(name)
865 return ctx.flags(name)
854
866
855 @annotatesubrepoerror
867 @annotatesubrepoerror
856 def printfiles(self, ui, m, fm, fmt):
868 def printfiles(self, ui, m, fm, fmt):
857 # If the parent context is a workingctx, use the workingctx here for
869 # If the parent context is a workingctx, use the workingctx here for
858 # consistency.
870 # consistency.
859 if self._ctx.rev() is None:
871 if self._ctx.rev() is None:
860 ctx = self._repo[None]
872 ctx = self._repo[None]
861 else:
873 else:
862 rev = self._state[1]
874 rev = self._state[1]
863 ctx = self._repo[rev]
875 ctx = self._repo[rev]
864 return cmdutil.files(ui, ctx, m, fm, fmt, True)
876 return cmdutil.files(ui, ctx, m, fm, fmt, True)
865
877
866 def walk(self, match):
878 def walk(self, match):
867 ctx = self._repo[None]
879 ctx = self._repo[None]
868 return ctx.walk(match)
880 return ctx.walk(match)
869
881
870 @annotatesubrepoerror
882 @annotatesubrepoerror
871 def forget(self, match, prefix):
883 def forget(self, match, prefix):
872 return cmdutil.forget(self.ui, self._repo, match,
884 return cmdutil.forget(self.ui, self._repo, match,
873 os.path.join(prefix, self._path), True)
885 os.path.join(prefix, self._path), True)
874
886
875 @annotatesubrepoerror
887 @annotatesubrepoerror
876 def removefiles(self, matcher, prefix, after, force, subrepos):
888 def removefiles(self, matcher, prefix, after, force, subrepos):
877 return cmdutil.remove(self.ui, self._repo, matcher,
889 return cmdutil.remove(self.ui, self._repo, matcher,
878 os.path.join(prefix, self._path), after, force,
890 os.path.join(prefix, self._path), after, force,
879 subrepos)
891 subrepos)
880
892
881 @annotatesubrepoerror
893 @annotatesubrepoerror
882 def revert(self, substate, *pats, **opts):
894 def revert(self, substate, *pats, **opts):
883 # reverting a subrepo is a 2 step process:
895 # reverting a subrepo is a 2 step process:
884 # 1. if the no_backup is not set, revert all modified
896 # 1. if the no_backup is not set, revert all modified
885 # files inside the subrepo
897 # files inside the subrepo
886 # 2. update the subrepo to the revision specified in
898 # 2. update the subrepo to the revision specified in
887 # the corresponding substate dictionary
899 # the corresponding substate dictionary
888 self.ui.status(_('reverting subrepo %s\n') % substate[0])
900 self.ui.status(_('reverting subrepo %s\n') % substate[0])
889 if not opts.get('no_backup'):
901 if not opts.get('no_backup'):
890 # Revert all files on the subrepo, creating backups
902 # Revert all files on the subrepo, creating backups
891 # Note that this will not recursively revert subrepos
903 # Note that this will not recursively revert subrepos
892 # We could do it if there was a set:subrepos() predicate
904 # We could do it if there was a set:subrepos() predicate
893 opts = opts.copy()
905 opts = opts.copy()
894 opts['date'] = None
906 opts['date'] = None
895 opts['rev'] = substate[1]
907 opts['rev'] = substate[1]
896
908
897 self.filerevert(*pats, **opts)
909 self.filerevert(*pats, **opts)
898
910
899 # Update the repo to the revision specified in the given substate
911 # Update the repo to the revision specified in the given substate
900 if not opts.get('dry_run'):
912 if not opts.get('dry_run'):
901 self.get(substate, overwrite=True)
913 self.get(substate, overwrite=True)
902
914
903 def filerevert(self, *pats, **opts):
915 def filerevert(self, *pats, **opts):
904 ctx = self._repo[opts['rev']]
916 ctx = self._repo[opts['rev']]
905 parents = self._repo.dirstate.parents()
917 parents = self._repo.dirstate.parents()
906 if opts.get('all'):
918 if opts.get('all'):
907 pats = ['set:modified()']
919 pats = ['set:modified()']
908 else:
920 else:
909 pats = []
921 pats = []
910 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
922 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
911
923
912 def shortid(self, revid):
924 def shortid(self, revid):
913 return revid[:12]
925 return revid[:12]
914
926
915 class svnsubrepo(abstractsubrepo):
927 class svnsubrepo(abstractsubrepo):
916 def __init__(self, ctx, path, state):
928 def __init__(self, ctx, path, state):
917 super(svnsubrepo, self).__init__(ctx.repo().ui)
929 super(svnsubrepo, self).__init__(ctx.repo().ui)
918 self._path = path
930 self._path = path
919 self._state = state
931 self._state = state
920 self._ctx = ctx
932 self._ctx = ctx
921 self._exe = util.findexe('svn')
933 self._exe = util.findexe('svn')
922 if not self._exe:
934 if not self._exe:
923 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
935 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
924 % self._path)
936 % self._path)
925
937
926 def _svncommand(self, commands, filename='', failok=False):
938 def _svncommand(self, commands, filename='', failok=False):
927 cmd = [self._exe]
939 cmd = [self._exe]
928 extrakw = {}
940 extrakw = {}
929 if not self.ui.interactive():
941 if not self.ui.interactive():
930 # Making stdin be a pipe should prevent svn from behaving
942 # Making stdin be a pipe should prevent svn from behaving
931 # interactively even if we can't pass --non-interactive.
943 # interactively even if we can't pass --non-interactive.
932 extrakw['stdin'] = subprocess.PIPE
944 extrakw['stdin'] = subprocess.PIPE
933 # Starting in svn 1.5 --non-interactive is a global flag
945 # Starting in svn 1.5 --non-interactive is a global flag
934 # instead of being per-command, but we need to support 1.4 so
946 # instead of being per-command, but we need to support 1.4 so
935 # we have to be intelligent about what commands take
947 # we have to be intelligent about what commands take
936 # --non-interactive.
948 # --non-interactive.
937 if commands[0] in ('update', 'checkout', 'commit'):
949 if commands[0] in ('update', 'checkout', 'commit'):
938 cmd.append('--non-interactive')
950 cmd.append('--non-interactive')
939 cmd.extend(commands)
951 cmd.extend(commands)
940 if filename is not None:
952 if filename is not None:
941 path = os.path.join(self._ctx.repo().origroot, self._path, filename)
953 path = os.path.join(self._ctx.repo().origroot, self._path, filename)
942 cmd.append(path)
954 cmd.append(path)
943 env = dict(os.environ)
955 env = dict(os.environ)
944 # Avoid localized output, preserve current locale for everything else.
956 # Avoid localized output, preserve current locale for everything else.
945 lc_all = env.get('LC_ALL')
957 lc_all = env.get('LC_ALL')
946 if lc_all:
958 if lc_all:
947 env['LANG'] = lc_all
959 env['LANG'] = lc_all
948 del env['LC_ALL']
960 del env['LC_ALL']
949 env['LC_MESSAGES'] = 'C'
961 env['LC_MESSAGES'] = 'C'
950 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
962 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
951 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
963 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
952 universal_newlines=True, env=env, **extrakw)
964 universal_newlines=True, env=env, **extrakw)
953 stdout, stderr = p.communicate()
965 stdout, stderr = p.communicate()
954 stderr = stderr.strip()
966 stderr = stderr.strip()
955 if not failok:
967 if not failok:
956 if p.returncode:
968 if p.returncode:
957 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
969 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
958 if stderr:
970 if stderr:
959 self.ui.warn(stderr + '\n')
971 self.ui.warn(stderr + '\n')
960 return stdout, stderr
972 return stdout, stderr
961
973
962 @propertycache
974 @propertycache
963 def _svnversion(self):
975 def _svnversion(self):
964 output, err = self._svncommand(['--version', '--quiet'], filename=None)
976 output, err = self._svncommand(['--version', '--quiet'], filename=None)
965 m = re.search(r'^(\d+)\.(\d+)', output)
977 m = re.search(r'^(\d+)\.(\d+)', output)
966 if not m:
978 if not m:
967 raise util.Abort(_('cannot retrieve svn tool version'))
979 raise util.Abort(_('cannot retrieve svn tool version'))
968 return (int(m.group(1)), int(m.group(2)))
980 return (int(m.group(1)), int(m.group(2)))
969
981
970 def _wcrevs(self):
982 def _wcrevs(self):
971 # Get the working directory revision as well as the last
983 # Get the working directory revision as well as the last
972 # commit revision so we can compare the subrepo state with
984 # commit revision so we can compare the subrepo state with
973 # both. We used to store the working directory one.
985 # both. We used to store the working directory one.
974 output, err = self._svncommand(['info', '--xml'])
986 output, err = self._svncommand(['info', '--xml'])
975 doc = xml.dom.minidom.parseString(output)
987 doc = xml.dom.minidom.parseString(output)
976 entries = doc.getElementsByTagName('entry')
988 entries = doc.getElementsByTagName('entry')
977 lastrev, rev = '0', '0'
989 lastrev, rev = '0', '0'
978 if entries:
990 if entries:
979 rev = str(entries[0].getAttribute('revision')) or '0'
991 rev = str(entries[0].getAttribute('revision')) or '0'
980 commits = entries[0].getElementsByTagName('commit')
992 commits = entries[0].getElementsByTagName('commit')
981 if commits:
993 if commits:
982 lastrev = str(commits[0].getAttribute('revision')) or '0'
994 lastrev = str(commits[0].getAttribute('revision')) or '0'
983 return (lastrev, rev)
995 return (lastrev, rev)
984
996
985 def _wcrev(self):
997 def _wcrev(self):
986 return self._wcrevs()[0]
998 return self._wcrevs()[0]
987
999
988 def _wcchanged(self):
1000 def _wcchanged(self):
989 """Return (changes, extchanges, missing) where changes is True
1001 """Return (changes, extchanges, missing) where changes is True
990 if the working directory was changed, extchanges is
1002 if the working directory was changed, extchanges is
991 True if any of these changes concern an external entry and missing
1003 True if any of these changes concern an external entry and missing
992 is True if any change is a missing entry.
1004 is True if any change is a missing entry.
993 """
1005 """
994 output, err = self._svncommand(['status', '--xml'])
1006 output, err = self._svncommand(['status', '--xml'])
995 externals, changes, missing = [], [], []
1007 externals, changes, missing = [], [], []
996 doc = xml.dom.minidom.parseString(output)
1008 doc = xml.dom.minidom.parseString(output)
997 for e in doc.getElementsByTagName('entry'):
1009 for e in doc.getElementsByTagName('entry'):
998 s = e.getElementsByTagName('wc-status')
1010 s = e.getElementsByTagName('wc-status')
999 if not s:
1011 if not s:
1000 continue
1012 continue
1001 item = s[0].getAttribute('item')
1013 item = s[0].getAttribute('item')
1002 props = s[0].getAttribute('props')
1014 props = s[0].getAttribute('props')
1003 path = e.getAttribute('path')
1015 path = e.getAttribute('path')
1004 if item == 'external':
1016 if item == 'external':
1005 externals.append(path)
1017 externals.append(path)
1006 elif item == 'missing':
1018 elif item == 'missing':
1007 missing.append(path)
1019 missing.append(path)
1008 if (item not in ('', 'normal', 'unversioned', 'external')
1020 if (item not in ('', 'normal', 'unversioned', 'external')
1009 or props not in ('', 'none', 'normal')):
1021 or props not in ('', 'none', 'normal')):
1010 changes.append(path)
1022 changes.append(path)
1011 for path in changes:
1023 for path in changes:
1012 for ext in externals:
1024 for ext in externals:
1013 if path == ext or path.startswith(ext + os.sep):
1025 if path == ext or path.startswith(ext + os.sep):
1014 return True, True, bool(missing)
1026 return True, True, bool(missing)
1015 return bool(changes), False, bool(missing)
1027 return bool(changes), False, bool(missing)
1016
1028
1017 def dirty(self, ignoreupdate=False):
1029 def dirty(self, ignoreupdate=False):
1018 if not self._wcchanged()[0]:
1030 if not self._wcchanged()[0]:
1019 if self._state[1] in self._wcrevs() or ignoreupdate:
1031 if self._state[1] in self._wcrevs() or ignoreupdate:
1020 return False
1032 return False
1021 return True
1033 return True
1022
1034
1023 def basestate(self):
1035 def basestate(self):
1024 lastrev, rev = self._wcrevs()
1036 lastrev, rev = self._wcrevs()
1025 if lastrev != rev:
1037 if lastrev != rev:
1026 # Last committed rev is not the same than rev. We would
1038 # Last committed rev is not the same than rev. We would
1027 # like to take lastrev but we do not know if the subrepo
1039 # like to take lastrev but we do not know if the subrepo
1028 # URL exists at lastrev. Test it and fallback to rev it
1040 # URL exists at lastrev. Test it and fallback to rev it
1029 # is not there.
1041 # is not there.
1030 try:
1042 try:
1031 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1043 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1032 return lastrev
1044 return lastrev
1033 except error.Abort:
1045 except error.Abort:
1034 pass
1046 pass
1035 return rev
1047 return rev
1036
1048
1037 @annotatesubrepoerror
1049 @annotatesubrepoerror
1038 def commit(self, text, user, date):
1050 def commit(self, text, user, date):
1039 # user and date are out of our hands since svn is centralized
1051 # user and date are out of our hands since svn is centralized
1040 changed, extchanged, missing = self._wcchanged()
1052 changed, extchanged, missing = self._wcchanged()
1041 if not changed:
1053 if not changed:
1042 return self.basestate()
1054 return self.basestate()
1043 if extchanged:
1055 if extchanged:
1044 # Do not try to commit externals
1056 # Do not try to commit externals
1045 raise util.Abort(_('cannot commit svn externals'))
1057 raise util.Abort(_('cannot commit svn externals'))
1046 if missing:
1058 if missing:
1047 # svn can commit with missing entries but aborting like hg
1059 # svn can commit with missing entries but aborting like hg
1048 # seems a better approach.
1060 # seems a better approach.
1049 raise util.Abort(_('cannot commit missing svn entries'))
1061 raise util.Abort(_('cannot commit missing svn entries'))
1050 commitinfo, err = self._svncommand(['commit', '-m', text])
1062 commitinfo, err = self._svncommand(['commit', '-m', text])
1051 self.ui.status(commitinfo)
1063 self.ui.status(commitinfo)
1052 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1064 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1053 if not newrev:
1065 if not newrev:
1054 if not commitinfo.strip():
1066 if not commitinfo.strip():
1055 # Sometimes, our definition of "changed" differs from
1067 # Sometimes, our definition of "changed" differs from
1056 # svn one. For instance, svn ignores missing files
1068 # svn one. For instance, svn ignores missing files
1057 # when committing. If there are only missing files, no
1069 # when committing. If there are only missing files, no
1058 # commit is made, no output and no error code.
1070 # commit is made, no output and no error code.
1059 raise util.Abort(_('failed to commit svn changes'))
1071 raise util.Abort(_('failed to commit svn changes'))
1060 raise util.Abort(commitinfo.splitlines()[-1])
1072 raise util.Abort(commitinfo.splitlines()[-1])
1061 newrev = newrev.groups()[0]
1073 newrev = newrev.groups()[0]
1062 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1074 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1063 return newrev
1075 return newrev
1064
1076
1065 @annotatesubrepoerror
1077 @annotatesubrepoerror
1066 def remove(self):
1078 def remove(self):
1067 if self.dirty():
1079 if self.dirty():
1068 self.ui.warn(_('not removing repo %s because '
1080 self.ui.warn(_('not removing repo %s because '
1069 'it has changes.\n') % self._path)
1081 'it has changes.\n') % self._path)
1070 return
1082 return
1071 self.ui.note(_('removing subrepo %s\n') % self._path)
1083 self.ui.note(_('removing subrepo %s\n') % self._path)
1072
1084
1073 def onerror(function, path, excinfo):
1085 def onerror(function, path, excinfo):
1074 if function is not os.remove:
1086 if function is not os.remove:
1075 raise
1087 raise
1076 # read-only files cannot be unlinked under Windows
1088 # read-only files cannot be unlinked under Windows
1077 s = os.stat(path)
1089 s = os.stat(path)
1078 if (s.st_mode & stat.S_IWRITE) != 0:
1090 if (s.st_mode & stat.S_IWRITE) != 0:
1079 raise
1091 raise
1080 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
1092 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
1081 os.remove(path)
1093 os.remove(path)
1082
1094
1083 path = self._ctx.repo().wjoin(self._path)
1095 path = self._ctx.repo().wjoin(self._path)
1084 shutil.rmtree(path, onerror=onerror)
1096 shutil.rmtree(path, onerror=onerror)
1085 try:
1097 try:
1086 os.removedirs(os.path.dirname(path))
1098 os.removedirs(os.path.dirname(path))
1087 except OSError:
1099 except OSError:
1088 pass
1100 pass
1089
1101
1090 @annotatesubrepoerror
1102 @annotatesubrepoerror
1091 def get(self, state, overwrite=False):
1103 def get(self, state, overwrite=False):
1092 if overwrite:
1104 if overwrite:
1093 self._svncommand(['revert', '--recursive'])
1105 self._svncommand(['revert', '--recursive'])
1094 args = ['checkout']
1106 args = ['checkout']
1095 if self._svnversion >= (1, 5):
1107 if self._svnversion >= (1, 5):
1096 args.append('--force')
1108 args.append('--force')
1097 # The revision must be specified at the end of the URL to properly
1109 # The revision must be specified at the end of the URL to properly
1098 # update to a directory which has since been deleted and recreated.
1110 # update to a directory which has since been deleted and recreated.
1099 args.append('%s@%s' % (state[0], state[1]))
1111 args.append('%s@%s' % (state[0], state[1]))
1100 status, err = self._svncommand(args, failok=True)
1112 status, err = self._svncommand(args, failok=True)
1101 _sanitize(self.ui, self._ctx.repo().wjoin(self._path), '.svn')
1113 _sanitize(self.ui, self._ctx.repo().wjoin(self._path), '.svn')
1102 if not re.search('Checked out revision [0-9]+.', status):
1114 if not re.search('Checked out revision [0-9]+.', status):
1103 if ('is already a working copy for a different URL' in err
1115 if ('is already a working copy for a different URL' in err
1104 and (self._wcchanged()[:2] == (False, False))):
1116 and (self._wcchanged()[:2] == (False, False))):
1105 # obstructed but clean working copy, so just blow it away.
1117 # obstructed but clean working copy, so just blow it away.
1106 self.remove()
1118 self.remove()
1107 self.get(state, overwrite=False)
1119 self.get(state, overwrite=False)
1108 return
1120 return
1109 raise util.Abort((status or err).splitlines()[-1])
1121 raise util.Abort((status or err).splitlines()[-1])
1110 self.ui.status(status)
1122 self.ui.status(status)
1111
1123
1112 @annotatesubrepoerror
1124 @annotatesubrepoerror
1113 def merge(self, state):
1125 def merge(self, state):
1114 old = self._state[1]
1126 old = self._state[1]
1115 new = state[1]
1127 new = state[1]
1116 wcrev = self._wcrev()
1128 wcrev = self._wcrev()
1117 if new != wcrev:
1129 if new != wcrev:
1118 dirty = old == wcrev or self._wcchanged()[0]
1130 dirty = old == wcrev or self._wcchanged()[0]
1119 if _updateprompt(self.ui, self, dirty, wcrev, new):
1131 if _updateprompt(self.ui, self, dirty, wcrev, new):
1120 self.get(state, False)
1132 self.get(state, False)
1121
1133
1122 def push(self, opts):
1134 def push(self, opts):
1123 # push is a no-op for SVN
1135 # push is a no-op for SVN
1124 return True
1136 return True
1125
1137
1126 @annotatesubrepoerror
1138 @annotatesubrepoerror
1127 def files(self):
1139 def files(self):
1128 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1140 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1129 doc = xml.dom.minidom.parseString(output)
1141 doc = xml.dom.minidom.parseString(output)
1130 paths = []
1142 paths = []
1131 for e in doc.getElementsByTagName('entry'):
1143 for e in doc.getElementsByTagName('entry'):
1132 kind = str(e.getAttribute('kind'))
1144 kind = str(e.getAttribute('kind'))
1133 if kind != 'file':
1145 if kind != 'file':
1134 continue
1146 continue
1135 name = ''.join(c.data for c
1147 name = ''.join(c.data for c
1136 in e.getElementsByTagName('name')[0].childNodes
1148 in e.getElementsByTagName('name')[0].childNodes
1137 if c.nodeType == c.TEXT_NODE)
1149 if c.nodeType == c.TEXT_NODE)
1138 paths.append(name.encode('utf-8'))
1150 paths.append(name.encode('utf-8'))
1139 return paths
1151 return paths
1140
1152
1141 def filedata(self, name):
1153 def filedata(self, name):
1142 return self._svncommand(['cat'], name)[0]
1154 return self._svncommand(['cat'], name)[0]
1143
1155
1144
1156
1145 class gitsubrepo(abstractsubrepo):
1157 class gitsubrepo(abstractsubrepo):
1146 def __init__(self, ctx, path, state):
1158 def __init__(self, ctx, path, state):
1147 super(gitsubrepo, self).__init__(ctx.repo().ui)
1159 super(gitsubrepo, self).__init__(ctx.repo().ui)
1148 self._state = state
1160 self._state = state
1149 self._ctx = ctx
1161 self._ctx = ctx
1150 self._path = path
1162 self._path = path
1151 self._relpath = os.path.join(reporelpath(ctx.repo()), path)
1163 self._relpath = os.path.join(reporelpath(ctx.repo()), path)
1152 self._abspath = ctx.repo().wjoin(path)
1164 self._abspath = ctx.repo().wjoin(path)
1153 self._subparent = ctx.repo()
1165 self._subparent = ctx.repo()
1154 self._ensuregit()
1166 self._ensuregit()
1155
1167
1156 def _ensuregit(self):
1168 def _ensuregit(self):
1157 try:
1169 try:
1158 self._gitexecutable = 'git'
1170 self._gitexecutable = 'git'
1159 out, err = self._gitnodir(['--version'])
1171 out, err = self._gitnodir(['--version'])
1160 except OSError, e:
1172 except OSError, e:
1161 if e.errno != 2 or os.name != 'nt':
1173 if e.errno != 2 or os.name != 'nt':
1162 raise
1174 raise
1163 self._gitexecutable = 'git.cmd'
1175 self._gitexecutable = 'git.cmd'
1164 out, err = self._gitnodir(['--version'])
1176 out, err = self._gitnodir(['--version'])
1165 versionstatus = self._checkversion(out)
1177 versionstatus = self._checkversion(out)
1166 if versionstatus == 'unknown':
1178 if versionstatus == 'unknown':
1167 self.ui.warn(_('cannot retrieve git version\n'))
1179 self.ui.warn(_('cannot retrieve git version\n'))
1168 elif versionstatus == 'abort':
1180 elif versionstatus == 'abort':
1169 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
1181 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
1170 elif versionstatus == 'warning':
1182 elif versionstatus == 'warning':
1171 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1183 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1172
1184
1173 @staticmethod
1185 @staticmethod
1174 def _gitversion(out):
1186 def _gitversion(out):
1175 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1187 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1176 if m:
1188 if m:
1177 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1189 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1178
1190
1179 m = re.search(r'^git version (\d+)\.(\d+)', out)
1191 m = re.search(r'^git version (\d+)\.(\d+)', out)
1180 if m:
1192 if m:
1181 return (int(m.group(1)), int(m.group(2)), 0)
1193 return (int(m.group(1)), int(m.group(2)), 0)
1182
1194
1183 return -1
1195 return -1
1184
1196
1185 @staticmethod
1197 @staticmethod
1186 def _checkversion(out):
1198 def _checkversion(out):
1187 '''ensure git version is new enough
1199 '''ensure git version is new enough
1188
1200
1189 >>> _checkversion = gitsubrepo._checkversion
1201 >>> _checkversion = gitsubrepo._checkversion
1190 >>> _checkversion('git version 1.6.0')
1202 >>> _checkversion('git version 1.6.0')
1191 'ok'
1203 'ok'
1192 >>> _checkversion('git version 1.8.5')
1204 >>> _checkversion('git version 1.8.5')
1193 'ok'
1205 'ok'
1194 >>> _checkversion('git version 1.4.0')
1206 >>> _checkversion('git version 1.4.0')
1195 'abort'
1207 'abort'
1196 >>> _checkversion('git version 1.5.0')
1208 >>> _checkversion('git version 1.5.0')
1197 'warning'
1209 'warning'
1198 >>> _checkversion('git version 1.9-rc0')
1210 >>> _checkversion('git version 1.9-rc0')
1199 'ok'
1211 'ok'
1200 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1212 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1201 'ok'
1213 'ok'
1202 >>> _checkversion('git version 1.9.0.GIT')
1214 >>> _checkversion('git version 1.9.0.GIT')
1203 'ok'
1215 'ok'
1204 >>> _checkversion('git version 12345')
1216 >>> _checkversion('git version 12345')
1205 'unknown'
1217 'unknown'
1206 >>> _checkversion('no')
1218 >>> _checkversion('no')
1207 'unknown'
1219 'unknown'
1208 '''
1220 '''
1209 version = gitsubrepo._gitversion(out)
1221 version = gitsubrepo._gitversion(out)
1210 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1222 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1211 # despite the docstring comment. For now, error on 1.4.0, warn on
1223 # despite the docstring comment. For now, error on 1.4.0, warn on
1212 # 1.5.0 but attempt to continue.
1224 # 1.5.0 but attempt to continue.
1213 if version == -1:
1225 if version == -1:
1214 return 'unknown'
1226 return 'unknown'
1215 if version < (1, 5, 0):
1227 if version < (1, 5, 0):
1216 return 'abort'
1228 return 'abort'
1217 elif version < (1, 6, 0):
1229 elif version < (1, 6, 0):
1218 return 'warning'
1230 return 'warning'
1219 return 'ok'
1231 return 'ok'
1220
1232
1221 def _gitcommand(self, commands, env=None, stream=False):
1233 def _gitcommand(self, commands, env=None, stream=False):
1222 return self._gitdir(commands, env=env, stream=stream)[0]
1234 return self._gitdir(commands, env=env, stream=stream)[0]
1223
1235
1224 def _gitdir(self, commands, env=None, stream=False):
1236 def _gitdir(self, commands, env=None, stream=False):
1225 return self._gitnodir(commands, env=env, stream=stream,
1237 return self._gitnodir(commands, env=env, stream=stream,
1226 cwd=self._abspath)
1238 cwd=self._abspath)
1227
1239
1228 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1240 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1229 """Calls the git command
1241 """Calls the git command
1230
1242
1231 The methods tries to call the git command. versions prior to 1.6.0
1243 The methods tries to call the git command. versions prior to 1.6.0
1232 are not supported and very probably fail.
1244 are not supported and very probably fail.
1233 """
1245 """
1234 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1246 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1235 # unless ui.quiet is set, print git's stderr,
1247 # unless ui.quiet is set, print git's stderr,
1236 # which is mostly progress and useful info
1248 # which is mostly progress and useful info
1237 errpipe = None
1249 errpipe = None
1238 if self.ui.quiet:
1250 if self.ui.quiet:
1239 errpipe = open(os.devnull, 'w')
1251 errpipe = open(os.devnull, 'w')
1240 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1252 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1241 cwd=cwd, env=env, close_fds=util.closefds,
1253 cwd=cwd, env=env, close_fds=util.closefds,
1242 stdout=subprocess.PIPE, stderr=errpipe)
1254 stdout=subprocess.PIPE, stderr=errpipe)
1243 if stream:
1255 if stream:
1244 return p.stdout, None
1256 return p.stdout, None
1245
1257
1246 retdata = p.stdout.read().strip()
1258 retdata = p.stdout.read().strip()
1247 # wait for the child to exit to avoid race condition.
1259 # wait for the child to exit to avoid race condition.
1248 p.wait()
1260 p.wait()
1249
1261
1250 if p.returncode != 0 and p.returncode != 1:
1262 if p.returncode != 0 and p.returncode != 1:
1251 # there are certain error codes that are ok
1263 # there are certain error codes that are ok
1252 command = commands[0]
1264 command = commands[0]
1253 if command in ('cat-file', 'symbolic-ref'):
1265 if command in ('cat-file', 'symbolic-ref'):
1254 return retdata, p.returncode
1266 return retdata, p.returncode
1255 # for all others, abort
1267 # for all others, abort
1256 raise util.Abort('git %s error %d in %s' %
1268 raise util.Abort('git %s error %d in %s' %
1257 (command, p.returncode, self._relpath))
1269 (command, p.returncode, self._relpath))
1258
1270
1259 return retdata, p.returncode
1271 return retdata, p.returncode
1260
1272
1261 def _gitmissing(self):
1273 def _gitmissing(self):
1262 return not os.path.exists(os.path.join(self._abspath, '.git'))
1274 return not os.path.exists(os.path.join(self._abspath, '.git'))
1263
1275
1264 def _gitstate(self):
1276 def _gitstate(self):
1265 return self._gitcommand(['rev-parse', 'HEAD'])
1277 return self._gitcommand(['rev-parse', 'HEAD'])
1266
1278
1267 def _gitcurrentbranch(self):
1279 def _gitcurrentbranch(self):
1268 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1280 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1269 if err:
1281 if err:
1270 current = None
1282 current = None
1271 return current
1283 return current
1272
1284
1273 def _gitremote(self, remote):
1285 def _gitremote(self, remote):
1274 out = self._gitcommand(['remote', 'show', '-n', remote])
1286 out = self._gitcommand(['remote', 'show', '-n', remote])
1275 line = out.split('\n')[1]
1287 line = out.split('\n')[1]
1276 i = line.index('URL: ') + len('URL: ')
1288 i = line.index('URL: ') + len('URL: ')
1277 return line[i:]
1289 return line[i:]
1278
1290
1279 def _githavelocally(self, revision):
1291 def _githavelocally(self, revision):
1280 out, code = self._gitdir(['cat-file', '-e', revision])
1292 out, code = self._gitdir(['cat-file', '-e', revision])
1281 return code == 0
1293 return code == 0
1282
1294
1283 def _gitisancestor(self, r1, r2):
1295 def _gitisancestor(self, r1, r2):
1284 base = self._gitcommand(['merge-base', r1, r2])
1296 base = self._gitcommand(['merge-base', r1, r2])
1285 return base == r1
1297 return base == r1
1286
1298
1287 def _gitisbare(self):
1299 def _gitisbare(self):
1288 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1300 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1289
1301
1290 def _gitupdatestat(self):
1302 def _gitupdatestat(self):
1291 """This must be run before git diff-index.
1303 """This must be run before git diff-index.
1292 diff-index only looks at changes to file stat;
1304 diff-index only looks at changes to file stat;
1293 this command looks at file contents and updates the stat."""
1305 this command looks at file contents and updates the stat."""
1294 self._gitcommand(['update-index', '-q', '--refresh'])
1306 self._gitcommand(['update-index', '-q', '--refresh'])
1295
1307
1296 def _gitbranchmap(self):
1308 def _gitbranchmap(self):
1297 '''returns 2 things:
1309 '''returns 2 things:
1298 a map from git branch to revision
1310 a map from git branch to revision
1299 a map from revision to branches'''
1311 a map from revision to branches'''
1300 branch2rev = {}
1312 branch2rev = {}
1301 rev2branch = {}
1313 rev2branch = {}
1302
1314
1303 out = self._gitcommand(['for-each-ref', '--format',
1315 out = self._gitcommand(['for-each-ref', '--format',
1304 '%(objectname) %(refname)'])
1316 '%(objectname) %(refname)'])
1305 for line in out.split('\n'):
1317 for line in out.split('\n'):
1306 revision, ref = line.split(' ')
1318 revision, ref = line.split(' ')
1307 if (not ref.startswith('refs/heads/') and
1319 if (not ref.startswith('refs/heads/') and
1308 not ref.startswith('refs/remotes/')):
1320 not ref.startswith('refs/remotes/')):
1309 continue
1321 continue
1310 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1322 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1311 continue # ignore remote/HEAD redirects
1323 continue # ignore remote/HEAD redirects
1312 branch2rev[ref] = revision
1324 branch2rev[ref] = revision
1313 rev2branch.setdefault(revision, []).append(ref)
1325 rev2branch.setdefault(revision, []).append(ref)
1314 return branch2rev, rev2branch
1326 return branch2rev, rev2branch
1315
1327
1316 def _gittracking(self, branches):
1328 def _gittracking(self, branches):
1317 'return map of remote branch to local tracking branch'
1329 'return map of remote branch to local tracking branch'
1318 # assumes no more than one local tracking branch for each remote
1330 # assumes no more than one local tracking branch for each remote
1319 tracking = {}
1331 tracking = {}
1320 for b in branches:
1332 for b in branches:
1321 if b.startswith('refs/remotes/'):
1333 if b.startswith('refs/remotes/'):
1322 continue
1334 continue
1323 bname = b.split('/', 2)[2]
1335 bname = b.split('/', 2)[2]
1324 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1336 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1325 if remote:
1337 if remote:
1326 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1338 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1327 tracking['refs/remotes/%s/%s' %
1339 tracking['refs/remotes/%s/%s' %
1328 (remote, ref.split('/', 2)[2])] = b
1340 (remote, ref.split('/', 2)[2])] = b
1329 return tracking
1341 return tracking
1330
1342
1331 def _abssource(self, source):
1343 def _abssource(self, source):
1332 if '://' not in source:
1344 if '://' not in source:
1333 # recognize the scp syntax as an absolute source
1345 # recognize the scp syntax as an absolute source
1334 colon = source.find(':')
1346 colon = source.find(':')
1335 if colon != -1 and '/' not in source[:colon]:
1347 if colon != -1 and '/' not in source[:colon]:
1336 return source
1348 return source
1337 self._subsource = source
1349 self._subsource = source
1338 return _abssource(self)
1350 return _abssource(self)
1339
1351
1340 def _fetch(self, source, revision):
1352 def _fetch(self, source, revision):
1341 if self._gitmissing():
1353 if self._gitmissing():
1342 source = self._abssource(source)
1354 source = self._abssource(source)
1343 self.ui.status(_('cloning subrepo %s from %s\n') %
1355 self.ui.status(_('cloning subrepo %s from %s\n') %
1344 (self._relpath, source))
1356 (self._relpath, source))
1345 self._gitnodir(['clone', source, self._abspath])
1357 self._gitnodir(['clone', source, self._abspath])
1346 if self._githavelocally(revision):
1358 if self._githavelocally(revision):
1347 return
1359 return
1348 self.ui.status(_('pulling subrepo %s from %s\n') %
1360 self.ui.status(_('pulling subrepo %s from %s\n') %
1349 (self._relpath, self._gitremote('origin')))
1361 (self._relpath, self._gitremote('origin')))
1350 # try only origin: the originally cloned repo
1362 # try only origin: the originally cloned repo
1351 self._gitcommand(['fetch'])
1363 self._gitcommand(['fetch'])
1352 if not self._githavelocally(revision):
1364 if not self._githavelocally(revision):
1353 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1365 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1354 (revision, self._relpath))
1366 (revision, self._relpath))
1355
1367
1356 @annotatesubrepoerror
1368 @annotatesubrepoerror
1357 def dirty(self, ignoreupdate=False):
1369 def dirty(self, ignoreupdate=False):
1358 if self._gitmissing():
1370 if self._gitmissing():
1359 return self._state[1] != ''
1371 return self._state[1] != ''
1360 if self._gitisbare():
1372 if self._gitisbare():
1361 return True
1373 return True
1362 if not ignoreupdate and self._state[1] != self._gitstate():
1374 if not ignoreupdate and self._state[1] != self._gitstate():
1363 # different version checked out
1375 # different version checked out
1364 return True
1376 return True
1365 # check for staged changes or modified files; ignore untracked files
1377 # check for staged changes or modified files; ignore untracked files
1366 self._gitupdatestat()
1378 self._gitupdatestat()
1367 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1379 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1368 return code == 1
1380 return code == 1
1369
1381
1370 def basestate(self):
1382 def basestate(self):
1371 return self._gitstate()
1383 return self._gitstate()
1372
1384
1373 @annotatesubrepoerror
1385 @annotatesubrepoerror
1374 def get(self, state, overwrite=False):
1386 def get(self, state, overwrite=False):
1375 source, revision, kind = state
1387 source, revision, kind = state
1376 if not revision:
1388 if not revision:
1377 self.remove()
1389 self.remove()
1378 return
1390 return
1379 self._fetch(source, revision)
1391 self._fetch(source, revision)
1380 # if the repo was set to be bare, unbare it
1392 # if the repo was set to be bare, unbare it
1381 if self._gitisbare():
1393 if self._gitisbare():
1382 self._gitcommand(['config', 'core.bare', 'false'])
1394 self._gitcommand(['config', 'core.bare', 'false'])
1383 if self._gitstate() == revision:
1395 if self._gitstate() == revision:
1384 self._gitcommand(['reset', '--hard', 'HEAD'])
1396 self._gitcommand(['reset', '--hard', 'HEAD'])
1385 return
1397 return
1386 elif self._gitstate() == revision:
1398 elif self._gitstate() == revision:
1387 if overwrite:
1399 if overwrite:
1388 # first reset the index to unmark new files for commit, because
1400 # first reset the index to unmark new files for commit, because
1389 # reset --hard will otherwise throw away files added for commit,
1401 # reset --hard will otherwise throw away files added for commit,
1390 # not just unmark them.
1402 # not just unmark them.
1391 self._gitcommand(['reset', 'HEAD'])
1403 self._gitcommand(['reset', 'HEAD'])
1392 self._gitcommand(['reset', '--hard', 'HEAD'])
1404 self._gitcommand(['reset', '--hard', 'HEAD'])
1393 return
1405 return
1394 branch2rev, rev2branch = self._gitbranchmap()
1406 branch2rev, rev2branch = self._gitbranchmap()
1395
1407
1396 def checkout(args):
1408 def checkout(args):
1397 cmd = ['checkout']
1409 cmd = ['checkout']
1398 if overwrite:
1410 if overwrite:
1399 # first reset the index to unmark new files for commit, because
1411 # first reset the index to unmark new files for commit, because
1400 # the -f option will otherwise throw away files added for
1412 # the -f option will otherwise throw away files added for
1401 # commit, not just unmark them.
1413 # commit, not just unmark them.
1402 self._gitcommand(['reset', 'HEAD'])
1414 self._gitcommand(['reset', 'HEAD'])
1403 cmd.append('-f')
1415 cmd.append('-f')
1404 self._gitcommand(cmd + args)
1416 self._gitcommand(cmd + args)
1405 _sanitize(self.ui, self._abspath, '.git')
1417 _sanitize(self.ui, self._abspath, '.git')
1406
1418
1407 def rawcheckout():
1419 def rawcheckout():
1408 # no branch to checkout, check it out with no branch
1420 # no branch to checkout, check it out with no branch
1409 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1421 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1410 self._relpath)
1422 self._relpath)
1411 self.ui.warn(_('check out a git branch if you intend '
1423 self.ui.warn(_('check out a git branch if you intend '
1412 'to make changes\n'))
1424 'to make changes\n'))
1413 checkout(['-q', revision])
1425 checkout(['-q', revision])
1414
1426
1415 if revision not in rev2branch:
1427 if revision not in rev2branch:
1416 rawcheckout()
1428 rawcheckout()
1417 return
1429 return
1418 branches = rev2branch[revision]
1430 branches = rev2branch[revision]
1419 firstlocalbranch = None
1431 firstlocalbranch = None
1420 for b in branches:
1432 for b in branches:
1421 if b == 'refs/heads/master':
1433 if b == 'refs/heads/master':
1422 # master trumps all other branches
1434 # master trumps all other branches
1423 checkout(['refs/heads/master'])
1435 checkout(['refs/heads/master'])
1424 return
1436 return
1425 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1437 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1426 firstlocalbranch = b
1438 firstlocalbranch = b
1427 if firstlocalbranch:
1439 if firstlocalbranch:
1428 checkout([firstlocalbranch])
1440 checkout([firstlocalbranch])
1429 return
1441 return
1430
1442
1431 tracking = self._gittracking(branch2rev.keys())
1443 tracking = self._gittracking(branch2rev.keys())
1432 # choose a remote branch already tracked if possible
1444 # choose a remote branch already tracked if possible
1433 remote = branches[0]
1445 remote = branches[0]
1434 if remote not in tracking:
1446 if remote not in tracking:
1435 for b in branches:
1447 for b in branches:
1436 if b in tracking:
1448 if b in tracking:
1437 remote = b
1449 remote = b
1438 break
1450 break
1439
1451
1440 if remote not in tracking:
1452 if remote not in tracking:
1441 # create a new local tracking branch
1453 # create a new local tracking branch
1442 local = remote.split('/', 3)[3]
1454 local = remote.split('/', 3)[3]
1443 checkout(['-b', local, remote])
1455 checkout(['-b', local, remote])
1444 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1456 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1445 # When updating to a tracked remote branch,
1457 # When updating to a tracked remote branch,
1446 # if the local tracking branch is downstream of it,
1458 # if the local tracking branch is downstream of it,
1447 # a normal `git pull` would have performed a "fast-forward merge"
1459 # a normal `git pull` would have performed a "fast-forward merge"
1448 # which is equivalent to updating the local branch to the remote.
1460 # which is equivalent to updating the local branch to the remote.
1449 # Since we are only looking at branching at update, we need to
1461 # Since we are only looking at branching at update, we need to
1450 # detect this situation and perform this action lazily.
1462 # detect this situation and perform this action lazily.
1451 if tracking[remote] != self._gitcurrentbranch():
1463 if tracking[remote] != self._gitcurrentbranch():
1452 checkout([tracking[remote]])
1464 checkout([tracking[remote]])
1453 self._gitcommand(['merge', '--ff', remote])
1465 self._gitcommand(['merge', '--ff', remote])
1454 _sanitize(self.ui, self._abspath, '.git')
1466 _sanitize(self.ui, self._abspath, '.git')
1455 else:
1467 else:
1456 # a real merge would be required, just checkout the revision
1468 # a real merge would be required, just checkout the revision
1457 rawcheckout()
1469 rawcheckout()
1458
1470
1459 @annotatesubrepoerror
1471 @annotatesubrepoerror
1460 def commit(self, text, user, date):
1472 def commit(self, text, user, date):
1461 if self._gitmissing():
1473 if self._gitmissing():
1462 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1474 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1463 cmd = ['commit', '-a', '-m', text]
1475 cmd = ['commit', '-a', '-m', text]
1464 env = os.environ.copy()
1476 env = os.environ.copy()
1465 if user:
1477 if user:
1466 cmd += ['--author', user]
1478 cmd += ['--author', user]
1467 if date:
1479 if date:
1468 # git's date parser silently ignores when seconds < 1e9
1480 # git's date parser silently ignores when seconds < 1e9
1469 # convert to ISO8601
1481 # convert to ISO8601
1470 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1482 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1471 '%Y-%m-%dT%H:%M:%S %1%2')
1483 '%Y-%m-%dT%H:%M:%S %1%2')
1472 self._gitcommand(cmd, env=env)
1484 self._gitcommand(cmd, env=env)
1473 # make sure commit works otherwise HEAD might not exist under certain
1485 # make sure commit works otherwise HEAD might not exist under certain
1474 # circumstances
1486 # circumstances
1475 return self._gitstate()
1487 return self._gitstate()
1476
1488
1477 @annotatesubrepoerror
1489 @annotatesubrepoerror
1478 def merge(self, state):
1490 def merge(self, state):
1479 source, revision, kind = state
1491 source, revision, kind = state
1480 self._fetch(source, revision)
1492 self._fetch(source, revision)
1481 base = self._gitcommand(['merge-base', revision, self._state[1]])
1493 base = self._gitcommand(['merge-base', revision, self._state[1]])
1482 self._gitupdatestat()
1494 self._gitupdatestat()
1483 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1495 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1484
1496
1485 def mergefunc():
1497 def mergefunc():
1486 if base == revision:
1498 if base == revision:
1487 self.get(state) # fast forward merge
1499 self.get(state) # fast forward merge
1488 elif base != self._state[1]:
1500 elif base != self._state[1]:
1489 self._gitcommand(['merge', '--no-commit', revision])
1501 self._gitcommand(['merge', '--no-commit', revision])
1490 _sanitize(self.ui, self._abspath, '.git')
1502 _sanitize(self.ui, self._abspath, '.git')
1491
1503
1492 if self.dirty():
1504 if self.dirty():
1493 if self._gitstate() != revision:
1505 if self._gitstate() != revision:
1494 dirty = self._gitstate() == self._state[1] or code != 0
1506 dirty = self._gitstate() == self._state[1] or code != 0
1495 if _updateprompt(self.ui, self, dirty,
1507 if _updateprompt(self.ui, self, dirty,
1496 self._state[1][:7], revision[:7]):
1508 self._state[1][:7], revision[:7]):
1497 mergefunc()
1509 mergefunc()
1498 else:
1510 else:
1499 mergefunc()
1511 mergefunc()
1500
1512
1501 @annotatesubrepoerror
1513 @annotatesubrepoerror
1502 def push(self, opts):
1514 def push(self, opts):
1503 force = opts.get('force')
1515 force = opts.get('force')
1504
1516
1505 if not self._state[1]:
1517 if not self._state[1]:
1506 return True
1518 return True
1507 if self._gitmissing():
1519 if self._gitmissing():
1508 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1520 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1509 # if a branch in origin contains the revision, nothing to do
1521 # if a branch in origin contains the revision, nothing to do
1510 branch2rev, rev2branch = self._gitbranchmap()
1522 branch2rev, rev2branch = self._gitbranchmap()
1511 if self._state[1] in rev2branch:
1523 if self._state[1] in rev2branch:
1512 for b in rev2branch[self._state[1]]:
1524 for b in rev2branch[self._state[1]]:
1513 if b.startswith('refs/remotes/origin/'):
1525 if b.startswith('refs/remotes/origin/'):
1514 return True
1526 return True
1515 for b, revision in branch2rev.iteritems():
1527 for b, revision in branch2rev.iteritems():
1516 if b.startswith('refs/remotes/origin/'):
1528 if b.startswith('refs/remotes/origin/'):
1517 if self._gitisancestor(self._state[1], revision):
1529 if self._gitisancestor(self._state[1], revision):
1518 return True
1530 return True
1519 # otherwise, try to push the currently checked out branch
1531 # otherwise, try to push the currently checked out branch
1520 cmd = ['push']
1532 cmd = ['push']
1521 if force:
1533 if force:
1522 cmd.append('--force')
1534 cmd.append('--force')
1523
1535
1524 current = self._gitcurrentbranch()
1536 current = self._gitcurrentbranch()
1525 if current:
1537 if current:
1526 # determine if the current branch is even useful
1538 # determine if the current branch is even useful
1527 if not self._gitisancestor(self._state[1], current):
1539 if not self._gitisancestor(self._state[1], current):
1528 self.ui.warn(_('unrelated git branch checked out '
1540 self.ui.warn(_('unrelated git branch checked out '
1529 'in subrepo %s\n') % self._relpath)
1541 'in subrepo %s\n') % self._relpath)
1530 return False
1542 return False
1531 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1543 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1532 (current.split('/', 2)[2], self._relpath))
1544 (current.split('/', 2)[2], self._relpath))
1533 ret = self._gitdir(cmd + ['origin', current])
1545 ret = self._gitdir(cmd + ['origin', current])
1534 return ret[1] == 0
1546 return ret[1] == 0
1535 else:
1547 else:
1536 self.ui.warn(_('no branch checked out in subrepo %s\n'
1548 self.ui.warn(_('no branch checked out in subrepo %s\n'
1537 'cannot push revision %s\n') %
1549 'cannot push revision %s\n') %
1538 (self._relpath, self._state[1]))
1550 (self._relpath, self._state[1]))
1539 return False
1551 return False
1540
1552
1541 @annotatesubrepoerror
1553 @annotatesubrepoerror
1542 def add(self, ui, match, prefix, explicitonly, **opts):
1554 def add(self, ui, match, prefix, explicitonly, **opts):
1543 if self._gitmissing():
1555 if self._gitmissing():
1544 return []
1556 return []
1545
1557
1546 (modified, added, removed,
1558 (modified, added, removed,
1547 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1559 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1548 clean=True)
1560 clean=True)
1549
1561
1550 tracked = set()
1562 tracked = set()
1551 # dirstates 'amn' warn, 'r' is added again
1563 # dirstates 'amn' warn, 'r' is added again
1552 for l in (modified, added, deleted, clean):
1564 for l in (modified, added, deleted, clean):
1553 tracked.update(l)
1565 tracked.update(l)
1554
1566
1555 # Unknown files not of interest will be rejected by the matcher
1567 # Unknown files not of interest will be rejected by the matcher
1556 files = unknown
1568 files = unknown
1557 files.extend(match.files())
1569 files.extend(match.files())
1558
1570
1559 rejected = []
1571 rejected = []
1560
1572
1561 files = [f for f in sorted(set(files)) if match(f)]
1573 files = [f for f in sorted(set(files)) if match(f)]
1562 for f in files:
1574 for f in files:
1563 exact = match.exact(f)
1575 exact = match.exact(f)
1564 command = ["add"]
1576 command = ["add"]
1565 if exact:
1577 if exact:
1566 command.append("-f") #should be added, even if ignored
1578 command.append("-f") #should be added, even if ignored
1567 if ui.verbose or not exact:
1579 if ui.verbose or not exact:
1568 ui.status(_('adding %s\n') % match.rel(f))
1580 ui.status(_('adding %s\n') % match.rel(f))
1569
1581
1570 if f in tracked: # hg prints 'adding' even if already tracked
1582 if f in tracked: # hg prints 'adding' even if already tracked
1571 if exact:
1583 if exact:
1572 rejected.append(f)
1584 rejected.append(f)
1573 continue
1585 continue
1574 if not opts.get('dry_run'):
1586 if not opts.get('dry_run'):
1575 self._gitcommand(command + [f])
1587 self._gitcommand(command + [f])
1576
1588
1577 for f in rejected:
1589 for f in rejected:
1578 ui.warn(_("%s already tracked!\n") % match.abs(f))
1590 ui.warn(_("%s already tracked!\n") % match.abs(f))
1579
1591
1580 return rejected
1592 return rejected
1581
1593
1582 @annotatesubrepoerror
1594 @annotatesubrepoerror
1583 def remove(self):
1595 def remove(self):
1584 if self._gitmissing():
1596 if self._gitmissing():
1585 return
1597 return
1586 if self.dirty():
1598 if self.dirty():
1587 self.ui.warn(_('not removing repo %s because '
1599 self.ui.warn(_('not removing repo %s because '
1588 'it has changes.\n') % self._relpath)
1600 'it has changes.\n') % self._relpath)
1589 return
1601 return
1590 # we can't fully delete the repository as it may contain
1602 # we can't fully delete the repository as it may contain
1591 # local-only history
1603 # local-only history
1592 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1604 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1593 self._gitcommand(['config', 'core.bare', 'true'])
1605 self._gitcommand(['config', 'core.bare', 'true'])
1594 for f in os.listdir(self._abspath):
1606 for f in os.listdir(self._abspath):
1595 if f == '.git':
1607 if f == '.git':
1596 continue
1608 continue
1597 path = os.path.join(self._abspath, f)
1609 path = os.path.join(self._abspath, f)
1598 if os.path.isdir(path) and not os.path.islink(path):
1610 if os.path.isdir(path) and not os.path.islink(path):
1599 shutil.rmtree(path)
1611 shutil.rmtree(path)
1600 else:
1612 else:
1601 os.remove(path)
1613 os.remove(path)
1602
1614
1603 def archive(self, archiver, prefix, match=None):
1615 def archive(self, archiver, prefix, match=None):
1604 total = 0
1616 total = 0
1605 source, revision = self._state
1617 source, revision = self._state
1606 if not revision:
1618 if not revision:
1607 return total
1619 return total
1608 self._fetch(source, revision)
1620 self._fetch(source, revision)
1609
1621
1610 # Parse git's native archive command.
1622 # Parse git's native archive command.
1611 # This should be much faster than manually traversing the trees
1623 # This should be much faster than manually traversing the trees
1612 # and objects with many subprocess calls.
1624 # and objects with many subprocess calls.
1613 tarstream = self._gitcommand(['archive', revision], stream=True)
1625 tarstream = self._gitcommand(['archive', revision], stream=True)
1614 tar = tarfile.open(fileobj=tarstream, mode='r|')
1626 tar = tarfile.open(fileobj=tarstream, mode='r|')
1615 relpath = subrelpath(self)
1627 relpath = subrelpath(self)
1616 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1628 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1617 for i, info in enumerate(tar):
1629 for i, info in enumerate(tar):
1618 if info.isdir():
1630 if info.isdir():
1619 continue
1631 continue
1620 if match and not match(info.name):
1632 if match and not match(info.name):
1621 continue
1633 continue
1622 if info.issym():
1634 if info.issym():
1623 data = info.linkname
1635 data = info.linkname
1624 else:
1636 else:
1625 data = tar.extractfile(info).read()
1637 data = tar.extractfile(info).read()
1626 archiver.addfile(os.path.join(prefix, self._path, info.name),
1638 archiver.addfile(os.path.join(prefix, self._path, info.name),
1627 info.mode, info.issym(), data)
1639 info.mode, info.issym(), data)
1628 total += 1
1640 total += 1
1629 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1641 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1630 unit=_('files'))
1642 unit=_('files'))
1631 self.ui.progress(_('archiving (%s)') % relpath, None)
1643 self.ui.progress(_('archiving (%s)') % relpath, None)
1632 return total
1644 return total
1633
1645
1634
1646
1635 @annotatesubrepoerror
1647 @annotatesubrepoerror
1636 def cat(self, match, prefix, **opts):
1648 def cat(self, match, prefix, **opts):
1637 rev = self._state[1]
1649 rev = self._state[1]
1638 if match.anypats():
1650 if match.anypats():
1639 return 1 #No support for include/exclude yet
1651 return 1 #No support for include/exclude yet
1640
1652
1641 if not match.files():
1653 if not match.files():
1642 return 1
1654 return 1
1643
1655
1644 for f in match.files():
1656 for f in match.files():
1645 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1657 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1646 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1658 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1647 self._ctx.node(),
1659 self._ctx.node(),
1648 pathname=os.path.join(prefix, f))
1660 pathname=os.path.join(prefix, f))
1649 fp.write(output)
1661 fp.write(output)
1650 fp.close()
1662 fp.close()
1651 return 0
1663 return 0
1652
1664
1653
1665
1654 @annotatesubrepoerror
1666 @annotatesubrepoerror
1655 def status(self, rev2, **opts):
1667 def status(self, rev2, **opts):
1656 rev1 = self._state[1]
1668 rev1 = self._state[1]
1657 if self._gitmissing() or not rev1:
1669 if self._gitmissing() or not rev1:
1658 # if the repo is missing, return no results
1670 # if the repo is missing, return no results
1659 return scmutil.status([], [], [], [], [], [], [])
1671 return scmutil.status([], [], [], [], [], [], [])
1660 modified, added, removed = [], [], []
1672 modified, added, removed = [], [], []
1661 self._gitupdatestat()
1673 self._gitupdatestat()
1662 if rev2:
1674 if rev2:
1663 command = ['diff-tree', rev1, rev2]
1675 command = ['diff-tree', rev1, rev2]
1664 else:
1676 else:
1665 command = ['diff-index', rev1]
1677 command = ['diff-index', rev1]
1666 out = self._gitcommand(command)
1678 out = self._gitcommand(command)
1667 for line in out.split('\n'):
1679 for line in out.split('\n'):
1668 tab = line.find('\t')
1680 tab = line.find('\t')
1669 if tab == -1:
1681 if tab == -1:
1670 continue
1682 continue
1671 status, f = line[tab - 1], line[tab + 1:]
1683 status, f = line[tab - 1], line[tab + 1:]
1672 if status == 'M':
1684 if status == 'M':
1673 modified.append(f)
1685 modified.append(f)
1674 elif status == 'A':
1686 elif status == 'A':
1675 added.append(f)
1687 added.append(f)
1676 elif status == 'D':
1688 elif status == 'D':
1677 removed.append(f)
1689 removed.append(f)
1678
1690
1679 deleted, unknown, ignored, clean = [], [], [], []
1691 deleted, unknown, ignored, clean = [], [], [], []
1680
1692
1681 command = ['status', '--porcelain', '-z']
1693 command = ['status', '--porcelain', '-z']
1682 if opts.get('unknown'):
1694 if opts.get('unknown'):
1683 command += ['--untracked-files=all']
1695 command += ['--untracked-files=all']
1684 if opts.get('ignored'):
1696 if opts.get('ignored'):
1685 command += ['--ignored']
1697 command += ['--ignored']
1686 out = self._gitcommand(command)
1698 out = self._gitcommand(command)
1687
1699
1688 changedfiles = set()
1700 changedfiles = set()
1689 changedfiles.update(modified)
1701 changedfiles.update(modified)
1690 changedfiles.update(added)
1702 changedfiles.update(added)
1691 changedfiles.update(removed)
1703 changedfiles.update(removed)
1692 for line in out.split('\0'):
1704 for line in out.split('\0'):
1693 if not line:
1705 if not line:
1694 continue
1706 continue
1695 st = line[0:2]
1707 st = line[0:2]
1696 #moves and copies show 2 files on one line
1708 #moves and copies show 2 files on one line
1697 if line.find('\0') >= 0:
1709 if line.find('\0') >= 0:
1698 filename1, filename2 = line[3:].split('\0')
1710 filename1, filename2 = line[3:].split('\0')
1699 else:
1711 else:
1700 filename1 = line[3:]
1712 filename1 = line[3:]
1701 filename2 = None
1713 filename2 = None
1702
1714
1703 changedfiles.add(filename1)
1715 changedfiles.add(filename1)
1704 if filename2:
1716 if filename2:
1705 changedfiles.add(filename2)
1717 changedfiles.add(filename2)
1706
1718
1707 if st == '??':
1719 if st == '??':
1708 unknown.append(filename1)
1720 unknown.append(filename1)
1709 elif st == '!!':
1721 elif st == '!!':
1710 ignored.append(filename1)
1722 ignored.append(filename1)
1711
1723
1712 if opts.get('clean'):
1724 if opts.get('clean'):
1713 out = self._gitcommand(['ls-files'])
1725 out = self._gitcommand(['ls-files'])
1714 for f in out.split('\n'):
1726 for f in out.split('\n'):
1715 if not f in changedfiles:
1727 if not f in changedfiles:
1716 clean.append(f)
1728 clean.append(f)
1717
1729
1718 return scmutil.status(modified, added, removed, deleted,
1730 return scmutil.status(modified, added, removed, deleted,
1719 unknown, ignored, clean)
1731 unknown, ignored, clean)
1720
1732
1721 @annotatesubrepoerror
1733 @annotatesubrepoerror
1722 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1734 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1723 node1 = self._state[1]
1735 node1 = self._state[1]
1724 cmd = ['diff']
1736 cmd = ['diff']
1725 if opts['stat']:
1737 if opts['stat']:
1726 cmd.append('--stat')
1738 cmd.append('--stat')
1727 else:
1739 else:
1728 # for Git, this also implies '-p'
1740 # for Git, this also implies '-p'
1729 cmd.append('-U%d' % diffopts.context)
1741 cmd.append('-U%d' % diffopts.context)
1730
1742
1731 gitprefix = os.path.join(prefix, self._path)
1743 gitprefix = os.path.join(prefix, self._path)
1732
1744
1733 if diffopts.noprefix:
1745 if diffopts.noprefix:
1734 cmd.extend(['--src-prefix=%s/' % gitprefix,
1746 cmd.extend(['--src-prefix=%s/' % gitprefix,
1735 '--dst-prefix=%s/' % gitprefix])
1747 '--dst-prefix=%s/' % gitprefix])
1736 else:
1748 else:
1737 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1749 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1738 '--dst-prefix=b/%s/' % gitprefix])
1750 '--dst-prefix=b/%s/' % gitprefix])
1739
1751
1740 if diffopts.ignorews:
1752 if diffopts.ignorews:
1741 cmd.append('--ignore-all-space')
1753 cmd.append('--ignore-all-space')
1742 if diffopts.ignorewsamount:
1754 if diffopts.ignorewsamount:
1743 cmd.append('--ignore-space-change')
1755 cmd.append('--ignore-space-change')
1744 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1756 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1745 and diffopts.ignoreblanklines:
1757 and diffopts.ignoreblanklines:
1746 cmd.append('--ignore-blank-lines')
1758 cmd.append('--ignore-blank-lines')
1747
1759
1748 cmd.append(node1)
1760 cmd.append(node1)
1749 if node2:
1761 if node2:
1750 cmd.append(node2)
1762 cmd.append(node2)
1751
1763
1752 if match.anypats():
1764 if match.anypats():
1753 return #No support for include/exclude yet
1765 return #No support for include/exclude yet
1754
1766
1755 output = ""
1767 output = ""
1756 if match.always():
1768 if match.always():
1757 output += self._gitcommand(cmd) + '\n'
1769 output += self._gitcommand(cmd) + '\n'
1758 elif match.files():
1770 elif match.files():
1759 for f in match.files():
1771 for f in match.files():
1760 output += self._gitcommand(cmd + [f]) + '\n'
1772 output += self._gitcommand(cmd + [f]) + '\n'
1761 elif match(gitprefix): #Subrepo is matched
1773 elif match(gitprefix): #Subrepo is matched
1762 output += self._gitcommand(cmd) + '\n'
1774 output += self._gitcommand(cmd) + '\n'
1763
1775
1764 if output.strip():
1776 if output.strip():
1765 ui.write(output)
1777 ui.write(output)
1766
1778
1767 @annotatesubrepoerror
1779 @annotatesubrepoerror
1768 def revert(self, substate, *pats, **opts):
1780 def revert(self, substate, *pats, **opts):
1769 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1781 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1770 if not opts.get('no_backup'):
1782 if not opts.get('no_backup'):
1771 status = self.status(None)
1783 status = self.status(None)
1772 names = status.modified
1784 names = status.modified
1773 for name in names:
1785 for name in names:
1774 bakname = "%s.orig" % name
1786 bakname = "%s.orig" % name
1775 self.ui.note(_('saving current version of %s as %s\n') %
1787 self.ui.note(_('saving current version of %s as %s\n') %
1776 (name, bakname))
1788 (name, bakname))
1777 util.rename(os.path.join(self._abspath, name),
1789 util.rename(os.path.join(self._abspath, name),
1778 os.path.join(self._abspath, bakname))
1790 os.path.join(self._abspath, bakname))
1779
1791
1780 if not opts.get('dry_run'):
1792 if not opts.get('dry_run'):
1781 self.get(substate, overwrite=True)
1793 self.get(substate, overwrite=True)
1782 return []
1794 return []
1783
1795
1784 def shortid(self, revid):
1796 def shortid(self, revid):
1785 return revid[:7]
1797 return revid[:7]
1786
1798
1787 types = {
1799 types = {
1788 'hg': hgsubrepo,
1800 'hg': hgsubrepo,
1789 'svn': svnsubrepo,
1801 'svn': svnsubrepo,
1790 'git': gitsubrepo,
1802 'git': gitsubrepo,
1791 }
1803 }
@@ -1,1074 +1,1074 b''
1 This file contains testcases that tend to be related to special cases or less
1 This file contains testcases that tend to be related to special cases or less
2 common commands affecting largefile.
2 common commands affecting largefile.
3
3
4 Each sections should be independent of each others.
4 Each sections should be independent of each others.
5
5
6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 $ mkdir "${USERCACHE}"
7 $ mkdir "${USERCACHE}"
8 $ cat >> $HGRCPATH <<EOF
8 $ cat >> $HGRCPATH <<EOF
9 > [extensions]
9 > [extensions]
10 > largefiles=
10 > largefiles=
11 > purge=
11 > purge=
12 > rebase=
12 > rebase=
13 > transplant=
13 > transplant=
14 > [phases]
14 > [phases]
15 > publish=False
15 > publish=False
16 > [largefiles]
16 > [largefiles]
17 > minsize=2
17 > minsize=2
18 > patterns=glob:**.dat
18 > patterns=glob:**.dat
19 > usercache=${USERCACHE}
19 > usercache=${USERCACHE}
20 > [hooks]
20 > [hooks]
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 > EOF
22 > EOF
23
23
24
24
25
25
26 Test copies and moves from a directory other than root (issue3516)
26 Test copies and moves from a directory other than root (issue3516)
27 =========================================================================
27 =========================================================================
28
28
29 $ hg init lf_cpmv
29 $ hg init lf_cpmv
30 $ cd lf_cpmv
30 $ cd lf_cpmv
31 $ mkdir dira
31 $ mkdir dira
32 $ mkdir dira/dirb
32 $ mkdir dira/dirb
33 $ touch dira/dirb/largefile
33 $ touch dira/dirb/largefile
34 $ hg add --large dira/dirb/largefile
34 $ hg add --large dira/dirb/largefile
35 $ hg commit -m "added"
35 $ hg commit -m "added"
36 Invoking status precommit hook
36 Invoking status precommit hook
37 A dira/dirb/largefile
37 A dira/dirb/largefile
38 $ cd dira
38 $ cd dira
39 $ hg cp dirb/largefile foo/largefile
39 $ hg cp dirb/largefile foo/largefile
40 $ hg ci -m "deep copy"
40 $ hg ci -m "deep copy"
41 Invoking status precommit hook
41 Invoking status precommit hook
42 A dira/foo/largefile
42 A dira/foo/largefile
43 $ find . | sort
43 $ find . | sort
44 .
44 .
45 ./dirb
45 ./dirb
46 ./dirb/largefile
46 ./dirb/largefile
47 ./foo
47 ./foo
48 ./foo/largefile
48 ./foo/largefile
49 $ hg mv foo/largefile baz/largefile
49 $ hg mv foo/largefile baz/largefile
50 $ hg ci -m "moved"
50 $ hg ci -m "moved"
51 Invoking status precommit hook
51 Invoking status precommit hook
52 A dira/baz/largefile
52 A dira/baz/largefile
53 R dira/foo/largefile
53 R dira/foo/largefile
54 $ find . | sort
54 $ find . | sort
55 .
55 .
56 ./baz
56 ./baz
57 ./baz/largefile
57 ./baz/largefile
58 ./dirb
58 ./dirb
59 ./dirb/largefile
59 ./dirb/largefile
60 $ cd ..
60 $ cd ..
61 $ hg mv dira dirc
61 $ hg mv dira dirc
62 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
62 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
63 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
63 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
64 $ find * | sort
64 $ find * | sort
65 dirc
65 dirc
66 dirc/baz
66 dirc/baz
67 dirc/baz/largefile
67 dirc/baz/largefile
68 dirc/dirb
68 dirc/dirb
69 dirc/dirb/largefile
69 dirc/dirb/largefile
70
70
71 $ hg clone -q . ../fetch
71 $ hg clone -q . ../fetch
72 $ hg --config extensions.fetch= fetch ../fetch
72 $ hg --config extensions.fetch= fetch ../fetch
73 abort: uncommitted changes
73 abort: uncommitted changes
74 [255]
74 [255]
75 $ hg up -qC
75 $ hg up -qC
76 $ cd ..
76 $ cd ..
77
77
78 Clone a local repository owned by another user
78 Clone a local repository owned by another user
79 ===================================================
79 ===================================================
80
80
81 #if unix-permissions
81 #if unix-permissions
82
82
83 We have to simulate that here by setting $HOME and removing write permissions
83 We have to simulate that here by setting $HOME and removing write permissions
84 $ ORIGHOME="$HOME"
84 $ ORIGHOME="$HOME"
85 $ mkdir alice
85 $ mkdir alice
86 $ HOME="`pwd`/alice"
86 $ HOME="`pwd`/alice"
87 $ cd alice
87 $ cd alice
88 $ hg init pubrepo
88 $ hg init pubrepo
89 $ cd pubrepo
89 $ cd pubrepo
90 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
90 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
91 $ hg add --large a-large-file
91 $ hg add --large a-large-file
92 $ hg commit -m "Add a large file"
92 $ hg commit -m "Add a large file"
93 Invoking status precommit hook
93 Invoking status precommit hook
94 A a-large-file
94 A a-large-file
95 $ cd ..
95 $ cd ..
96 $ chmod -R a-w pubrepo
96 $ chmod -R a-w pubrepo
97 $ cd ..
97 $ cd ..
98 $ mkdir bob
98 $ mkdir bob
99 $ HOME="`pwd`/bob"
99 $ HOME="`pwd`/bob"
100 $ cd bob
100 $ cd bob
101 $ hg clone --pull ../alice/pubrepo pubrepo
101 $ hg clone --pull ../alice/pubrepo pubrepo
102 requesting all changes
102 requesting all changes
103 adding changesets
103 adding changesets
104 adding manifests
104 adding manifests
105 adding file changes
105 adding file changes
106 added 1 changesets with 1 changes to 1 files
106 added 1 changesets with 1 changes to 1 files
107 updating to branch default
107 updating to branch default
108 getting changed largefiles
108 getting changed largefiles
109 1 largefiles updated, 0 removed
109 1 largefiles updated, 0 removed
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ cd ..
111 $ cd ..
112 $ chmod -R u+w alice/pubrepo
112 $ chmod -R u+w alice/pubrepo
113 $ HOME="$ORIGHOME"
113 $ HOME="$ORIGHOME"
114
114
115 #endif
115 #endif
116
116
117
117
118 Symlink to a large largefile should behave the same as a symlink to a normal file
118 Symlink to a large largefile should behave the same as a symlink to a normal file
119 =====================================================================================
119 =====================================================================================
120
120
121 #if symlink
121 #if symlink
122
122
123 $ hg init largesymlink
123 $ hg init largesymlink
124 $ cd largesymlink
124 $ cd largesymlink
125 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
125 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
126 $ hg add --large largefile
126 $ hg add --large largefile
127 $ hg commit -m "commit a large file"
127 $ hg commit -m "commit a large file"
128 Invoking status precommit hook
128 Invoking status precommit hook
129 A largefile
129 A largefile
130 $ ln -s largefile largelink
130 $ ln -s largefile largelink
131 $ hg add largelink
131 $ hg add largelink
132 $ hg commit -m "commit a large symlink"
132 $ hg commit -m "commit a large symlink"
133 Invoking status precommit hook
133 Invoking status precommit hook
134 A largelink
134 A largelink
135 $ rm -f largelink
135 $ rm -f largelink
136 $ hg up >/dev/null
136 $ hg up >/dev/null
137 $ test -f largelink
137 $ test -f largelink
138 [1]
138 [1]
139 $ test -L largelink
139 $ test -L largelink
140 [1]
140 [1]
141 $ rm -f largelink # make next part of the test independent of the previous
141 $ rm -f largelink # make next part of the test independent of the previous
142 $ hg up -C >/dev/null
142 $ hg up -C >/dev/null
143 $ test -f largelink
143 $ test -f largelink
144 $ test -L largelink
144 $ test -L largelink
145 $ cd ..
145 $ cd ..
146
146
147 #endif
147 #endif
148
148
149
149
150 test for pattern matching on 'hg status':
150 test for pattern matching on 'hg status':
151 ==============================================
151 ==============================================
152
152
153
153
154 to boost performance, largefiles checks whether specified patterns are
154 to boost performance, largefiles checks whether specified patterns are
155 related to largefiles in working directory (NOT to STANDIN) or not.
155 related to largefiles in working directory (NOT to STANDIN) or not.
156
156
157 $ hg init statusmatch
157 $ hg init statusmatch
158 $ cd statusmatch
158 $ cd statusmatch
159
159
160 $ mkdir -p a/b/c/d
160 $ mkdir -p a/b/c/d
161 $ echo normal > a/b/c/d/e.normal.txt
161 $ echo normal > a/b/c/d/e.normal.txt
162 $ hg add a/b/c/d/e.normal.txt
162 $ hg add a/b/c/d/e.normal.txt
163 $ echo large > a/b/c/d/e.large.txt
163 $ echo large > a/b/c/d/e.large.txt
164 $ hg add --large a/b/c/d/e.large.txt
164 $ hg add --large a/b/c/d/e.large.txt
165 $ mkdir -p a/b/c/x
165 $ mkdir -p a/b/c/x
166 $ echo normal > a/b/c/x/y.normal.txt
166 $ echo normal > a/b/c/x/y.normal.txt
167 $ hg add a/b/c/x/y.normal.txt
167 $ hg add a/b/c/x/y.normal.txt
168 $ hg commit -m 'add files'
168 $ hg commit -m 'add files'
169 Invoking status precommit hook
169 Invoking status precommit hook
170 A a/b/c/d/e.large.txt
170 A a/b/c/d/e.large.txt
171 A a/b/c/d/e.normal.txt
171 A a/b/c/d/e.normal.txt
172 A a/b/c/x/y.normal.txt
172 A a/b/c/x/y.normal.txt
173
173
174 (1) no pattern: no performance boost
174 (1) no pattern: no performance boost
175 $ hg status -A
175 $ hg status -A
176 C a/b/c/d/e.large.txt
176 C a/b/c/d/e.large.txt
177 C a/b/c/d/e.normal.txt
177 C a/b/c/d/e.normal.txt
178 C a/b/c/x/y.normal.txt
178 C a/b/c/x/y.normal.txt
179
179
180 (2) pattern not related to largefiles: performance boost
180 (2) pattern not related to largefiles: performance boost
181 $ hg status -A a/b/c/x
181 $ hg status -A a/b/c/x
182 C a/b/c/x/y.normal.txt
182 C a/b/c/x/y.normal.txt
183
183
184 (3) pattern related to largefiles: no performance boost
184 (3) pattern related to largefiles: no performance boost
185 $ hg status -A a/b/c/d
185 $ hg status -A a/b/c/d
186 C a/b/c/d/e.large.txt
186 C a/b/c/d/e.large.txt
187 C a/b/c/d/e.normal.txt
187 C a/b/c/d/e.normal.txt
188
188
189 (4) pattern related to STANDIN (not to largefiles): performance boost
189 (4) pattern related to STANDIN (not to largefiles): performance boost
190 $ hg status -A .hglf/a
190 $ hg status -A .hglf/a
191 C .hglf/a/b/c/d/e.large.txt
191 C .hglf/a/b/c/d/e.large.txt
192
192
193 (5) mixed case: no performance boost
193 (5) mixed case: no performance boost
194 $ hg status -A a/b/c/x a/b/c/d
194 $ hg status -A a/b/c/x a/b/c/d
195 C a/b/c/d/e.large.txt
195 C a/b/c/d/e.large.txt
196 C a/b/c/d/e.normal.txt
196 C a/b/c/d/e.normal.txt
197 C a/b/c/x/y.normal.txt
197 C a/b/c/x/y.normal.txt
198
198
199 verify that largefiles doesn't break filesets
199 verify that largefiles doesn't break filesets
200
200
201 $ hg log --rev . --exclude "set:binary()"
201 $ hg log --rev . --exclude "set:binary()"
202 changeset: 0:41bd42f10efa
202 changeset: 0:41bd42f10efa
203 tag: tip
203 tag: tip
204 user: test
204 user: test
205 date: Thu Jan 01 00:00:00 1970 +0000
205 date: Thu Jan 01 00:00:00 1970 +0000
206 summary: add files
206 summary: add files
207
207
208 verify that large files in subrepos handled properly
208 verify that large files in subrepos handled properly
209 $ hg init subrepo
209 $ hg init subrepo
210 $ echo "subrepo = subrepo" > .hgsub
210 $ echo "subrepo = subrepo" > .hgsub
211 $ hg add .hgsub
211 $ hg add .hgsub
212 $ hg ci -m "add subrepo"
212 $ hg ci -m "add subrepo"
213 Invoking status precommit hook
213 Invoking status precommit hook
214 A .hgsub
214 A .hgsub
215 ? .hgsubstate
215 ? .hgsubstate
216 $ echo "rev 1" > subrepo/large.txt
216 $ echo "rev 1" > subrepo/large.txt
217 $ hg add --large subrepo/large.txt
217 $ hg add --large subrepo/large.txt
218 $ hg sum
218 $ hg sum
219 parent: 1:8ee150ea2e9c tip
219 parent: 1:8ee150ea2e9c tip
220 add subrepo
220 add subrepo
221 branch: default
221 branch: default
222 commit: 1 subrepos
222 commit: 1 subrepos
223 update: (current)
223 update: (current)
224 $ hg st
224 $ hg st
225 $ hg st -S
225 $ hg st -S
226 A subrepo/large.txt
226 A subrepo/large.txt
227 $ hg ci -S -m "commit top repo"
227 $ hg ci -S -m "commit top repo"
228 committing subrepository subrepo
228 committing subrepository subrepo
229 Invoking status precommit hook
229 Invoking status precommit hook
230 A large.txt
230 A large.txt
231 Invoking status precommit hook
231 Invoking status precommit hook
232 M .hgsubstate
232 M .hgsubstate
233 # No differences
233 # No differences
234 $ hg st -S
234 $ hg st -S
235 $ hg sum
235 $ hg sum
236 parent: 2:ce4cd0c527a6 tip
236 parent: 2:ce4cd0c527a6 tip
237 commit top repo
237 commit top repo
238 branch: default
238 branch: default
239 commit: (clean)
239 commit: (clean)
240 update: (current)
240 update: (current)
241 $ echo "rev 2" > subrepo/large.txt
241 $ echo "rev 2" > subrepo/large.txt
242 $ hg st -S
242 $ hg st -S
243 M subrepo/large.txt
243 M subrepo/large.txt
244 $ hg sum
244 $ hg sum
245 parent: 2:ce4cd0c527a6 tip
245 parent: 2:ce4cd0c527a6 tip
246 commit top repo
246 commit top repo
247 branch: default
247 branch: default
248 commit: 1 subrepos
248 commit: 1 subrepos
249 update: (current)
249 update: (current)
250 $ hg ci -m "this commit should fail without -S"
250 $ hg ci -m "this commit should fail without -S"
251 abort: uncommitted changes in subrepo subrepo
251 abort: uncommitted changes in subrepository 'subrepo'
252 (use --subrepos for recursive commit)
252 (use --subrepos for recursive commit)
253 [255]
253 [255]
254
254
255 Add a normal file to the subrepo, then test archiving
255 Add a normal file to the subrepo, then test archiving
256
256
257 $ echo 'normal file' > subrepo/normal.txt
257 $ echo 'normal file' > subrepo/normal.txt
258 $ touch large.dat
258 $ touch large.dat
259 $ mv subrepo/large.txt subrepo/renamed-large.txt
259 $ mv subrepo/large.txt subrepo/renamed-large.txt
260 $ hg addremove -S --dry-run
260 $ hg addremove -S --dry-run
261 adding large.dat as a largefile
261 adding large.dat as a largefile
262 removing subrepo/large.txt
262 removing subrepo/large.txt
263 adding subrepo/normal.txt
263 adding subrepo/normal.txt
264 adding subrepo/renamed-large.txt
264 adding subrepo/renamed-large.txt
265 $ hg status -S
265 $ hg status -S
266 ! subrepo/large.txt
266 ! subrepo/large.txt
267 ? large.dat
267 ? large.dat
268 ? subrepo/normal.txt
268 ? subrepo/normal.txt
269 ? subrepo/renamed-large.txt
269 ? subrepo/renamed-large.txt
270
270
271 $ hg addremove --dry-run subrepo
271 $ hg addremove --dry-run subrepo
272 removing subrepo/large.txt (glob)
272 removing subrepo/large.txt (glob)
273 adding subrepo/normal.txt (glob)
273 adding subrepo/normal.txt (glob)
274 adding subrepo/renamed-large.txt (glob)
274 adding subrepo/renamed-large.txt (glob)
275 $ hg status -S
275 $ hg status -S
276 ! subrepo/large.txt
276 ! subrepo/large.txt
277 ? large.dat
277 ? large.dat
278 ? subrepo/normal.txt
278 ? subrepo/normal.txt
279 ? subrepo/renamed-large.txt
279 ? subrepo/renamed-large.txt
280 $ cd ..
280 $ cd ..
281
281
282 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
282 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
283 removing statusmatch/subrepo/large.txt (glob)
283 removing statusmatch/subrepo/large.txt (glob)
284 adding statusmatch/subrepo/normal.txt (glob)
284 adding statusmatch/subrepo/normal.txt (glob)
285 adding statusmatch/subrepo/renamed-large.txt (glob)
285 adding statusmatch/subrepo/renamed-large.txt (glob)
286 $ hg -R statusmatch status -S
286 $ hg -R statusmatch status -S
287 ! subrepo/large.txt
287 ! subrepo/large.txt
288 ? large.dat
288 ? large.dat
289 ? subrepo/normal.txt
289 ? subrepo/normal.txt
290 ? subrepo/renamed-large.txt
290 ? subrepo/renamed-large.txt
291
291
292 $ hg -R statusmatch addremove --dry-run -S
292 $ hg -R statusmatch addremove --dry-run -S
293 adding large.dat as a largefile
293 adding large.dat as a largefile
294 removing subrepo/large.txt
294 removing subrepo/large.txt
295 adding subrepo/normal.txt
295 adding subrepo/normal.txt
296 adding subrepo/renamed-large.txt
296 adding subrepo/renamed-large.txt
297 $ cd statusmatch
297 $ cd statusmatch
298
298
299 $ mv subrepo/renamed-large.txt subrepo/large.txt
299 $ mv subrepo/renamed-large.txt subrepo/large.txt
300 $ hg addremove subrepo
300 $ hg addremove subrepo
301 adding subrepo/normal.txt (glob)
301 adding subrepo/normal.txt (glob)
302 $ hg forget subrepo/normal.txt
302 $ hg forget subrepo/normal.txt
303
303
304 $ hg addremove -S
304 $ hg addremove -S
305 adding large.dat as a largefile
305 adding large.dat as a largefile
306 adding subrepo/normal.txt
306 adding subrepo/normal.txt
307 $ rm large.dat
307 $ rm large.dat
308
308
309 $ hg addremove subrepo
309 $ hg addremove subrepo
310 $ hg addremove -S
310 $ hg addremove -S
311 removing large.dat
311 removing large.dat
312
312
313 Lock in subrepo, otherwise the change isn't archived
313 Lock in subrepo, otherwise the change isn't archived
314
314
315 $ hg ci -S -m "add normal file to top level"
315 $ hg ci -S -m "add normal file to top level"
316 committing subrepository subrepo
316 committing subrepository subrepo
317 Invoking status precommit hook
317 Invoking status precommit hook
318 M large.txt
318 M large.txt
319 A normal.txt
319 A normal.txt
320 Invoking status precommit hook
320 Invoking status precommit hook
321 M .hgsubstate
321 M .hgsubstate
322 $ hg archive -S ../lf_subrepo_archive
322 $ hg archive -S ../lf_subrepo_archive
323 $ find ../lf_subrepo_archive | sort
323 $ find ../lf_subrepo_archive | sort
324 ../lf_subrepo_archive
324 ../lf_subrepo_archive
325 ../lf_subrepo_archive/.hg_archival.txt
325 ../lf_subrepo_archive/.hg_archival.txt
326 ../lf_subrepo_archive/.hgsub
326 ../lf_subrepo_archive/.hgsub
327 ../lf_subrepo_archive/.hgsubstate
327 ../lf_subrepo_archive/.hgsubstate
328 ../lf_subrepo_archive/a
328 ../lf_subrepo_archive/a
329 ../lf_subrepo_archive/a/b
329 ../lf_subrepo_archive/a/b
330 ../lf_subrepo_archive/a/b/c
330 ../lf_subrepo_archive/a/b/c
331 ../lf_subrepo_archive/a/b/c/d
331 ../lf_subrepo_archive/a/b/c/d
332 ../lf_subrepo_archive/a/b/c/d/e.large.txt
332 ../lf_subrepo_archive/a/b/c/d/e.large.txt
333 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
333 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
334 ../lf_subrepo_archive/a/b/c/x
334 ../lf_subrepo_archive/a/b/c/x
335 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
335 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
336 ../lf_subrepo_archive/subrepo
336 ../lf_subrepo_archive/subrepo
337 ../lf_subrepo_archive/subrepo/large.txt
337 ../lf_subrepo_archive/subrepo/large.txt
338 ../lf_subrepo_archive/subrepo/normal.txt
338 ../lf_subrepo_archive/subrepo/normal.txt
339
339
340 Test update with subrepos.
340 Test update with subrepos.
341
341
342 $ hg update 0
342 $ hg update 0
343 getting changed largefiles
343 getting changed largefiles
344 0 largefiles updated, 1 removed
344 0 largefiles updated, 1 removed
345 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
345 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
346 $ hg status -S
346 $ hg status -S
347 $ hg update tip
347 $ hg update tip
348 getting changed largefiles
348 getting changed largefiles
349 1 largefiles updated, 0 removed
349 1 largefiles updated, 0 removed
350 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 $ hg status -S
351 $ hg status -S
352 # modify a large file
352 # modify a large file
353 $ echo "modified" > subrepo/large.txt
353 $ echo "modified" > subrepo/large.txt
354 $ hg st -S
354 $ hg st -S
355 M subrepo/large.txt
355 M subrepo/large.txt
356 # update -C should revert the change.
356 # update -C should revert the change.
357 $ hg update -C
357 $ hg update -C
358 getting changed largefiles
358 getting changed largefiles
359 1 largefiles updated, 0 removed
359 1 largefiles updated, 0 removed
360 getting changed largefiles
360 getting changed largefiles
361 0 largefiles updated, 0 removed
361 0 largefiles updated, 0 removed
362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 $ hg status -S
363 $ hg status -S
364
364
365 $ hg forget -v subrepo/large.txt
365 $ hg forget -v subrepo/large.txt
366 removing subrepo/large.txt (glob)
366 removing subrepo/large.txt (glob)
367
367
368 Test reverting a forgotten file
368 Test reverting a forgotten file
369 $ hg revert -R subrepo subrepo/large.txt
369 $ hg revert -R subrepo subrepo/large.txt
370 $ hg status -SA subrepo/large.txt
370 $ hg status -SA subrepo/large.txt
371 C subrepo/large.txt
371 C subrepo/large.txt
372
372
373 $ hg rm -v subrepo/large.txt
373 $ hg rm -v subrepo/large.txt
374 removing subrepo/large.txt (glob)
374 removing subrepo/large.txt (glob)
375 $ hg revert -R subrepo subrepo/large.txt
375 $ hg revert -R subrepo subrepo/large.txt
376 $ rm subrepo/large.txt
376 $ rm subrepo/large.txt
377 $ hg addremove -S
377 $ hg addremove -S
378 removing subrepo/large.txt
378 removing subrepo/large.txt
379 $ hg st -S
379 $ hg st -S
380 R subrepo/large.txt
380 R subrepo/large.txt
381
381
382 Test archiving a revision that references a subrepo that is not yet
382 Test archiving a revision that references a subrepo that is not yet
383 cloned (see test-subrepo-recursion.t):
383 cloned (see test-subrepo-recursion.t):
384
384
385 $ hg clone -U . ../empty
385 $ hg clone -U . ../empty
386 $ cd ../empty
386 $ cd ../empty
387 $ hg archive --subrepos -r tip ../archive.tar.gz
387 $ hg archive --subrepos -r tip ../archive.tar.gz
388 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
388 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
389 $ cd ..
389 $ cd ..
390
390
391
391
392
392
393
393
394
394
395
395
396 Test addremove, forget and others
396 Test addremove, forget and others
397 ==============================================
397 ==============================================
398
398
399 Test that addremove picks up largefiles prior to the initial commit (issue3541)
399 Test that addremove picks up largefiles prior to the initial commit (issue3541)
400
400
401 $ hg init addrm2
401 $ hg init addrm2
402 $ cd addrm2
402 $ cd addrm2
403 $ touch large.dat
403 $ touch large.dat
404 $ touch large2.dat
404 $ touch large2.dat
405 $ touch normal
405 $ touch normal
406 $ hg add --large large.dat
406 $ hg add --large large.dat
407 $ hg addremove -v
407 $ hg addremove -v
408 adding large2.dat as a largefile
408 adding large2.dat as a largefile
409 adding normal
409 adding normal
410
410
411 Test that forgetting all largefiles reverts to islfilesrepo() == False
411 Test that forgetting all largefiles reverts to islfilesrepo() == False
412 (addremove will add *.dat as normal files now)
412 (addremove will add *.dat as normal files now)
413 $ hg forget large.dat
413 $ hg forget large.dat
414 $ hg forget large2.dat
414 $ hg forget large2.dat
415 $ hg addremove -v
415 $ hg addremove -v
416 adding large.dat
416 adding large.dat
417 adding large2.dat
417 adding large2.dat
418
418
419 Test commit's addremove option prior to the first commit
419 Test commit's addremove option prior to the first commit
420 $ hg forget large.dat
420 $ hg forget large.dat
421 $ hg forget large2.dat
421 $ hg forget large2.dat
422 $ hg add --large large.dat
422 $ hg add --large large.dat
423 $ hg ci -Am "commit"
423 $ hg ci -Am "commit"
424 adding large2.dat as a largefile
424 adding large2.dat as a largefile
425 Invoking status precommit hook
425 Invoking status precommit hook
426 A large.dat
426 A large.dat
427 A large2.dat
427 A large2.dat
428 A normal
428 A normal
429 $ find .hglf | sort
429 $ find .hglf | sort
430 .hglf
430 .hglf
431 .hglf/large.dat
431 .hglf/large.dat
432 .hglf/large2.dat
432 .hglf/large2.dat
433
433
434 Test actions on largefiles using relative paths from subdir
434 Test actions on largefiles using relative paths from subdir
435
435
436 $ mkdir sub
436 $ mkdir sub
437 $ cd sub
437 $ cd sub
438 $ echo anotherlarge > anotherlarge
438 $ echo anotherlarge > anotherlarge
439 $ hg add --large anotherlarge
439 $ hg add --large anotherlarge
440 $ hg st
440 $ hg st
441 A sub/anotherlarge
441 A sub/anotherlarge
442 $ hg st anotherlarge
442 $ hg st anotherlarge
443 A anotherlarge
443 A anotherlarge
444 $ hg commit -m anotherlarge anotherlarge
444 $ hg commit -m anotherlarge anotherlarge
445 Invoking status precommit hook
445 Invoking status precommit hook
446 A sub/anotherlarge
446 A sub/anotherlarge
447 $ hg log anotherlarge
447 $ hg log anotherlarge
448 changeset: 1:9627a577c5e9
448 changeset: 1:9627a577c5e9
449 tag: tip
449 tag: tip
450 user: test
450 user: test
451 date: Thu Jan 01 00:00:00 1970 +0000
451 date: Thu Jan 01 00:00:00 1970 +0000
452 summary: anotherlarge
452 summary: anotherlarge
453
453
454 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
454 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
455 updated patterns: ['../.hglf/sub/../sub/anotherlarge', '../sub/anotherlarge']
455 updated patterns: ['../.hglf/sub/../sub/anotherlarge', '../sub/anotherlarge']
456 1: anotherlarge
456 1: anotherlarge
457
457
458 $ hg log -G anotherlarge
458 $ hg log -G anotherlarge
459 @ changeset: 1:9627a577c5e9
459 @ changeset: 1:9627a577c5e9
460 | tag: tip
460 | tag: tip
461 | user: test
461 | user: test
462 | date: Thu Jan 01 00:00:00 1970 +0000
462 | date: Thu Jan 01 00:00:00 1970 +0000
463 | summary: anotherlarge
463 | summary: anotherlarge
464 |
464 |
465
465
466 $ hg log glob:another*
466 $ hg log glob:another*
467 changeset: 1:9627a577c5e9
467 changeset: 1:9627a577c5e9
468 tag: tip
468 tag: tip
469 user: test
469 user: test
470 date: Thu Jan 01 00:00:00 1970 +0000
470 date: Thu Jan 01 00:00:00 1970 +0000
471 summary: anotherlarge
471 summary: anotherlarge
472
472
473 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
473 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
474 updated patterns: ['glob:../.hglf/sub/another*', 'glob:another*']
474 updated patterns: ['glob:../.hglf/sub/another*', 'glob:another*']
475 @ 1: anotherlarge
475 @ 1: anotherlarge
476 |
476 |
477
477
478 #if no-msys
478 #if no-msys
479 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
479 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
480 updated patterns: ['glob:../.hglf/sub/another*']
480 updated patterns: ['glob:../.hglf/sub/another*']
481 1: anotherlarge
481 1: anotherlarge
482
482
483 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
483 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
484 updated patterns: ['glob:../.hglf/sub/another*']
484 updated patterns: ['glob:../.hglf/sub/another*']
485 @ 1: anotherlarge
485 @ 1: anotherlarge
486 |
486 |
487 #endif
487 #endif
488
488
489 $ echo more >> anotherlarge
489 $ echo more >> anotherlarge
490 $ hg st .
490 $ hg st .
491 M anotherlarge
491 M anotherlarge
492 $ hg cat anotherlarge
492 $ hg cat anotherlarge
493 anotherlarge
493 anotherlarge
494 $ hg revert anotherlarge
494 $ hg revert anotherlarge
495 $ hg st
495 $ hg st
496 ? sub/anotherlarge.orig
496 ? sub/anotherlarge.orig
497 $ cd ..
497 $ cd ..
498
498
499 Test glob logging from the root dir
499 Test glob logging from the root dir
500 $ hg log glob:**another*
500 $ hg log glob:**another*
501 changeset: 1:9627a577c5e9
501 changeset: 1:9627a577c5e9
502 tag: tip
502 tag: tip
503 user: test
503 user: test
504 date: Thu Jan 01 00:00:00 1970 +0000
504 date: Thu Jan 01 00:00:00 1970 +0000
505 summary: anotherlarge
505 summary: anotherlarge
506
506
507 $ hg log -G glob:**another*
507 $ hg log -G glob:**another*
508 @ changeset: 1:9627a577c5e9
508 @ changeset: 1:9627a577c5e9
509 | tag: tip
509 | tag: tip
510 | user: test
510 | user: test
511 | date: Thu Jan 01 00:00:00 1970 +0000
511 | date: Thu Jan 01 00:00:00 1970 +0000
512 | summary: anotherlarge
512 | summary: anotherlarge
513 |
513 |
514
514
515 $ cd ..
515 $ cd ..
516
516
517 Log from outer space
517 Log from outer space
518 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
518 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
519 updated patterns: ['addrm2/.hglf/sub/anotherlarge', 'addrm2/sub/anotherlarge']
519 updated patterns: ['addrm2/.hglf/sub/anotherlarge', 'addrm2/sub/anotherlarge']
520 1: anotherlarge
520 1: anotherlarge
521 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
521 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
522 updated patterns: ['addrm2/.hglf/sub/anotherlarge']
522 updated patterns: ['addrm2/.hglf/sub/anotherlarge']
523 1: anotherlarge
523 1: anotherlarge
524
524
525
525
526 Check error message while exchange
526 Check error message while exchange
527 =========================================================
527 =========================================================
528
528
529 issue3651: summary/outgoing with largefiles shows "no remote repo"
529 issue3651: summary/outgoing with largefiles shows "no remote repo"
530 unexpectedly
530 unexpectedly
531
531
532 $ mkdir issue3651
532 $ mkdir issue3651
533 $ cd issue3651
533 $ cd issue3651
534
534
535 $ hg init src
535 $ hg init src
536 $ echo a > src/a
536 $ echo a > src/a
537 $ hg -R src add --large src/a
537 $ hg -R src add --large src/a
538 $ hg -R src commit -m '#0'
538 $ hg -R src commit -m '#0'
539 Invoking status precommit hook
539 Invoking status precommit hook
540 A a
540 A a
541
541
542 check messages when no remote repository is specified:
542 check messages when no remote repository is specified:
543 "no remote repo" route for "hg outgoing --large" is not tested here,
543 "no remote repo" route for "hg outgoing --large" is not tested here,
544 because it can't be reproduced easily.
544 because it can't be reproduced easily.
545
545
546 $ hg init clone1
546 $ hg init clone1
547 $ hg -R clone1 -q pull src
547 $ hg -R clone1 -q pull src
548 $ hg -R clone1 -q update
548 $ hg -R clone1 -q update
549 $ hg -R clone1 paths | grep default
549 $ hg -R clone1 paths | grep default
550 [1]
550 [1]
551
551
552 $ hg -R clone1 summary --large
552 $ hg -R clone1 summary --large
553 parent: 0:fc0bd45326d3 tip
553 parent: 0:fc0bd45326d3 tip
554 #0
554 #0
555 branch: default
555 branch: default
556 commit: (clean)
556 commit: (clean)
557 update: (current)
557 update: (current)
558 largefiles: (no remote repo)
558 largefiles: (no remote repo)
559
559
560 check messages when there is no files to upload:
560 check messages when there is no files to upload:
561
561
562 $ hg -q clone src clone2
562 $ hg -q clone src clone2
563 $ hg -R clone2 paths | grep default
563 $ hg -R clone2 paths | grep default
564 default = $TESTTMP/issue3651/src (glob)
564 default = $TESTTMP/issue3651/src (glob)
565
565
566 $ hg -R clone2 summary --large
566 $ hg -R clone2 summary --large
567 parent: 0:fc0bd45326d3 tip
567 parent: 0:fc0bd45326d3 tip
568 #0
568 #0
569 branch: default
569 branch: default
570 commit: (clean)
570 commit: (clean)
571 update: (current)
571 update: (current)
572 largefiles: (no files to upload)
572 largefiles: (no files to upload)
573 $ hg -R clone2 outgoing --large
573 $ hg -R clone2 outgoing --large
574 comparing with $TESTTMP/issue3651/src (glob)
574 comparing with $TESTTMP/issue3651/src (glob)
575 searching for changes
575 searching for changes
576 no changes found
576 no changes found
577 largefiles: no files to upload
577 largefiles: no files to upload
578 [1]
578 [1]
579
579
580 $ hg -R clone2 outgoing --large --graph --template "{rev}"
580 $ hg -R clone2 outgoing --large --graph --template "{rev}"
581 comparing with $TESTTMP/issue3651/src (glob)
581 comparing with $TESTTMP/issue3651/src (glob)
582 searching for changes
582 searching for changes
583 no changes found
583 no changes found
584 largefiles: no files to upload
584 largefiles: no files to upload
585
585
586 check messages when there are files to upload:
586 check messages when there are files to upload:
587
587
588 $ echo b > clone2/b
588 $ echo b > clone2/b
589 $ hg -R clone2 add --large clone2/b
589 $ hg -R clone2 add --large clone2/b
590 $ hg -R clone2 commit -m '#1'
590 $ hg -R clone2 commit -m '#1'
591 Invoking status precommit hook
591 Invoking status precommit hook
592 A b
592 A b
593 $ hg -R clone2 summary --large
593 $ hg -R clone2 summary --large
594 parent: 1:1acbe71ce432 tip
594 parent: 1:1acbe71ce432 tip
595 #1
595 #1
596 branch: default
596 branch: default
597 commit: (clean)
597 commit: (clean)
598 update: (current)
598 update: (current)
599 largefiles: 1 entities for 1 files to upload
599 largefiles: 1 entities for 1 files to upload
600 $ hg -R clone2 outgoing --large
600 $ hg -R clone2 outgoing --large
601 comparing with $TESTTMP/issue3651/src (glob)
601 comparing with $TESTTMP/issue3651/src (glob)
602 searching for changes
602 searching for changes
603 changeset: 1:1acbe71ce432
603 changeset: 1:1acbe71ce432
604 tag: tip
604 tag: tip
605 user: test
605 user: test
606 date: Thu Jan 01 00:00:00 1970 +0000
606 date: Thu Jan 01 00:00:00 1970 +0000
607 summary: #1
607 summary: #1
608
608
609 largefiles to upload (1 entities):
609 largefiles to upload (1 entities):
610 b
610 b
611
611
612 $ hg -R clone2 outgoing --large --graph --template "{rev}"
612 $ hg -R clone2 outgoing --large --graph --template "{rev}"
613 comparing with $TESTTMP/issue3651/src (glob)
613 comparing with $TESTTMP/issue3651/src (glob)
614 searching for changes
614 searching for changes
615 @ 1
615 @ 1
616
616
617 largefiles to upload (1 entities):
617 largefiles to upload (1 entities):
618 b
618 b
619
619
620
620
621 $ cp clone2/b clone2/b1
621 $ cp clone2/b clone2/b1
622 $ cp clone2/b clone2/b2
622 $ cp clone2/b clone2/b2
623 $ hg -R clone2 add --large clone2/b1 clone2/b2
623 $ hg -R clone2 add --large clone2/b1 clone2/b2
624 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
624 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
625 Invoking status precommit hook
625 Invoking status precommit hook
626 A b1
626 A b1
627 A b2
627 A b2
628 $ hg -R clone2 summary --large
628 $ hg -R clone2 summary --large
629 parent: 2:6095d0695d70 tip
629 parent: 2:6095d0695d70 tip
630 #2: add largefiles referring same entity
630 #2: add largefiles referring same entity
631 branch: default
631 branch: default
632 commit: (clean)
632 commit: (clean)
633 update: (current)
633 update: (current)
634 largefiles: 1 entities for 3 files to upload
634 largefiles: 1 entities for 3 files to upload
635 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
635 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
636 comparing with $TESTTMP/issue3651/src (glob)
636 comparing with $TESTTMP/issue3651/src (glob)
637 searching for changes
637 searching for changes
638 1:1acbe71ce432
638 1:1acbe71ce432
639 2:6095d0695d70
639 2:6095d0695d70
640 largefiles to upload (1 entities):
640 largefiles to upload (1 entities):
641 b
641 b
642 b1
642 b1
643 b2
643 b2
644
644
645 $ hg -R clone2 cat -r 1 clone2/.hglf/b
645 $ hg -R clone2 cat -r 1 clone2/.hglf/b
646 89e6c98d92887913cadf06b2adb97f26cde4849b
646 89e6c98d92887913cadf06b2adb97f26cde4849b
647 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
647 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
648 comparing with $TESTTMP/issue3651/src (glob)
648 comparing with $TESTTMP/issue3651/src (glob)
649 query 1; heads
649 query 1; heads
650 searching for changes
650 searching for changes
651 all remote heads known locally
651 all remote heads known locally
652 1:1acbe71ce432
652 1:1acbe71ce432
653 2:6095d0695d70
653 2:6095d0695d70
654 finding outgoing largefiles: 0/2 revision (0.00%)
654 finding outgoing largefiles: 0/2 revision (0.00%)
655 finding outgoing largefiles: 1/2 revision (50.00%)
655 finding outgoing largefiles: 1/2 revision (50.00%)
656 largefiles to upload (1 entities):
656 largefiles to upload (1 entities):
657 b
657 b
658 89e6c98d92887913cadf06b2adb97f26cde4849b
658 89e6c98d92887913cadf06b2adb97f26cde4849b
659 b1
659 b1
660 89e6c98d92887913cadf06b2adb97f26cde4849b
660 89e6c98d92887913cadf06b2adb97f26cde4849b
661 b2
661 b2
662 89e6c98d92887913cadf06b2adb97f26cde4849b
662 89e6c98d92887913cadf06b2adb97f26cde4849b
663
663
664
664
665 $ echo bbb > clone2/b
665 $ echo bbb > clone2/b
666 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
666 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
667 Invoking status precommit hook
667 Invoking status precommit hook
668 M b
668 M b
669 $ echo bbbb > clone2/b
669 $ echo bbbb > clone2/b
670 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
670 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
671 Invoking status precommit hook
671 Invoking status precommit hook
672 M b
672 M b
673 $ cp clone2/b1 clone2/b
673 $ cp clone2/b1 clone2/b
674 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
674 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
675 Invoking status precommit hook
675 Invoking status precommit hook
676 M b
676 M b
677 $ hg -R clone2 summary --large
677 $ hg -R clone2 summary --large
678 parent: 5:036794ea641c tip
678 parent: 5:036794ea641c tip
679 #5: refer existing largefile entity again
679 #5: refer existing largefile entity again
680 branch: default
680 branch: default
681 commit: (clean)
681 commit: (clean)
682 update: (current)
682 update: (current)
683 largefiles: 3 entities for 3 files to upload
683 largefiles: 3 entities for 3 files to upload
684 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
684 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
685 comparing with $TESTTMP/issue3651/src (glob)
685 comparing with $TESTTMP/issue3651/src (glob)
686 searching for changes
686 searching for changes
687 1:1acbe71ce432
687 1:1acbe71ce432
688 2:6095d0695d70
688 2:6095d0695d70
689 3:7983dce246cc
689 3:7983dce246cc
690 4:233f12ada4ae
690 4:233f12ada4ae
691 5:036794ea641c
691 5:036794ea641c
692 largefiles to upload (3 entities):
692 largefiles to upload (3 entities):
693 b
693 b
694 b1
694 b1
695 b2
695 b2
696
696
697 $ hg -R clone2 cat -r 3 clone2/.hglf/b
697 $ hg -R clone2 cat -r 3 clone2/.hglf/b
698 c801c9cfe94400963fcb683246217d5db77f9a9a
698 c801c9cfe94400963fcb683246217d5db77f9a9a
699 $ hg -R clone2 cat -r 4 clone2/.hglf/b
699 $ hg -R clone2 cat -r 4 clone2/.hglf/b
700 13f9ed0898e315bf59dc2973fec52037b6f441a2
700 13f9ed0898e315bf59dc2973fec52037b6f441a2
701 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
701 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
702 comparing with $TESTTMP/issue3651/src (glob)
702 comparing with $TESTTMP/issue3651/src (glob)
703 query 1; heads
703 query 1; heads
704 searching for changes
704 searching for changes
705 all remote heads known locally
705 all remote heads known locally
706 1:1acbe71ce432
706 1:1acbe71ce432
707 2:6095d0695d70
707 2:6095d0695d70
708 3:7983dce246cc
708 3:7983dce246cc
709 4:233f12ada4ae
709 4:233f12ada4ae
710 5:036794ea641c
710 5:036794ea641c
711 finding outgoing largefiles: 0/5 revision (0.00%)
711 finding outgoing largefiles: 0/5 revision (0.00%)
712 finding outgoing largefiles: 1/5 revision (20.00%)
712 finding outgoing largefiles: 1/5 revision (20.00%)
713 finding outgoing largefiles: 2/5 revision (40.00%)
713 finding outgoing largefiles: 2/5 revision (40.00%)
714 finding outgoing largefiles: 3/5 revision (60.00%)
714 finding outgoing largefiles: 3/5 revision (60.00%)
715 finding outgoing largefiles: 4/5 revision (80.00%)
715 finding outgoing largefiles: 4/5 revision (80.00%)
716 largefiles to upload (3 entities):
716 largefiles to upload (3 entities):
717 b
717 b
718 13f9ed0898e315bf59dc2973fec52037b6f441a2
718 13f9ed0898e315bf59dc2973fec52037b6f441a2
719 89e6c98d92887913cadf06b2adb97f26cde4849b
719 89e6c98d92887913cadf06b2adb97f26cde4849b
720 c801c9cfe94400963fcb683246217d5db77f9a9a
720 c801c9cfe94400963fcb683246217d5db77f9a9a
721 b1
721 b1
722 89e6c98d92887913cadf06b2adb97f26cde4849b
722 89e6c98d92887913cadf06b2adb97f26cde4849b
723 b2
723 b2
724 89e6c98d92887913cadf06b2adb97f26cde4849b
724 89e6c98d92887913cadf06b2adb97f26cde4849b
725
725
726
726
727 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
727 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
728 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
728 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
729
729
730 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
730 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
731 summary" and "hg outgoing", even though files in outgoing revision #2
731 summary" and "hg outgoing", even though files in outgoing revision #2
732 and #5 refer it.
732 and #5 refer it.
733
733
734 $ hg -R clone2 push -r 1 -q
734 $ hg -R clone2 push -r 1 -q
735 $ hg -R clone2 summary --large
735 $ hg -R clone2 summary --large
736 parent: 5:036794ea641c tip
736 parent: 5:036794ea641c tip
737 #5: refer existing largefile entity again
737 #5: refer existing largefile entity again
738 branch: default
738 branch: default
739 commit: (clean)
739 commit: (clean)
740 update: (current)
740 update: (current)
741 largefiles: 2 entities for 1 files to upload
741 largefiles: 2 entities for 1 files to upload
742 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
742 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
743 comparing with $TESTTMP/issue3651/src (glob)
743 comparing with $TESTTMP/issue3651/src (glob)
744 searching for changes
744 searching for changes
745 2:6095d0695d70
745 2:6095d0695d70
746 3:7983dce246cc
746 3:7983dce246cc
747 4:233f12ada4ae
747 4:233f12ada4ae
748 5:036794ea641c
748 5:036794ea641c
749 largefiles to upload (2 entities):
749 largefiles to upload (2 entities):
750 b
750 b
751
751
752 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
752 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
753 comparing with $TESTTMP/issue3651/src (glob)
753 comparing with $TESTTMP/issue3651/src (glob)
754 query 1; heads
754 query 1; heads
755 searching for changes
755 searching for changes
756 all remote heads known locally
756 all remote heads known locally
757 2:6095d0695d70
757 2:6095d0695d70
758 3:7983dce246cc
758 3:7983dce246cc
759 4:233f12ada4ae
759 4:233f12ada4ae
760 5:036794ea641c
760 5:036794ea641c
761 finding outgoing largefiles: 0/4 revision (0.00%)
761 finding outgoing largefiles: 0/4 revision (0.00%)
762 finding outgoing largefiles: 1/4 revision (25.00%)
762 finding outgoing largefiles: 1/4 revision (25.00%)
763 finding outgoing largefiles: 2/4 revision (50.00%)
763 finding outgoing largefiles: 2/4 revision (50.00%)
764 finding outgoing largefiles: 3/4 revision (75.00%)
764 finding outgoing largefiles: 3/4 revision (75.00%)
765 largefiles to upload (2 entities):
765 largefiles to upload (2 entities):
766 b
766 b
767 13f9ed0898e315bf59dc2973fec52037b6f441a2
767 13f9ed0898e315bf59dc2973fec52037b6f441a2
768 c801c9cfe94400963fcb683246217d5db77f9a9a
768 c801c9cfe94400963fcb683246217d5db77f9a9a
769
769
770
770
771 $ cd ..
771 $ cd ..
772
772
773 merge action 'd' for 'local renamed directory to d2/g' which has no filename
773 merge action 'd' for 'local renamed directory to d2/g' which has no filename
774 ==================================================================================
774 ==================================================================================
775
775
776 $ hg init merge-action
776 $ hg init merge-action
777 $ cd merge-action
777 $ cd merge-action
778 $ touch l
778 $ touch l
779 $ hg add --large l
779 $ hg add --large l
780 $ mkdir d1
780 $ mkdir d1
781 $ touch d1/f
781 $ touch d1/f
782 $ hg ci -Aqm0
782 $ hg ci -Aqm0
783 Invoking status precommit hook
783 Invoking status precommit hook
784 A d1/f
784 A d1/f
785 A l
785 A l
786 $ echo > d1/f
786 $ echo > d1/f
787 $ touch d1/g
787 $ touch d1/g
788 $ hg ci -Aqm1
788 $ hg ci -Aqm1
789 Invoking status precommit hook
789 Invoking status precommit hook
790 M d1/f
790 M d1/f
791 A d1/g
791 A d1/g
792 $ hg up -qr0
792 $ hg up -qr0
793 $ hg mv d1 d2
793 $ hg mv d1 d2
794 moving d1/f to d2/f (glob)
794 moving d1/f to d2/f (glob)
795 $ hg ci -qm2
795 $ hg ci -qm2
796 Invoking status precommit hook
796 Invoking status precommit hook
797 A d2/f
797 A d2/f
798 R d1/f
798 R d1/f
799 $ hg merge
799 $ hg merge
800 merging d2/f and d1/f to d2/f
800 merging d2/f and d1/f to d2/f
801 getting changed largefiles
801 getting changed largefiles
802 0 largefiles updated, 0 removed
802 0 largefiles updated, 0 removed
803 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
803 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
804 (branch merge, don't forget to commit)
804 (branch merge, don't forget to commit)
805 $ cd ..
805 $ cd ..
806
806
807
807
808 Merge conflicts:
808 Merge conflicts:
809 =====================
809 =====================
810
810
811 $ hg init merge
811 $ hg init merge
812 $ cd merge
812 $ cd merge
813 $ echo 0 > f-different
813 $ echo 0 > f-different
814 $ echo 0 > f-same
814 $ echo 0 > f-same
815 $ echo 0 > f-unchanged-1
815 $ echo 0 > f-unchanged-1
816 $ echo 0 > f-unchanged-2
816 $ echo 0 > f-unchanged-2
817 $ hg add --large *
817 $ hg add --large *
818 $ hg ci -m0
818 $ hg ci -m0
819 Invoking status precommit hook
819 Invoking status precommit hook
820 A f-different
820 A f-different
821 A f-same
821 A f-same
822 A f-unchanged-1
822 A f-unchanged-1
823 A f-unchanged-2
823 A f-unchanged-2
824 $ echo tmp1 > f-unchanged-1
824 $ echo tmp1 > f-unchanged-1
825 $ echo tmp1 > f-unchanged-2
825 $ echo tmp1 > f-unchanged-2
826 $ echo tmp1 > f-same
826 $ echo tmp1 > f-same
827 $ hg ci -m1
827 $ hg ci -m1
828 Invoking status precommit hook
828 Invoking status precommit hook
829 M f-same
829 M f-same
830 M f-unchanged-1
830 M f-unchanged-1
831 M f-unchanged-2
831 M f-unchanged-2
832 $ echo 2 > f-different
832 $ echo 2 > f-different
833 $ echo 0 > f-unchanged-1
833 $ echo 0 > f-unchanged-1
834 $ echo 1 > f-unchanged-2
834 $ echo 1 > f-unchanged-2
835 $ echo 1 > f-same
835 $ echo 1 > f-same
836 $ hg ci -m2
836 $ hg ci -m2
837 Invoking status precommit hook
837 Invoking status precommit hook
838 M f-different
838 M f-different
839 M f-same
839 M f-same
840 M f-unchanged-1
840 M f-unchanged-1
841 M f-unchanged-2
841 M f-unchanged-2
842 $ hg up -qr0
842 $ hg up -qr0
843 $ echo tmp2 > f-unchanged-1
843 $ echo tmp2 > f-unchanged-1
844 $ echo tmp2 > f-unchanged-2
844 $ echo tmp2 > f-unchanged-2
845 $ echo tmp2 > f-same
845 $ echo tmp2 > f-same
846 $ hg ci -m3
846 $ hg ci -m3
847 Invoking status precommit hook
847 Invoking status precommit hook
848 M f-same
848 M f-same
849 M f-unchanged-1
849 M f-unchanged-1
850 M f-unchanged-2
850 M f-unchanged-2
851 created new head
851 created new head
852 $ echo 1 > f-different
852 $ echo 1 > f-different
853 $ echo 1 > f-unchanged-1
853 $ echo 1 > f-unchanged-1
854 $ echo 0 > f-unchanged-2
854 $ echo 0 > f-unchanged-2
855 $ echo 1 > f-same
855 $ echo 1 > f-same
856 $ hg ci -m4
856 $ hg ci -m4
857 Invoking status precommit hook
857 Invoking status precommit hook
858 M f-different
858 M f-different
859 M f-same
859 M f-same
860 M f-unchanged-1
860 M f-unchanged-1
861 M f-unchanged-2
861 M f-unchanged-2
862 $ hg merge
862 $ hg merge
863 largefile f-different has a merge conflict
863 largefile f-different has a merge conflict
864 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
864 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
865 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
865 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
866 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
866 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
867 getting changed largefiles
867 getting changed largefiles
868 1 largefiles updated, 0 removed
868 1 largefiles updated, 0 removed
869 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
869 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
870 (branch merge, don't forget to commit)
870 (branch merge, don't forget to commit)
871 $ cat f-different
871 $ cat f-different
872 1
872 1
873 $ cat f-same
873 $ cat f-same
874 1
874 1
875 $ cat f-unchanged-1
875 $ cat f-unchanged-1
876 1
876 1
877 $ cat f-unchanged-2
877 $ cat f-unchanged-2
878 1
878 1
879 $ cd ..
879 $ cd ..
880
880
881 Test largefile insulation (do not enabled a side effect
881 Test largefile insulation (do not enabled a side effect
882 ========================================================
882 ========================================================
883
883
884 Check whether "largefiles" feature is supported only in repositories
884 Check whether "largefiles" feature is supported only in repositories
885 enabling largefiles extension.
885 enabling largefiles extension.
886
886
887 $ mkdir individualenabling
887 $ mkdir individualenabling
888 $ cd individualenabling
888 $ cd individualenabling
889
889
890 $ hg init enabledlocally
890 $ hg init enabledlocally
891 $ echo large > enabledlocally/large
891 $ echo large > enabledlocally/large
892 $ hg -R enabledlocally add --large enabledlocally/large
892 $ hg -R enabledlocally add --large enabledlocally/large
893 $ hg -R enabledlocally commit -m '#0'
893 $ hg -R enabledlocally commit -m '#0'
894 Invoking status precommit hook
894 Invoking status precommit hook
895 A large
895 A large
896
896
897 $ hg init notenabledlocally
897 $ hg init notenabledlocally
898 $ echo large > notenabledlocally/large
898 $ echo large > notenabledlocally/large
899 $ hg -R notenabledlocally add --large notenabledlocally/large
899 $ hg -R notenabledlocally add --large notenabledlocally/large
900 $ hg -R notenabledlocally commit -m '#0'
900 $ hg -R notenabledlocally commit -m '#0'
901 Invoking status precommit hook
901 Invoking status precommit hook
902 A large
902 A large
903
903
904 $ cat >> $HGRCPATH <<EOF
904 $ cat >> $HGRCPATH <<EOF
905 > [extensions]
905 > [extensions]
906 > # disable globally
906 > # disable globally
907 > largefiles=!
907 > largefiles=!
908 > EOF
908 > EOF
909 $ cat >> enabledlocally/.hg/hgrc <<EOF
909 $ cat >> enabledlocally/.hg/hgrc <<EOF
910 > [extensions]
910 > [extensions]
911 > # enable locally
911 > # enable locally
912 > largefiles=
912 > largefiles=
913 > EOF
913 > EOF
914 $ hg -R enabledlocally root
914 $ hg -R enabledlocally root
915 $TESTTMP/individualenabling/enabledlocally (glob)
915 $TESTTMP/individualenabling/enabledlocally (glob)
916 $ hg -R notenabledlocally root
916 $ hg -R notenabledlocally root
917 abort: repository requires features unknown to this Mercurial: largefiles!
917 abort: repository requires features unknown to this Mercurial: largefiles!
918 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
918 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
919 [255]
919 [255]
920
920
921 $ hg init push-dst
921 $ hg init push-dst
922 $ hg -R enabledlocally push push-dst
922 $ hg -R enabledlocally push push-dst
923 pushing to push-dst
923 pushing to push-dst
924 abort: required features are not supported in the destination: largefiles
924 abort: required features are not supported in the destination: largefiles
925 [255]
925 [255]
926
926
927 $ hg init pull-src
927 $ hg init pull-src
928 $ hg -R pull-src pull enabledlocally
928 $ hg -R pull-src pull enabledlocally
929 pulling from enabledlocally
929 pulling from enabledlocally
930 abort: required features are not supported in the destination: largefiles
930 abort: required features are not supported in the destination: largefiles
931 [255]
931 [255]
932
932
933 $ hg clone enabledlocally clone-dst
933 $ hg clone enabledlocally clone-dst
934 abort: repository requires features unknown to this Mercurial: largefiles!
934 abort: repository requires features unknown to this Mercurial: largefiles!
935 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
935 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
936 [255]
936 [255]
937 $ test -d clone-dst
937 $ test -d clone-dst
938 [1]
938 [1]
939 $ hg clone --pull enabledlocally clone-pull-dst
939 $ hg clone --pull enabledlocally clone-pull-dst
940 abort: required features are not supported in the destination: largefiles
940 abort: required features are not supported in the destination: largefiles
941 [255]
941 [255]
942 $ test -d clone-pull-dst
942 $ test -d clone-pull-dst
943 [1]
943 [1]
944
944
945 #if serve
945 #if serve
946
946
947 Test largefiles specific peer setup, when largefiles is enabled
947 Test largefiles specific peer setup, when largefiles is enabled
948 locally (issue4109)
948 locally (issue4109)
949
949
950 $ hg showconfig extensions | grep largefiles
950 $ hg showconfig extensions | grep largefiles
951 extensions.largefiles=!
951 extensions.largefiles=!
952 $ mkdir -p $TESTTMP/individualenabling/usercache
952 $ mkdir -p $TESTTMP/individualenabling/usercache
953
953
954 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
954 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
955 $ cat hg.pid >> $DAEMON_PIDS
955 $ cat hg.pid >> $DAEMON_PIDS
956
956
957 $ hg init pull-dst
957 $ hg init pull-dst
958 $ cat > pull-dst/.hg/hgrc <<EOF
958 $ cat > pull-dst/.hg/hgrc <<EOF
959 > [extensions]
959 > [extensions]
960 > # enable locally
960 > # enable locally
961 > largefiles=
961 > largefiles=
962 > [largefiles]
962 > [largefiles]
963 > # ignore system cache to force largefiles specific wire proto access
963 > # ignore system cache to force largefiles specific wire proto access
964 > usercache=$TESTTMP/individualenabling/usercache
964 > usercache=$TESTTMP/individualenabling/usercache
965 > EOF
965 > EOF
966 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
966 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
967
967
968 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
968 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
969 #endif
969 #endif
970
970
971 Test overridden functions work correctly even for repos disabling
971 Test overridden functions work correctly even for repos disabling
972 largefiles (issue4547)
972 largefiles (issue4547)
973
973
974 $ hg showconfig extensions | grep largefiles
974 $ hg showconfig extensions | grep largefiles
975 extensions.largefiles=!
975 extensions.largefiles=!
976
976
977 (test updating implied by clone)
977 (test updating implied by clone)
978
978
979 $ hg init enabled-but-no-largefiles
979 $ hg init enabled-but-no-largefiles
980 $ echo normal1 > enabled-but-no-largefiles/normal1
980 $ echo normal1 > enabled-but-no-largefiles/normal1
981 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
981 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
982 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
982 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
983 Invoking status precommit hook
983 Invoking status precommit hook
984 A normal1
984 A normal1
985 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
985 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
986 > [extensions]
986 > [extensions]
987 > # enable locally
987 > # enable locally
988 > largefiles=
988 > largefiles=
989 > EOF
989 > EOF
990 $ hg clone -q enabled-but-no-largefiles no-largefiles
990 $ hg clone -q enabled-but-no-largefiles no-largefiles
991
991
992 (test rebasing implied by pull: precommit while rebasing unexpectedly
992 (test rebasing implied by pull: precommit while rebasing unexpectedly
993 shows "normal3" as "?", because lfdirstate isn't yet written out at
993 shows "normal3" as "?", because lfdirstate isn't yet written out at
994 that time)
994 that time)
995
995
996 $ echo normal2 > enabled-but-no-largefiles/normal2
996 $ echo normal2 > enabled-but-no-largefiles/normal2
997 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
997 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
998 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
998 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
999 Invoking status precommit hook
999 Invoking status precommit hook
1000 A normal2
1000 A normal2
1001
1001
1002 $ echo normal3 > no-largefiles/normal3
1002 $ echo normal3 > no-largefiles/normal3
1003 $ hg -R no-largefiles add no-largefiles/normal3
1003 $ hg -R no-largefiles add no-largefiles/normal3
1004 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1004 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1005 Invoking status precommit hook
1005 Invoking status precommit hook
1006 A normal3
1006 A normal3
1007
1007
1008 $ hg -R no-largefiles -q pull --rebase
1008 $ hg -R no-largefiles -q pull --rebase
1009 Invoking status precommit hook
1009 Invoking status precommit hook
1010 ? normal3
1010 ? normal3
1011
1011
1012 (test reverting)
1012 (test reverting)
1013
1013
1014 $ hg init subrepo-root
1014 $ hg init subrepo-root
1015 $ cat >> subrepo-root/.hg/hgrc <<EOF
1015 $ cat >> subrepo-root/.hg/hgrc <<EOF
1016 > [extensions]
1016 > [extensions]
1017 > # enable locally
1017 > # enable locally
1018 > largefiles=
1018 > largefiles=
1019 > EOF
1019 > EOF
1020 $ echo large > subrepo-root/large
1020 $ echo large > subrepo-root/large
1021 $ hg -R subrepo-root add --large subrepo-root/large
1021 $ hg -R subrepo-root add --large subrepo-root/large
1022 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1022 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1023 $ cat > subrepo-root/.hgsub <<EOF
1023 $ cat > subrepo-root/.hgsub <<EOF
1024 > no-largefiles = no-largefiles
1024 > no-largefiles = no-largefiles
1025 > EOF
1025 > EOF
1026 $ hg -R subrepo-root add subrepo-root/.hgsub
1026 $ hg -R subrepo-root add subrepo-root/.hgsub
1027 $ hg -R subrepo-root commit -m '#0'
1027 $ hg -R subrepo-root commit -m '#0'
1028 Invoking status precommit hook
1028 Invoking status precommit hook
1029 A .hgsub
1029 A .hgsub
1030 A large
1030 A large
1031 ? .hgsubstate
1031 ? .hgsubstate
1032 $ echo dirty >> subrepo-root/large
1032 $ echo dirty >> subrepo-root/large
1033 $ echo dirty >> subrepo-root/no-largefiles/normal1
1033 $ echo dirty >> subrepo-root/no-largefiles/normal1
1034 $ hg -R subrepo-root status -S
1034 $ hg -R subrepo-root status -S
1035 M large
1035 M large
1036 M no-largefiles/normal1
1036 M no-largefiles/normal1
1037 $ hg -R subrepo-root revert --all
1037 $ hg -R subrepo-root revert --all
1038 reverting subrepo-root/.hglf/large (glob)
1038 reverting subrepo-root/.hglf/large (glob)
1039 reverting subrepo no-largefiles
1039 reverting subrepo no-largefiles
1040 reverting subrepo-root/no-largefiles/normal1 (glob)
1040 reverting subrepo-root/no-largefiles/normal1 (glob)
1041
1041
1042 $ cd ..
1042 $ cd ..
1043
1043
1044
1044
1045 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1045 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1046 =========================================================================
1046 =========================================================================
1047
1047
1048 $ hg showconfig extensions | grep largefiles
1048 $ hg showconfig extensions | grep largefiles
1049 extensions.largefiles=!
1049 extensions.largefiles=!
1050
1050
1051 $ mkdir issue3861
1051 $ mkdir issue3861
1052 $ cd issue3861
1052 $ cd issue3861
1053 $ hg init src
1053 $ hg init src
1054 $ hg clone -q src dst
1054 $ hg clone -q src dst
1055 $ echo a > src/a
1055 $ echo a > src/a
1056 $ hg -R src commit -Aqm "#0"
1056 $ hg -R src commit -Aqm "#0"
1057 Invoking status precommit hook
1057 Invoking status precommit hook
1058 A a
1058 A a
1059
1059
1060 $ cat >> dst/.hg/hgrc <<EOF
1060 $ cat >> dst/.hg/hgrc <<EOF
1061 > [extensions]
1061 > [extensions]
1062 > largefiles=
1062 > largefiles=
1063 > EOF
1063 > EOF
1064 $ hg -R dst pull --rebase
1064 $ hg -R dst pull --rebase
1065 pulling from $TESTTMP/issue3861/src (glob)
1065 pulling from $TESTTMP/issue3861/src (glob)
1066 requesting all changes
1066 requesting all changes
1067 adding changesets
1067 adding changesets
1068 adding manifests
1068 adding manifests
1069 adding file changes
1069 adding file changes
1070 added 1 changesets with 1 changes to 1 files
1070 added 1 changesets with 1 changes to 1 files
1071 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
1071 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
1072 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1072 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1073
1073
1074 $ cd ..
1074 $ cd ..
@@ -1,546 +1,546 b''
1 Create test repository:
1 Create test repository:
2
2
3 $ hg init repo
3 $ hg init repo
4 $ cd repo
4 $ cd repo
5 $ echo x1 > x.txt
5 $ echo x1 > x.txt
6
6
7 $ hg init foo
7 $ hg init foo
8 $ cd foo
8 $ cd foo
9 $ echo y1 > y.txt
9 $ echo y1 > y.txt
10
10
11 $ hg init bar
11 $ hg init bar
12 $ cd bar
12 $ cd bar
13 $ echo z1 > z.txt
13 $ echo z1 > z.txt
14
14
15 $ cd ..
15 $ cd ..
16 $ echo 'bar = bar' > .hgsub
16 $ echo 'bar = bar' > .hgsub
17
17
18 $ cd ..
18 $ cd ..
19 $ echo 'foo = foo' > .hgsub
19 $ echo 'foo = foo' > .hgsub
20
20
21 Add files --- .hgsub files must go first to trigger subrepos:
21 Add files --- .hgsub files must go first to trigger subrepos:
22
22
23 $ hg add -S .hgsub
23 $ hg add -S .hgsub
24 $ hg add -S foo/.hgsub
24 $ hg add -S foo/.hgsub
25 $ hg add -S foo/bar
25 $ hg add -S foo/bar
26 adding foo/bar/z.txt (glob)
26 adding foo/bar/z.txt (glob)
27 $ hg add -S
27 $ hg add -S
28 adding x.txt
28 adding x.txt
29 adding foo/y.txt (glob)
29 adding foo/y.txt (glob)
30
30
31 Test recursive status without committing anything:
31 Test recursive status without committing anything:
32
32
33 $ hg status -S
33 $ hg status -S
34 A .hgsub
34 A .hgsub
35 A foo/.hgsub
35 A foo/.hgsub
36 A foo/bar/z.txt
36 A foo/bar/z.txt
37 A foo/y.txt
37 A foo/y.txt
38 A x.txt
38 A x.txt
39
39
40 Test recursive diff without committing anything:
40 Test recursive diff without committing anything:
41
41
42 $ hg diff --nodates -S foo
42 $ hg diff --nodates -S foo
43 diff -r 000000000000 foo/.hgsub
43 diff -r 000000000000 foo/.hgsub
44 --- /dev/null
44 --- /dev/null
45 +++ b/foo/.hgsub
45 +++ b/foo/.hgsub
46 @@ -0,0 +1,1 @@
46 @@ -0,0 +1,1 @@
47 +bar = bar
47 +bar = bar
48 diff -r 000000000000 foo/y.txt
48 diff -r 000000000000 foo/y.txt
49 --- /dev/null
49 --- /dev/null
50 +++ b/foo/y.txt
50 +++ b/foo/y.txt
51 @@ -0,0 +1,1 @@
51 @@ -0,0 +1,1 @@
52 +y1
52 +y1
53 diff -r 000000000000 foo/bar/z.txt
53 diff -r 000000000000 foo/bar/z.txt
54 --- /dev/null
54 --- /dev/null
55 +++ b/foo/bar/z.txt
55 +++ b/foo/bar/z.txt
56 @@ -0,0 +1,1 @@
56 @@ -0,0 +1,1 @@
57 +z1
57 +z1
58
58
59 Commits:
59 Commits:
60
60
61 $ hg commit -m fails
61 $ hg commit -m fails
62 abort: uncommitted changes in subrepo foo
62 abort: uncommitted changes in subrepository 'foo'
63 (use --subrepos for recursive commit)
63 (use --subrepos for recursive commit)
64 [255]
64 [255]
65
65
66 The --subrepos flag overwrite the config setting:
66 The --subrepos flag overwrite the config setting:
67
67
68 $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos
68 $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos
69 committing subrepository foo
69 committing subrepository foo
70 committing subrepository foo/bar (glob)
70 committing subrepository foo/bar (glob)
71
71
72 $ cd foo
72 $ cd foo
73 $ echo y2 >> y.txt
73 $ echo y2 >> y.txt
74 $ hg commit -m 0-1-0
74 $ hg commit -m 0-1-0
75
75
76 $ cd bar
76 $ cd bar
77 $ echo z2 >> z.txt
77 $ echo z2 >> z.txt
78 $ hg commit -m 0-1-1
78 $ hg commit -m 0-1-1
79
79
80 $ cd ..
80 $ cd ..
81 $ hg commit -m 0-2-1
81 $ hg commit -m 0-2-1
82
82
83 $ cd ..
83 $ cd ..
84 $ hg commit -m 1-2-1
84 $ hg commit -m 1-2-1
85
85
86 Change working directory:
86 Change working directory:
87
87
88 $ echo y3 >> foo/y.txt
88 $ echo y3 >> foo/y.txt
89 $ echo z3 >> foo/bar/z.txt
89 $ echo z3 >> foo/bar/z.txt
90 $ hg status -S
90 $ hg status -S
91 M foo/bar/z.txt
91 M foo/bar/z.txt
92 M foo/y.txt
92 M foo/y.txt
93 $ hg diff --nodates -S
93 $ hg diff --nodates -S
94 diff -r d254738c5f5e foo/y.txt
94 diff -r d254738c5f5e foo/y.txt
95 --- a/foo/y.txt
95 --- a/foo/y.txt
96 +++ b/foo/y.txt
96 +++ b/foo/y.txt
97 @@ -1,2 +1,3 @@
97 @@ -1,2 +1,3 @@
98 y1
98 y1
99 y2
99 y2
100 +y3
100 +y3
101 diff -r 9647f22de499 foo/bar/z.txt
101 diff -r 9647f22de499 foo/bar/z.txt
102 --- a/foo/bar/z.txt
102 --- a/foo/bar/z.txt
103 +++ b/foo/bar/z.txt
103 +++ b/foo/bar/z.txt
104 @@ -1,2 +1,3 @@
104 @@ -1,2 +1,3 @@
105 z1
105 z1
106 z2
106 z2
107 +z3
107 +z3
108
108
109 Status call crossing repository boundaries:
109 Status call crossing repository boundaries:
110
110
111 $ hg status -S foo/bar/z.txt
111 $ hg status -S foo/bar/z.txt
112 M foo/bar/z.txt
112 M foo/bar/z.txt
113 $ hg status -S -I 'foo/?.txt'
113 $ hg status -S -I 'foo/?.txt'
114 M foo/y.txt
114 M foo/y.txt
115 $ hg status -S -I '**/?.txt'
115 $ hg status -S -I '**/?.txt'
116 M foo/bar/z.txt
116 M foo/bar/z.txt
117 M foo/y.txt
117 M foo/y.txt
118 $ hg diff --nodates -S -I '**/?.txt'
118 $ hg diff --nodates -S -I '**/?.txt'
119 diff -r d254738c5f5e foo/y.txt
119 diff -r d254738c5f5e foo/y.txt
120 --- a/foo/y.txt
120 --- a/foo/y.txt
121 +++ b/foo/y.txt
121 +++ b/foo/y.txt
122 @@ -1,2 +1,3 @@
122 @@ -1,2 +1,3 @@
123 y1
123 y1
124 y2
124 y2
125 +y3
125 +y3
126 diff -r 9647f22de499 foo/bar/z.txt
126 diff -r 9647f22de499 foo/bar/z.txt
127 --- a/foo/bar/z.txt
127 --- a/foo/bar/z.txt
128 +++ b/foo/bar/z.txt
128 +++ b/foo/bar/z.txt
129 @@ -1,2 +1,3 @@
129 @@ -1,2 +1,3 @@
130 z1
130 z1
131 z2
131 z2
132 +z3
132 +z3
133
133
134 Status from within a subdirectory:
134 Status from within a subdirectory:
135
135
136 $ mkdir dir
136 $ mkdir dir
137 $ cd dir
137 $ cd dir
138 $ echo a1 > a.txt
138 $ echo a1 > a.txt
139 $ hg status -S
139 $ hg status -S
140 M foo/bar/z.txt
140 M foo/bar/z.txt
141 M foo/y.txt
141 M foo/y.txt
142 ? dir/a.txt
142 ? dir/a.txt
143 $ hg diff --nodates -S
143 $ hg diff --nodates -S
144 diff -r d254738c5f5e foo/y.txt
144 diff -r d254738c5f5e foo/y.txt
145 --- a/foo/y.txt
145 --- a/foo/y.txt
146 +++ b/foo/y.txt
146 +++ b/foo/y.txt
147 @@ -1,2 +1,3 @@
147 @@ -1,2 +1,3 @@
148 y1
148 y1
149 y2
149 y2
150 +y3
150 +y3
151 diff -r 9647f22de499 foo/bar/z.txt
151 diff -r 9647f22de499 foo/bar/z.txt
152 --- a/foo/bar/z.txt
152 --- a/foo/bar/z.txt
153 +++ b/foo/bar/z.txt
153 +++ b/foo/bar/z.txt
154 @@ -1,2 +1,3 @@
154 @@ -1,2 +1,3 @@
155 z1
155 z1
156 z2
156 z2
157 +z3
157 +z3
158
158
159 Status with relative path:
159 Status with relative path:
160
160
161 $ hg status -S ..
161 $ hg status -S ..
162 M ../foo/bar/z.txt
162 M ../foo/bar/z.txt
163 M ../foo/y.txt
163 M ../foo/y.txt
164 ? a.txt
164 ? a.txt
165
165
166 XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as
166 XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as
167 added instead of modified.
167 added instead of modified.
168 $ hg status -S .. --config extensions.largefiles=
168 $ hg status -S .. --config extensions.largefiles=
169 M ../foo/bar/z.txt
169 M ../foo/bar/z.txt
170 M ../foo/y.txt
170 M ../foo/y.txt
171 ? a.txt
171 ? a.txt
172
172
173 $ hg diff --nodates -S ..
173 $ hg diff --nodates -S ..
174 diff -r d254738c5f5e foo/y.txt
174 diff -r d254738c5f5e foo/y.txt
175 --- a/foo/y.txt
175 --- a/foo/y.txt
176 +++ b/foo/y.txt
176 +++ b/foo/y.txt
177 @@ -1,2 +1,3 @@
177 @@ -1,2 +1,3 @@
178 y1
178 y1
179 y2
179 y2
180 +y3
180 +y3
181 diff -r 9647f22de499 foo/bar/z.txt
181 diff -r 9647f22de499 foo/bar/z.txt
182 --- a/foo/bar/z.txt
182 --- a/foo/bar/z.txt
183 +++ b/foo/bar/z.txt
183 +++ b/foo/bar/z.txt
184 @@ -1,2 +1,3 @@
184 @@ -1,2 +1,3 @@
185 z1
185 z1
186 z2
186 z2
187 +z3
187 +z3
188 $ cd ..
188 $ cd ..
189
189
190 Cleanup and final commit:
190 Cleanup and final commit:
191
191
192 $ rm -r dir
192 $ rm -r dir
193 $ hg commit --subrepos -m 2-3-2
193 $ hg commit --subrepos -m 2-3-2
194 committing subrepository foo
194 committing subrepository foo
195 committing subrepository foo/bar (glob)
195 committing subrepository foo/bar (glob)
196
196
197 Test explicit path commands within subrepos: add/forget
197 Test explicit path commands within subrepos: add/forget
198 $ echo z1 > foo/bar/z2.txt
198 $ echo z1 > foo/bar/z2.txt
199 $ hg status -S
199 $ hg status -S
200 ? foo/bar/z2.txt
200 ? foo/bar/z2.txt
201 $ hg add foo/bar/z2.txt
201 $ hg add foo/bar/z2.txt
202 $ hg status -S
202 $ hg status -S
203 A foo/bar/z2.txt
203 A foo/bar/z2.txt
204 $ hg forget foo/bar/z2.txt
204 $ hg forget foo/bar/z2.txt
205 $ hg status -S
205 $ hg status -S
206 ? foo/bar/z2.txt
206 ? foo/bar/z2.txt
207 $ hg forget foo/bar/z2.txt
207 $ hg forget foo/bar/z2.txt
208 not removing foo/bar/z2.txt: file is already untracked (glob)
208 not removing foo/bar/z2.txt: file is already untracked (glob)
209 [1]
209 [1]
210 $ hg status -S
210 $ hg status -S
211 ? foo/bar/z2.txt
211 ? foo/bar/z2.txt
212 $ rm foo/bar/z2.txt
212 $ rm foo/bar/z2.txt
213
213
214 Log with the relationships between repo and its subrepo:
214 Log with the relationships between repo and its subrepo:
215
215
216 $ hg log --template '{rev}:{node|short} {desc}\n'
216 $ hg log --template '{rev}:{node|short} {desc}\n'
217 2:1326fa26d0c0 2-3-2
217 2:1326fa26d0c0 2-3-2
218 1:4b3c9ff4f66b 1-2-1
218 1:4b3c9ff4f66b 1-2-1
219 0:23376cbba0d8 0-0-0
219 0:23376cbba0d8 0-0-0
220
220
221 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
221 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
222 3:65903cebad86 2-3-2
222 3:65903cebad86 2-3-2
223 2:d254738c5f5e 0-2-1
223 2:d254738c5f5e 0-2-1
224 1:8629ce7dcc39 0-1-0
224 1:8629ce7dcc39 0-1-0
225 0:af048e97ade2 0-0-0
225 0:af048e97ade2 0-0-0
226
226
227 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
227 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
228 2:31ecbdafd357 2-3-2
228 2:31ecbdafd357 2-3-2
229 1:9647f22de499 0-1-1
229 1:9647f22de499 0-1-1
230 0:4904098473f9 0-0-0
230 0:4904098473f9 0-0-0
231
231
232 Status between revisions:
232 Status between revisions:
233
233
234 $ hg status -S
234 $ hg status -S
235 $ hg status -S --rev 0:1
235 $ hg status -S --rev 0:1
236 M .hgsubstate
236 M .hgsubstate
237 M foo/.hgsubstate
237 M foo/.hgsubstate
238 M foo/bar/z.txt
238 M foo/bar/z.txt
239 M foo/y.txt
239 M foo/y.txt
240 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
240 $ hg diff --nodates -S -I '**/?.txt' --rev 0:1
241 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
241 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
242 --- a/foo/y.txt
242 --- a/foo/y.txt
243 +++ b/foo/y.txt
243 +++ b/foo/y.txt
244 @@ -1,1 +1,2 @@
244 @@ -1,1 +1,2 @@
245 y1
245 y1
246 +y2
246 +y2
247 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
247 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
248 --- a/foo/bar/z.txt
248 --- a/foo/bar/z.txt
249 +++ b/foo/bar/z.txt
249 +++ b/foo/bar/z.txt
250 @@ -1,1 +1,2 @@
250 @@ -1,1 +1,2 @@
251 z1
251 z1
252 +z2
252 +z2
253
253
254 Enable progress extension for archive tests:
254 Enable progress extension for archive tests:
255
255
256 $ cp $HGRCPATH $HGRCPATH.no-progress
256 $ cp $HGRCPATH $HGRCPATH.no-progress
257 $ cat >> $HGRCPATH <<EOF
257 $ cat >> $HGRCPATH <<EOF
258 > [extensions]
258 > [extensions]
259 > progress =
259 > progress =
260 > [progress]
260 > [progress]
261 > assume-tty = 1
261 > assume-tty = 1
262 > delay = 0
262 > delay = 0
263 > format = topic bar number
263 > format = topic bar number
264 > refresh = 0
264 > refresh = 0
265 > width = 60
265 > width = 60
266 > EOF
266 > EOF
267
267
268 Test archiving to a directory tree (the doubled lines in the output
268 Test archiving to a directory tree (the doubled lines in the output
269 only show up in the test output, not in real usage):
269 only show up in the test output, not in real usage):
270
270
271 $ hg archive --subrepos ../archive
271 $ hg archive --subrepos ../archive
272 \r (no-eol) (esc)
272 \r (no-eol) (esc)
273 archiving [ ] 0/3\r (no-eol) (esc)
273 archiving [ ] 0/3\r (no-eol) (esc)
274 archiving [ ] 0/3\r (no-eol) (esc)
274 archiving [ ] 0/3\r (no-eol) (esc)
275 archiving [=============> ] 1/3\r (no-eol) (esc)
275 archiving [=============> ] 1/3\r (no-eol) (esc)
276 archiving [=============> ] 1/3\r (no-eol) (esc)
276 archiving [=============> ] 1/3\r (no-eol) (esc)
277 archiving [===========================> ] 2/3\r (no-eol) (esc)
277 archiving [===========================> ] 2/3\r (no-eol) (esc)
278 archiving [===========================> ] 2/3\r (no-eol) (esc)
278 archiving [===========================> ] 2/3\r (no-eol) (esc)
279 archiving [==========================================>] 3/3\r (no-eol) (esc)
279 archiving [==========================================>] 3/3\r (no-eol) (esc)
280 archiving [==========================================>] 3/3\r (no-eol) (esc)
280 archiving [==========================================>] 3/3\r (no-eol) (esc)
281 \r (no-eol) (esc)
281 \r (no-eol) (esc)
282 \r (no-eol) (esc)
282 \r (no-eol) (esc)
283 archiving (foo) [ ] 0/3\r (no-eol) (esc)
283 archiving (foo) [ ] 0/3\r (no-eol) (esc)
284 archiving (foo) [ ] 0/3\r (no-eol) (esc)
284 archiving (foo) [ ] 0/3\r (no-eol) (esc)
285 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
285 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
286 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
286 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
287 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
287 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
288 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
288 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
289 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
289 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
290 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
290 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
291 \r (no-eol) (esc)
291 \r (no-eol) (esc)
292 \r (no-eol) (esc)
292 \r (no-eol) (esc)
293 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
293 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
294 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
294 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
295 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
295 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
296 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
296 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
297 \r (no-eol) (esc)
297 \r (no-eol) (esc)
298 $ find ../archive | sort
298 $ find ../archive | sort
299 ../archive
299 ../archive
300 ../archive/.hg_archival.txt
300 ../archive/.hg_archival.txt
301 ../archive/.hgsub
301 ../archive/.hgsub
302 ../archive/.hgsubstate
302 ../archive/.hgsubstate
303 ../archive/foo
303 ../archive/foo
304 ../archive/foo/.hgsub
304 ../archive/foo/.hgsub
305 ../archive/foo/.hgsubstate
305 ../archive/foo/.hgsubstate
306 ../archive/foo/bar
306 ../archive/foo/bar
307 ../archive/foo/bar/z.txt
307 ../archive/foo/bar/z.txt
308 ../archive/foo/y.txt
308 ../archive/foo/y.txt
309 ../archive/x.txt
309 ../archive/x.txt
310
310
311 Test archiving to zip file (unzip output is unstable):
311 Test archiving to zip file (unzip output is unstable):
312
312
313 $ hg archive --subrepos ../archive.zip
313 $ hg archive --subrepos ../archive.zip
314 \r (no-eol) (esc)
314 \r (no-eol) (esc)
315 archiving [ ] 0/3\r (no-eol) (esc)
315 archiving [ ] 0/3\r (no-eol) (esc)
316 archiving [ ] 0/3\r (no-eol) (esc)
316 archiving [ ] 0/3\r (no-eol) (esc)
317 archiving [=============> ] 1/3\r (no-eol) (esc)
317 archiving [=============> ] 1/3\r (no-eol) (esc)
318 archiving [=============> ] 1/3\r (no-eol) (esc)
318 archiving [=============> ] 1/3\r (no-eol) (esc)
319 archiving [===========================> ] 2/3\r (no-eol) (esc)
319 archiving [===========================> ] 2/3\r (no-eol) (esc)
320 archiving [===========================> ] 2/3\r (no-eol) (esc)
320 archiving [===========================> ] 2/3\r (no-eol) (esc)
321 archiving [==========================================>] 3/3\r (no-eol) (esc)
321 archiving [==========================================>] 3/3\r (no-eol) (esc)
322 archiving [==========================================>] 3/3\r (no-eol) (esc)
322 archiving [==========================================>] 3/3\r (no-eol) (esc)
323 \r (no-eol) (esc)
323 \r (no-eol) (esc)
324 \r (no-eol) (esc)
324 \r (no-eol) (esc)
325 archiving (foo) [ ] 0/3\r (no-eol) (esc)
325 archiving (foo) [ ] 0/3\r (no-eol) (esc)
326 archiving (foo) [ ] 0/3\r (no-eol) (esc)
326 archiving (foo) [ ] 0/3\r (no-eol) (esc)
327 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
327 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
328 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
328 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
329 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
329 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
330 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
330 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
331 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
331 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
332 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
332 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
333 \r (no-eol) (esc)
333 \r (no-eol) (esc)
334 \r (no-eol) (esc)
334 \r (no-eol) (esc)
335 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
335 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
336 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
336 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
337 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
337 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
338 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
338 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
339 \r (no-eol) (esc)
339 \r (no-eol) (esc)
340
340
341 Test archiving a revision that references a subrepo that is not yet
341 Test archiving a revision that references a subrepo that is not yet
342 cloned:
342 cloned:
343
343
344 $ hg clone -U . ../empty
344 $ hg clone -U . ../empty
345 \r (no-eol) (esc)
345 \r (no-eol) (esc)
346 linking [ <=> ] 1\r (no-eol) (esc)
346 linking [ <=> ] 1\r (no-eol) (esc)
347 linking [ <=> ] 2\r (no-eol) (esc)
347 linking [ <=> ] 2\r (no-eol) (esc)
348 linking [ <=> ] 3\r (no-eol) (esc)
348 linking [ <=> ] 3\r (no-eol) (esc)
349 linking [ <=> ] 4\r (no-eol) (esc)
349 linking [ <=> ] 4\r (no-eol) (esc)
350 linking [ <=> ] 5\r (no-eol) (esc)
350 linking [ <=> ] 5\r (no-eol) (esc)
351 linking [ <=> ] 6\r (no-eol) (esc)
351 linking [ <=> ] 6\r (no-eol) (esc)
352 linking [ <=> ] 7\r (no-eol) (esc)
352 linking [ <=> ] 7\r (no-eol) (esc)
353 linking [ <=> ] 8\r (no-eol) (esc)
353 linking [ <=> ] 8\r (no-eol) (esc)
354 \r (no-eol) (esc)
354 \r (no-eol) (esc)
355 $ cd ../empty
355 $ cd ../empty
356 $ hg archive --subrepos -r tip ../archive.tar.gz
356 $ hg archive --subrepos -r tip ../archive.tar.gz
357 \r (no-eol) (esc)
357 \r (no-eol) (esc)
358 archiving [ ] 0/3\r (no-eol) (esc)
358 archiving [ ] 0/3\r (no-eol) (esc)
359 archiving [ ] 0/3\r (no-eol) (esc)
359 archiving [ ] 0/3\r (no-eol) (esc)
360 archiving [=============> ] 1/3\r (no-eol) (esc)
360 archiving [=============> ] 1/3\r (no-eol) (esc)
361 archiving [=============> ] 1/3\r (no-eol) (esc)
361 archiving [=============> ] 1/3\r (no-eol) (esc)
362 archiving [===========================> ] 2/3\r (no-eol) (esc)
362 archiving [===========================> ] 2/3\r (no-eol) (esc)
363 archiving [===========================> ] 2/3\r (no-eol) (esc)
363 archiving [===========================> ] 2/3\r (no-eol) (esc)
364 archiving [==========================================>] 3/3\r (no-eol) (esc)
364 archiving [==========================================>] 3/3\r (no-eol) (esc)
365 archiving [==========================================>] 3/3\r (no-eol) (esc)
365 archiving [==========================================>] 3/3\r (no-eol) (esc)
366 \r (no-eol) (esc)
366 \r (no-eol) (esc)
367 \r (no-eol) (esc)
367 \r (no-eol) (esc)
368 linking [ <=> ] 1\r (no-eol) (esc)
368 linking [ <=> ] 1\r (no-eol) (esc)
369 linking [ <=> ] 2\r (no-eol) (esc)
369 linking [ <=> ] 2\r (no-eol) (esc)
370 linking [ <=> ] 3\r (no-eol) (esc)
370 linking [ <=> ] 3\r (no-eol) (esc)
371 linking [ <=> ] 4\r (no-eol) (esc)
371 linking [ <=> ] 4\r (no-eol) (esc)
372 linking [ <=> ] 5\r (no-eol) (esc)
372 linking [ <=> ] 5\r (no-eol) (esc)
373 linking [ <=> ] 6\r (no-eol) (esc)
373 linking [ <=> ] 6\r (no-eol) (esc)
374 linking [ <=> ] 7\r (no-eol) (esc)
374 linking [ <=> ] 7\r (no-eol) (esc)
375 linking [ <=> ] 8\r (no-eol) (esc)
375 linking [ <=> ] 8\r (no-eol) (esc)
376 \r (no-eol) (esc)
376 \r (no-eol) (esc)
377 \r (no-eol) (esc)
377 \r (no-eol) (esc)
378 archiving (foo) [ ] 0/3\r (no-eol) (esc)
378 archiving (foo) [ ] 0/3\r (no-eol) (esc)
379 archiving (foo) [ ] 0/3\r (no-eol) (esc)
379 archiving (foo) [ ] 0/3\r (no-eol) (esc)
380 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
380 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
381 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
381 archiving (foo) [===========> ] 1/3\r (no-eol) (esc)
382 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
382 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
383 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
383 archiving (foo) [=======================> ] 2/3\r (no-eol) (esc)
384 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
384 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
385 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
385 archiving (foo) [====================================>] 3/3\r (no-eol) (esc)
386 \r (no-eol) (esc)
386 \r (no-eol) (esc)
387 \r (no-eol) (esc)
387 \r (no-eol) (esc)
388 linking [ <=> ] 1\r (no-eol) (esc)
388 linking [ <=> ] 1\r (no-eol) (esc)
389 linking [ <=> ] 2\r (no-eol) (esc)
389 linking [ <=> ] 2\r (no-eol) (esc)
390 linking [ <=> ] 3\r (no-eol) (esc)
390 linking [ <=> ] 3\r (no-eol) (esc)
391 linking [ <=> ] 4\r (no-eol) (esc)
391 linking [ <=> ] 4\r (no-eol) (esc)
392 linking [ <=> ] 5\r (no-eol) (esc)
392 linking [ <=> ] 5\r (no-eol) (esc)
393 linking [ <=> ] 6\r (no-eol) (esc)
393 linking [ <=> ] 6\r (no-eol) (esc)
394 \r (no-eol) (esc)
394 \r (no-eol) (esc)
395 \r (no-eol) (esc)
395 \r (no-eol) (esc)
396 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
396 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
397 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
397 archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc)
398 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
398 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
399 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
399 archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc)
400 \r (no-eol) (esc)
400 \r (no-eol) (esc)
401 cloning subrepo foo from $TESTTMP/repo/foo
401 cloning subrepo foo from $TESTTMP/repo/foo
402 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
402 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
403
403
404 The newly cloned subrepos contain no working copy:
404 The newly cloned subrepos contain no working copy:
405
405
406 $ hg -R foo summary
406 $ hg -R foo summary
407 parent: -1:000000000000 (no revision checked out)
407 parent: -1:000000000000 (no revision checked out)
408 branch: default
408 branch: default
409 commit: (clean)
409 commit: (clean)
410 update: 4 new changesets (update)
410 update: 4 new changesets (update)
411
411
412 Disable progress extension and cleanup:
412 Disable progress extension and cleanup:
413
413
414 $ mv $HGRCPATH.no-progress $HGRCPATH
414 $ mv $HGRCPATH.no-progress $HGRCPATH
415
415
416 Test archiving when there is a directory in the way for a subrepo
416 Test archiving when there is a directory in the way for a subrepo
417 created by archive:
417 created by archive:
418
418
419 $ hg clone -U . ../almost-empty
419 $ hg clone -U . ../almost-empty
420 $ cd ../almost-empty
420 $ cd ../almost-empty
421 $ mkdir foo
421 $ mkdir foo
422 $ echo f > foo/f
422 $ echo f > foo/f
423 $ hg archive --subrepos -r tip archive
423 $ hg archive --subrepos -r tip archive
424 cloning subrepo foo from $TESTTMP/empty/foo
424 cloning subrepo foo from $TESTTMP/empty/foo
425 abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
425 abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
426 [255]
426 [255]
427
427
428 Clone and test outgoing:
428 Clone and test outgoing:
429
429
430 $ cd ..
430 $ cd ..
431 $ hg clone repo repo2
431 $ hg clone repo repo2
432 updating to branch default
432 updating to branch default
433 cloning subrepo foo from $TESTTMP/repo/foo
433 cloning subrepo foo from $TESTTMP/repo/foo
434 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
434 cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob)
435 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ cd repo2
436 $ cd repo2
437 $ hg outgoing -S
437 $ hg outgoing -S
438 comparing with $TESTTMP/repo (glob)
438 comparing with $TESTTMP/repo (glob)
439 searching for changes
439 searching for changes
440 no changes found
440 no changes found
441 comparing with $TESTTMP/repo/foo
441 comparing with $TESTTMP/repo/foo
442 searching for changes
442 searching for changes
443 no changes found
443 no changes found
444 comparing with $TESTTMP/repo/foo/bar
444 comparing with $TESTTMP/repo/foo/bar
445 searching for changes
445 searching for changes
446 no changes found
446 no changes found
447 [1]
447 [1]
448
448
449 Make nested change:
449 Make nested change:
450
450
451 $ echo y4 >> foo/y.txt
451 $ echo y4 >> foo/y.txt
452 $ hg diff --nodates -S
452 $ hg diff --nodates -S
453 diff -r 65903cebad86 foo/y.txt
453 diff -r 65903cebad86 foo/y.txt
454 --- a/foo/y.txt
454 --- a/foo/y.txt
455 +++ b/foo/y.txt
455 +++ b/foo/y.txt
456 @@ -1,3 +1,4 @@
456 @@ -1,3 +1,4 @@
457 y1
457 y1
458 y2
458 y2
459 y3
459 y3
460 +y4
460 +y4
461 $ hg commit --subrepos -m 3-4-2
461 $ hg commit --subrepos -m 3-4-2
462 committing subrepository foo
462 committing subrepository foo
463 $ hg outgoing -S
463 $ hg outgoing -S
464 comparing with $TESTTMP/repo (glob)
464 comparing with $TESTTMP/repo (glob)
465 searching for changes
465 searching for changes
466 changeset: 3:2655b8ecc4ee
466 changeset: 3:2655b8ecc4ee
467 tag: tip
467 tag: tip
468 user: test
468 user: test
469 date: Thu Jan 01 00:00:00 1970 +0000
469 date: Thu Jan 01 00:00:00 1970 +0000
470 summary: 3-4-2
470 summary: 3-4-2
471
471
472 comparing with $TESTTMP/repo/foo
472 comparing with $TESTTMP/repo/foo
473 searching for changes
473 searching for changes
474 changeset: 4:e96193d6cb36
474 changeset: 4:e96193d6cb36
475 tag: tip
475 tag: tip
476 user: test
476 user: test
477 date: Thu Jan 01 00:00:00 1970 +0000
477 date: Thu Jan 01 00:00:00 1970 +0000
478 summary: 3-4-2
478 summary: 3-4-2
479
479
480 comparing with $TESTTMP/repo/foo/bar
480 comparing with $TESTTMP/repo/foo/bar
481 searching for changes
481 searching for changes
482 no changes found
482 no changes found
483
483
484
484
485 Switch to original repo and setup default path:
485 Switch to original repo and setup default path:
486
486
487 $ cd ../repo
487 $ cd ../repo
488 $ echo '[paths]' >> .hg/hgrc
488 $ echo '[paths]' >> .hg/hgrc
489 $ echo 'default = ../repo2' >> .hg/hgrc
489 $ echo 'default = ../repo2' >> .hg/hgrc
490
490
491 Test incoming:
491 Test incoming:
492
492
493 $ hg incoming -S
493 $ hg incoming -S
494 comparing with $TESTTMP/repo2 (glob)
494 comparing with $TESTTMP/repo2 (glob)
495 searching for changes
495 searching for changes
496 changeset: 3:2655b8ecc4ee
496 changeset: 3:2655b8ecc4ee
497 tag: tip
497 tag: tip
498 user: test
498 user: test
499 date: Thu Jan 01 00:00:00 1970 +0000
499 date: Thu Jan 01 00:00:00 1970 +0000
500 summary: 3-4-2
500 summary: 3-4-2
501
501
502 comparing with $TESTTMP/repo2/foo
502 comparing with $TESTTMP/repo2/foo
503 searching for changes
503 searching for changes
504 changeset: 4:e96193d6cb36
504 changeset: 4:e96193d6cb36
505 tag: tip
505 tag: tip
506 user: test
506 user: test
507 date: Thu Jan 01 00:00:00 1970 +0000
507 date: Thu Jan 01 00:00:00 1970 +0000
508 summary: 3-4-2
508 summary: 3-4-2
509
509
510 comparing with $TESTTMP/repo2/foo/bar
510 comparing with $TESTTMP/repo2/foo/bar
511 searching for changes
511 searching for changes
512 no changes found
512 no changes found
513
513
514 $ hg incoming -S --bundle incoming.hg
514 $ hg incoming -S --bundle incoming.hg
515 abort: cannot combine --bundle and --subrepos
515 abort: cannot combine --bundle and --subrepos
516 [255]
516 [255]
517
517
518 Test missing subrepo:
518 Test missing subrepo:
519
519
520 $ rm -r foo
520 $ rm -r foo
521 $ hg status -S
521 $ hg status -S
522 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
522 warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo"
523
523
524 Issue2619: IndexError: list index out of range on hg add with subrepos
524 Issue2619: IndexError: list index out of range on hg add with subrepos
525 The subrepo must sorts after the explicit filename.
525 The subrepo must sorts after the explicit filename.
526
526
527 $ cd ..
527 $ cd ..
528 $ hg init test
528 $ hg init test
529 $ cd test
529 $ cd test
530 $ hg init x
530 $ hg init x
531 $ echo abc > abc.txt
531 $ echo abc > abc.txt
532 $ hg ci -Am "abc"
532 $ hg ci -Am "abc"
533 adding abc.txt
533 adding abc.txt
534 $ echo "x = x" >> .hgsub
534 $ echo "x = x" >> .hgsub
535 $ hg add .hgsub
535 $ hg add .hgsub
536 $ touch a x/a
536 $ touch a x/a
537 $ hg add a x/a
537 $ hg add a x/a
538
538
539 $ hg ci -Sm "added x"
539 $ hg ci -Sm "added x"
540 committing subrepository x
540 committing subrepository x
541 $ echo abc > x/a
541 $ echo abc > x/a
542 $ hg revert --rev '.^' "set:subrepo('glob:x*')"
542 $ hg revert --rev '.^' "set:subrepo('glob:x*')"
543 abort: subrepository 'x' does not exist in 25ac2c9b3180!
543 abort: subrepository 'x' does not exist in 25ac2c9b3180!
544 [255]
544 [255]
545
545
546 $ cd ..
546 $ cd ..
@@ -1,1618 +1,1618 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 $ hg ci -m1
41 $ hg ci -m1
42
42
43 test handling .hgsubstate "added" explicitly.
43 test handling .hgsubstate "added" explicitly.
44
44
45 $ hg parents --template '{node}\n{files}\n'
45 $ hg parents --template '{node}\n{files}\n'
46 7cf8cfea66e410e8e3336508dfeec07b3192de51
46 7cf8cfea66e410e8e3336508dfeec07b3192de51
47 .hgsub .hgsubstate
47 .hgsub .hgsubstate
48 $ hg rollback -q
48 $ hg rollback -q
49 $ hg add .hgsubstate
49 $ hg add .hgsubstate
50 $ hg ci -m1
50 $ hg ci -m1
51 $ hg parents --template '{node}\n{files}\n'
51 $ hg parents --template '{node}\n{files}\n'
52 7cf8cfea66e410e8e3336508dfeec07b3192de51
52 7cf8cfea66e410e8e3336508dfeec07b3192de51
53 .hgsub .hgsubstate
53 .hgsub .hgsubstate
54
54
55 Revert subrepo and test subrepo fileset keyword:
55 Revert subrepo and test subrepo fileset keyword:
56
56
57 $ echo b > s/a
57 $ echo b > s/a
58 $ hg revert --dry-run "set:subrepo('glob:s*')"
58 $ hg revert --dry-run "set:subrepo('glob:s*')"
59 reverting subrepo s
59 reverting subrepo s
60 reverting s/a (glob)
60 reverting s/a (glob)
61 $ cat s/a
61 $ cat s/a
62 b
62 b
63 $ hg revert "set:subrepo('glob:s*')"
63 $ hg revert "set:subrepo('glob:s*')"
64 reverting subrepo s
64 reverting subrepo s
65 reverting s/a (glob)
65 reverting s/a (glob)
66 $ cat s/a
66 $ cat s/a
67 a
67 a
68 $ rm s/a.orig
68 $ rm s/a.orig
69
69
70 Revert subrepo with no backup. The "reverting s/a" line is gone since
70 Revert subrepo with no backup. The "reverting s/a" line is gone since
71 we're really running 'hg update' in the subrepo:
71 we're really running 'hg update' in the subrepo:
72
72
73 $ echo b > s/a
73 $ echo b > s/a
74 $ hg revert --no-backup s
74 $ hg revert --no-backup s
75 reverting subrepo s
75 reverting subrepo s
76
76
77 Issue2022: update -C
77 Issue2022: update -C
78
78
79 $ echo b > s/a
79 $ echo b > s/a
80 $ hg sum
80 $ hg sum
81 parent: 1:7cf8cfea66e4 tip
81 parent: 1:7cf8cfea66e4 tip
82 1
82 1
83 branch: default
83 branch: default
84 commit: 1 subrepos
84 commit: 1 subrepos
85 update: (current)
85 update: (current)
86 $ hg co -C 1
86 $ hg co -C 1
87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 $ hg sum
88 $ hg sum
89 parent: 1:7cf8cfea66e4 tip
89 parent: 1:7cf8cfea66e4 tip
90 1
90 1
91 branch: default
91 branch: default
92 commit: (clean)
92 commit: (clean)
93 update: (current)
93 update: (current)
94
94
95 commands that require a clean repo should respect subrepos
95 commands that require a clean repo should respect subrepos
96
96
97 $ echo b >> s/a
97 $ echo b >> s/a
98 $ hg backout tip
98 $ hg backout tip
99 abort: uncommitted changes in subrepo s
99 abort: uncommitted changes in subrepo s
100 [255]
100 [255]
101 $ hg revert -C -R s s/a
101 $ hg revert -C -R s s/a
102
102
103 add sub sub
103 add sub sub
104
104
105 $ echo ss = ss > s/.hgsub
105 $ echo ss = ss > s/.hgsub
106 $ hg init s/ss
106 $ hg init s/ss
107 $ echo a > s/ss/a
107 $ echo a > s/ss/a
108 $ hg -R s add s/.hgsub
108 $ hg -R s add s/.hgsub
109 $ hg -R s/ss add s/ss/a
109 $ hg -R s/ss add s/ss/a
110 $ hg sum
110 $ hg sum
111 parent: 1:7cf8cfea66e4 tip
111 parent: 1:7cf8cfea66e4 tip
112 1
112 1
113 branch: default
113 branch: default
114 commit: 1 subrepos
114 commit: 1 subrepos
115 update: (current)
115 update: (current)
116 $ hg ci -m2
116 $ hg ci -m2
117 committing subrepository s
117 committing subrepository s
118 committing subrepository s/ss (glob)
118 committing subrepository s/ss (glob)
119 $ hg sum
119 $ hg sum
120 parent: 2:df30734270ae tip
120 parent: 2:df30734270ae tip
121 2
121 2
122 branch: default
122 branch: default
123 commit: (clean)
123 commit: (clean)
124 update: (current)
124 update: (current)
125
125
126 test handling .hgsubstate "modified" explicitly.
126 test handling .hgsubstate "modified" explicitly.
127
127
128 $ hg parents --template '{node}\n{files}\n'
128 $ hg parents --template '{node}\n{files}\n'
129 df30734270ae757feb35e643b7018e818e78a9aa
129 df30734270ae757feb35e643b7018e818e78a9aa
130 .hgsubstate
130 .hgsubstate
131 $ hg rollback -q
131 $ hg rollback -q
132 $ hg status -A .hgsubstate
132 $ hg status -A .hgsubstate
133 M .hgsubstate
133 M .hgsubstate
134 $ hg ci -m2
134 $ hg ci -m2
135 $ hg parents --template '{node}\n{files}\n'
135 $ hg parents --template '{node}\n{files}\n'
136 df30734270ae757feb35e643b7018e818e78a9aa
136 df30734270ae757feb35e643b7018e818e78a9aa
137 .hgsubstate
137 .hgsubstate
138
138
139 bump sub rev (and check it is ignored by ui.commitsubrepos)
139 bump sub rev (and check it is ignored by ui.commitsubrepos)
140
140
141 $ echo b > s/a
141 $ echo b > s/a
142 $ hg -R s ci -ms1
142 $ hg -R s ci -ms1
143 $ hg --config ui.commitsubrepos=no ci -m3
143 $ hg --config ui.commitsubrepos=no ci -m3
144
144
145 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
145 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
146
146
147 $ echo c > s/a
147 $ echo c > s/a
148 $ hg --config ui.commitsubrepos=no ci -m4
148 $ hg --config ui.commitsubrepos=no ci -m4
149 abort: uncommitted changes in subrepo s
149 abort: uncommitted changes in subrepository 's'
150 (use --subrepos for recursive commit)
150 (use --subrepos for recursive commit)
151 [255]
151 [255]
152 $ hg id
152 $ hg id
153 f6affe3fbfaa+ tip
153 f6affe3fbfaa+ tip
154 $ hg -R s ci -mc
154 $ hg -R s ci -mc
155 $ hg id
155 $ hg id
156 f6affe3fbfaa+ tip
156 f6affe3fbfaa+ tip
157 $ echo d > s/a
157 $ echo d > s/a
158 $ hg ci -m4
158 $ hg ci -m4
159 committing subrepository s
159 committing subrepository s
160 $ hg tip -R s
160 $ hg tip -R s
161 changeset: 4:02dcf1d70411
161 changeset: 4:02dcf1d70411
162 tag: tip
162 tag: tip
163 user: test
163 user: test
164 date: Thu Jan 01 00:00:00 1970 +0000
164 date: Thu Jan 01 00:00:00 1970 +0000
165 summary: 4
165 summary: 4
166
166
167
167
168 check caching
168 check caching
169
169
170 $ hg co 0
170 $ hg co 0
171 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
171 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
172 $ hg debugsub
172 $ hg debugsub
173
173
174 restore
174 restore
175
175
176 $ hg co
176 $ hg co
177 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 $ hg debugsub
178 $ hg debugsub
179 path s
179 path s
180 source s
180 source s
181 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
181 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
182
182
183 new branch for merge tests
183 new branch for merge tests
184
184
185 $ hg co 1
185 $ hg co 1
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 $ echo t = t >> .hgsub
187 $ echo t = t >> .hgsub
188 $ hg init t
188 $ hg init t
189 $ echo t > t/t
189 $ echo t > t/t
190 $ hg -R t add t
190 $ hg -R t add t
191 adding t/t (glob)
191 adding t/t (glob)
192
192
193 5
193 5
194
194
195 $ hg ci -m5 # add sub
195 $ hg ci -m5 # add sub
196 committing subrepository t
196 committing subrepository t
197 created new head
197 created new head
198 $ echo t2 > t/t
198 $ echo t2 > t/t
199
199
200 6
200 6
201
201
202 $ hg st -R s
202 $ hg st -R s
203 $ hg ci -m6 # change sub
203 $ hg ci -m6 # change sub
204 committing subrepository t
204 committing subrepository t
205 $ hg debugsub
205 $ hg debugsub
206 path s
206 path s
207 source s
207 source s
208 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
208 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
209 path t
209 path t
210 source t
210 source t
211 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
211 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
212 $ echo t3 > t/t
212 $ echo t3 > t/t
213
213
214 7
214 7
215
215
216 $ hg ci -m7 # change sub again for conflict test
216 $ hg ci -m7 # change sub again for conflict test
217 committing subrepository t
217 committing subrepository t
218 $ hg rm .hgsub
218 $ hg rm .hgsub
219
219
220 8
220 8
221
221
222 $ hg ci -m8 # remove sub
222 $ hg ci -m8 # remove sub
223
223
224 test handling .hgsubstate "removed" explicitly.
224 test handling .hgsubstate "removed" explicitly.
225
225
226 $ hg parents --template '{node}\n{files}\n'
226 $ hg parents --template '{node}\n{files}\n'
227 96615c1dad2dc8e3796d7332c77ce69156f7b78e
227 96615c1dad2dc8e3796d7332c77ce69156f7b78e
228 .hgsub .hgsubstate
228 .hgsub .hgsubstate
229 $ hg rollback -q
229 $ hg rollback -q
230 $ hg remove .hgsubstate
230 $ hg remove .hgsubstate
231 $ hg ci -m8
231 $ hg ci -m8
232 $ hg parents --template '{node}\n{files}\n'
232 $ hg parents --template '{node}\n{files}\n'
233 96615c1dad2dc8e3796d7332c77ce69156f7b78e
233 96615c1dad2dc8e3796d7332c77ce69156f7b78e
234 .hgsub .hgsubstate
234 .hgsub .hgsubstate
235
235
236 merge tests
236 merge tests
237
237
238 $ hg co -C 3
238 $ hg co -C 3
239 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
240 $ hg merge 5 # test adding
240 $ hg merge 5 # test adding
241 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 (branch merge, don't forget to commit)
242 (branch merge, don't forget to commit)
243 $ hg debugsub
243 $ hg debugsub
244 path s
244 path s
245 source s
245 source s
246 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
246 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
247 path t
247 path t
248 source t
248 source t
249 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
249 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
250 $ hg ci -m9
250 $ hg ci -m9
251 created new head
251 created new head
252 $ hg merge 6 --debug # test change
252 $ hg merge 6 --debug # test change
253 searching for copies back to rev 2
253 searching for copies back to rev 2
254 resolving manifests
254 resolving manifests
255 branchmerge: True, force: False, partial: False
255 branchmerge: True, force: False, partial: False
256 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
256 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
257 .hgsubstate: versions differ -> m
257 .hgsubstate: versions differ -> m
258 updating: .hgsubstate 1/1 files (100.00%)
258 updating: .hgsubstate 1/1 files (100.00%)
259 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
259 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
260 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
260 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
261 getting subrepo t
261 getting subrepo t
262 resolving manifests
262 resolving manifests
263 branchmerge: False, force: False, partial: False
263 branchmerge: False, force: False, partial: False
264 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
264 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
265 t: remote is newer -> g
265 t: remote is newer -> g
266 getting t
266 getting t
267 updating: t 1/1 files (100.00%)
267 updating: t 1/1 files (100.00%)
268 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
268 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 (branch merge, don't forget to commit)
269 (branch merge, don't forget to commit)
270 $ hg debugsub
270 $ hg debugsub
271 path s
271 path s
272 source s
272 source s
273 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
273 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
274 path t
274 path t
275 source t
275 source t
276 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
276 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
277 $ echo conflict > t/t
277 $ echo conflict > t/t
278 $ hg ci -m10
278 $ hg ci -m10
279 committing subrepository t
279 committing subrepository t
280 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
280 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
281 searching for copies back to rev 2
281 searching for copies back to rev 2
282 resolving manifests
282 resolving manifests
283 branchmerge: True, force: False, partial: False
283 branchmerge: True, force: False, partial: False
284 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
284 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
285 .hgsubstate: versions differ -> m
285 .hgsubstate: versions differ -> m
286 updating: .hgsubstate 1/1 files (100.00%)
286 updating: .hgsubstate 1/1 files (100.00%)
287 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
287 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
288 subrepo t: both sides changed
288 subrepo t: both sides changed
289 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
289 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
290 (M)erge, keep (l)ocal or keep (r)emote? m
290 (M)erge, keep (l)ocal or keep (r)emote? m
291 merging subrepo t
291 merging subrepo t
292 searching for copies back to rev 2
292 searching for copies back to rev 2
293 resolving manifests
293 resolving manifests
294 branchmerge: True, force: False, partial: False
294 branchmerge: True, force: False, partial: False
295 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
295 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
296 preserving t for resolve of t
296 preserving t for resolve of t
297 t: versions differ -> m
297 t: versions differ -> m
298 updating: t 1/1 files (100.00%)
298 updating: t 1/1 files (100.00%)
299 picked tool 'internal:merge' for t (binary False symlink False)
299 picked tool 'internal:merge' for t (binary False symlink False)
300 merging t
300 merging t
301 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
301 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
302 warning: conflicts during merge.
302 warning: conflicts during merge.
303 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
303 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
304 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
304 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
305 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
305 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
306 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
306 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 (branch merge, don't forget to commit)
308 (branch merge, don't forget to commit)
309
309
310 should conflict
310 should conflict
311
311
312 $ cat t/t
312 $ cat t/t
313 <<<<<<< local: 20a0db6fbf6c - test: 10
313 <<<<<<< local: 20a0db6fbf6c - test: 10
314 conflict
314 conflict
315 =======
315 =======
316 t3
316 t3
317 >>>>>>> other: 7af322bc1198 - test: 7
317 >>>>>>> other: 7af322bc1198 - test: 7
318
318
319 11: remove subrepo t
319 11: remove subrepo t
320
320
321 $ hg co -C 5
321 $ hg co -C 5
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 $ hg revert -r 4 .hgsub # remove t
323 $ hg revert -r 4 .hgsub # remove t
324 $ hg ci -m11
324 $ hg ci -m11
325 created new head
325 created new head
326 $ hg debugsub
326 $ hg debugsub
327 path s
327 path s
328 source s
328 source s
329 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
329 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
330
330
331 local removed, remote changed, keep changed
331 local removed, remote changed, keep changed
332
332
333 $ hg merge 6
333 $ hg merge 6
334 remote changed subrepository t which local removed
334 remote changed subrepository t which local removed
335 use (c)hanged version or (d)elete? c
335 use (c)hanged version or (d)elete? c
336 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
336 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 (branch merge, don't forget to commit)
337 (branch merge, don't forget to commit)
338 BROKEN: should include subrepo t
338 BROKEN: should include subrepo t
339 $ hg debugsub
339 $ hg debugsub
340 path s
340 path s
341 source s
341 source s
342 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
342 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
343 $ cat .hgsubstate
343 $ cat .hgsubstate
344 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
344 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
345 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
345 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
346 $ hg ci -m 'local removed, remote changed, keep changed'
346 $ hg ci -m 'local removed, remote changed, keep changed'
347 BROKEN: should include subrepo t
347 BROKEN: should include subrepo t
348 $ hg debugsub
348 $ hg debugsub
349 path s
349 path s
350 source s
350 source s
351 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
351 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
352 BROKEN: should include subrepo t
352 BROKEN: should include subrepo t
353 $ cat .hgsubstate
353 $ cat .hgsubstate
354 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
354 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
355 $ cat t/t
355 $ cat t/t
356 t2
356 t2
357
357
358 local removed, remote changed, keep removed
358 local removed, remote changed, keep removed
359
359
360 $ hg co -C 11
360 $ hg co -C 11
361 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
361 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 $ hg merge --config ui.interactive=true 6 <<EOF
362 $ hg merge --config ui.interactive=true 6 <<EOF
363 > d
363 > d
364 > EOF
364 > EOF
365 remote changed subrepository t which local removed
365 remote changed subrepository t which local removed
366 use (c)hanged version or (d)elete? d
366 use (c)hanged version or (d)elete? d
367 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 (branch merge, don't forget to commit)
368 (branch merge, don't forget to commit)
369 $ hg debugsub
369 $ hg debugsub
370 path s
370 path s
371 source s
371 source s
372 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
372 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
373 $ cat .hgsubstate
373 $ cat .hgsubstate
374 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
374 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
375 $ hg ci -m 'local removed, remote changed, keep removed'
375 $ hg ci -m 'local removed, remote changed, keep removed'
376 created new head
376 created new head
377 $ hg debugsub
377 $ hg debugsub
378 path s
378 path s
379 source s
379 source s
380 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
380 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
381 $ cat .hgsubstate
381 $ cat .hgsubstate
382 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
382 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
383
383
384 local changed, remote removed, keep changed
384 local changed, remote removed, keep changed
385
385
386 $ hg co -C 6
386 $ hg co -C 6
387 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
388 $ hg merge 11
388 $ hg merge 11
389 local changed subrepository t which remote removed
389 local changed subrepository t which remote removed
390 use (c)hanged version or (d)elete? c
390 use (c)hanged version or (d)elete? c
391 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 (branch merge, don't forget to commit)
392 (branch merge, don't forget to commit)
393 BROKEN: should include subrepo t
393 BROKEN: should include subrepo t
394 $ hg debugsub
394 $ hg debugsub
395 path s
395 path s
396 source s
396 source s
397 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
397 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
398 BROKEN: should include subrepo t
398 BROKEN: should include subrepo t
399 $ cat .hgsubstate
399 $ cat .hgsubstate
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
401 $ hg ci -m 'local changed, remote removed, keep changed'
401 $ hg ci -m 'local changed, remote removed, keep changed'
402 created new head
402 created new head
403 BROKEN: should include subrepo t
403 BROKEN: should include subrepo t
404 $ hg debugsub
404 $ hg debugsub
405 path s
405 path s
406 source s
406 source s
407 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
407 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
408 BROKEN: should include subrepo t
408 BROKEN: should include subrepo t
409 $ cat .hgsubstate
409 $ cat .hgsubstate
410 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
410 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
411 $ cat t/t
411 $ cat t/t
412 t2
412 t2
413
413
414 local changed, remote removed, keep removed
414 local changed, remote removed, keep removed
415
415
416 $ hg co -C 6
416 $ hg co -C 6
417 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
418 $ hg merge --config ui.interactive=true 11 <<EOF
418 $ hg merge --config ui.interactive=true 11 <<EOF
419 > d
419 > d
420 > EOF
420 > EOF
421 local changed subrepository t which remote removed
421 local changed subrepository t which remote removed
422 use (c)hanged version or (d)elete? d
422 use (c)hanged version or (d)elete? d
423 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 (branch merge, don't forget to commit)
424 (branch merge, don't forget to commit)
425 $ hg debugsub
425 $ hg debugsub
426 path s
426 path s
427 source s
427 source s
428 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
428 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
429 $ cat .hgsubstate
429 $ cat .hgsubstate
430 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
430 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
431 $ hg ci -m 'local changed, remote removed, keep removed'
431 $ hg ci -m 'local changed, remote removed, keep removed'
432 created new head
432 created new head
433 $ hg debugsub
433 $ hg debugsub
434 path s
434 path s
435 source s
435 source s
436 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
436 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
437 $ cat .hgsubstate
437 $ cat .hgsubstate
438 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
438 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
439
439
440 clean up to avoid having to fix up the tests below
440 clean up to avoid having to fix up the tests below
441
441
442 $ hg co -C 10
442 $ hg co -C 10
443 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
444 $ cat >> $HGRCPATH <<EOF
444 $ cat >> $HGRCPATH <<EOF
445 > [extensions]
445 > [extensions]
446 > strip=
446 > strip=
447 > EOF
447 > EOF
448 $ hg strip -r 11:15
448 $ hg strip -r 11:15
449 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
449 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
450
450
451 clone
451 clone
452
452
453 $ cd ..
453 $ cd ..
454 $ hg clone t tc
454 $ hg clone t tc
455 updating to branch default
455 updating to branch default
456 cloning subrepo s from $TESTTMP/t/s
456 cloning subrepo s from $TESTTMP/t/s
457 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
457 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
458 cloning subrepo t from $TESTTMP/t/t
458 cloning subrepo t from $TESTTMP/t/t
459 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 $ cd tc
460 $ cd tc
461 $ hg debugsub
461 $ hg debugsub
462 path s
462 path s
463 source s
463 source s
464 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
464 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
465 path t
465 path t
466 source t
466 source t
467 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
467 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
468
468
469 push
469 push
470
470
471 $ echo bah > t/t
471 $ echo bah > t/t
472 $ hg ci -m11
472 $ hg ci -m11
473 committing subrepository t
473 committing subrepository t
474 $ hg push
474 $ hg push
475 pushing to $TESTTMP/t (glob)
475 pushing to $TESTTMP/t (glob)
476 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
476 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
477 no changes made to subrepo s since last push to $TESTTMP/t/s
477 no changes made to subrepo s since last push to $TESTTMP/t/s
478 pushing subrepo t to $TESTTMP/t/t
478 pushing subrepo t to $TESTTMP/t/t
479 searching for changes
479 searching for changes
480 adding changesets
480 adding changesets
481 adding manifests
481 adding manifests
482 adding file changes
482 adding file changes
483 added 1 changesets with 1 changes to 1 files
483 added 1 changesets with 1 changes to 1 files
484 searching for changes
484 searching for changes
485 adding changesets
485 adding changesets
486 adding manifests
486 adding manifests
487 adding file changes
487 adding file changes
488 added 1 changesets with 1 changes to 1 files
488 added 1 changesets with 1 changes to 1 files
489
489
490 push -f
490 push -f
491
491
492 $ echo bah > s/a
492 $ echo bah > s/a
493 $ hg ci -m12
493 $ hg ci -m12
494 committing subrepository s
494 committing subrepository s
495 $ hg push
495 $ hg push
496 pushing to $TESTTMP/t (glob)
496 pushing to $TESTTMP/t (glob)
497 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
497 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
498 pushing subrepo s to $TESTTMP/t/s
498 pushing subrepo s to $TESTTMP/t/s
499 searching for changes
499 searching for changes
500 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
500 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
501 (merge or see "hg help push" for details about pushing new heads)
501 (merge or see "hg help push" for details about pushing new heads)
502 [255]
502 [255]
503 $ hg push -f
503 $ hg push -f
504 pushing to $TESTTMP/t (glob)
504 pushing to $TESTTMP/t (glob)
505 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
505 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
506 searching for changes
506 searching for changes
507 no changes found
507 no changes found
508 pushing subrepo s to $TESTTMP/t/s
508 pushing subrepo s to $TESTTMP/t/s
509 searching for changes
509 searching for changes
510 adding changesets
510 adding changesets
511 adding manifests
511 adding manifests
512 adding file changes
512 adding file changes
513 added 1 changesets with 1 changes to 1 files (+1 heads)
513 added 1 changesets with 1 changes to 1 files (+1 heads)
514 pushing subrepo t to $TESTTMP/t/t
514 pushing subrepo t to $TESTTMP/t/t
515 searching for changes
515 searching for changes
516 no changes found
516 no changes found
517 searching for changes
517 searching for changes
518 adding changesets
518 adding changesets
519 adding manifests
519 adding manifests
520 adding file changes
520 adding file changes
521 added 1 changesets with 1 changes to 1 files
521 added 1 changesets with 1 changes to 1 files
522
522
523 check that unmodified subrepos are not pushed
523 check that unmodified subrepos are not pushed
524
524
525 $ hg clone . ../tcc
525 $ hg clone . ../tcc
526 updating to branch default
526 updating to branch default
527 cloning subrepo s from $TESTTMP/tc/s
527 cloning subrepo s from $TESTTMP/tc/s
528 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
528 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
529 cloning subrepo t from $TESTTMP/tc/t
529 cloning subrepo t from $TESTTMP/tc/t
530 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
530 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
531
531
532 the subrepos on the new clone have nothing to push to its source
532 the subrepos on the new clone have nothing to push to its source
533
533
534 $ hg push -R ../tcc .
534 $ hg push -R ../tcc .
535 pushing to .
535 pushing to .
536 no changes made to subrepo s/ss since last push to s/ss (glob)
536 no changes made to subrepo s/ss since last push to s/ss (glob)
537 no changes made to subrepo s since last push to s
537 no changes made to subrepo s since last push to s
538 no changes made to subrepo t since last push to t
538 no changes made to subrepo t since last push to t
539 searching for changes
539 searching for changes
540 no changes found
540 no changes found
541 [1]
541 [1]
542
542
543 the subrepos on the source do not have a clean store versus the clone target
543 the subrepos on the source do not have a clean store versus the clone target
544 because they were never explicitly pushed to the source
544 because they were never explicitly pushed to the source
545
545
546 $ hg push ../tcc
546 $ hg push ../tcc
547 pushing to ../tcc
547 pushing to ../tcc
548 pushing subrepo s/ss to ../tcc/s/ss (glob)
548 pushing subrepo s/ss to ../tcc/s/ss (glob)
549 searching for changes
549 searching for changes
550 no changes found
550 no changes found
551 pushing subrepo s to ../tcc/s
551 pushing subrepo s to ../tcc/s
552 searching for changes
552 searching for changes
553 no changes found
553 no changes found
554 pushing subrepo t to ../tcc/t
554 pushing subrepo t to ../tcc/t
555 searching for changes
555 searching for changes
556 no changes found
556 no changes found
557 searching for changes
557 searching for changes
558 no changes found
558 no changes found
559 [1]
559 [1]
560
560
561 after push their stores become clean
561 after push their stores become clean
562
562
563 $ hg push ../tcc
563 $ hg push ../tcc
564 pushing to ../tcc
564 pushing to ../tcc
565 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
565 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
566 no changes made to subrepo s since last push to ../tcc/s
566 no changes made to subrepo s since last push to ../tcc/s
567 no changes made to subrepo t since last push to ../tcc/t
567 no changes made to subrepo t since last push to ../tcc/t
568 searching for changes
568 searching for changes
569 no changes found
569 no changes found
570 [1]
570 [1]
571
571
572 updating a subrepo to a different revision or changing
572 updating a subrepo to a different revision or changing
573 its working directory does not make its store dirty
573 its working directory does not make its store dirty
574
574
575 $ hg -R s update '.^'
575 $ hg -R s update '.^'
576 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
576 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
577 $ hg push
577 $ hg push
578 pushing to $TESTTMP/t (glob)
578 pushing to $TESTTMP/t (glob)
579 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
579 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
580 no changes made to subrepo s since last push to $TESTTMP/t/s
580 no changes made to subrepo s since last push to $TESTTMP/t/s
581 no changes made to subrepo t since last push to $TESTTMP/t/t
581 no changes made to subrepo t since last push to $TESTTMP/t/t
582 searching for changes
582 searching for changes
583 no changes found
583 no changes found
584 [1]
584 [1]
585 $ echo foo >> s/a
585 $ echo foo >> s/a
586 $ hg push
586 $ hg push
587 pushing to $TESTTMP/t (glob)
587 pushing to $TESTTMP/t (glob)
588 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
588 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
589 no changes made to subrepo s since last push to $TESTTMP/t/s
589 no changes made to subrepo s since last push to $TESTTMP/t/s
590 no changes made to subrepo t since last push to $TESTTMP/t/t
590 no changes made to subrepo t since last push to $TESTTMP/t/t
591 searching for changes
591 searching for changes
592 no changes found
592 no changes found
593 [1]
593 [1]
594 $ hg -R s update -C tip
594 $ hg -R s update -C tip
595 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
596
596
597 committing into a subrepo makes its store (but not its parent's store) dirty
597 committing into a subrepo makes its store (but not its parent's store) dirty
598
598
599 $ echo foo >> s/ss/a
599 $ echo foo >> s/ss/a
600 $ hg -R s/ss commit -m 'test dirty store detection'
600 $ hg -R s/ss commit -m 'test dirty store detection'
601 $ hg push
601 $ hg push
602 pushing to $TESTTMP/t (glob)
602 pushing to $TESTTMP/t (glob)
603 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
603 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
604 searching for changes
604 searching for changes
605 adding changesets
605 adding changesets
606 adding manifests
606 adding manifests
607 adding file changes
607 adding file changes
608 added 1 changesets with 1 changes to 1 files
608 added 1 changesets with 1 changes to 1 files
609 no changes made to subrepo s since last push to $TESTTMP/t/s
609 no changes made to subrepo s since last push to $TESTTMP/t/s
610 no changes made to subrepo t since last push to $TESTTMP/t/t
610 no changes made to subrepo t since last push to $TESTTMP/t/t
611 searching for changes
611 searching for changes
612 no changes found
612 no changes found
613 [1]
613 [1]
614
614
615 a subrepo store may be clean versus one repo but not versus another
615 a subrepo store may be clean versus one repo but not versus another
616
616
617 $ hg push
617 $ hg push
618 pushing to $TESTTMP/t (glob)
618 pushing to $TESTTMP/t (glob)
619 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
619 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
620 no changes made to subrepo s since last push to $TESTTMP/t/s
620 no changes made to subrepo s since last push to $TESTTMP/t/s
621 no changes made to subrepo t since last push to $TESTTMP/t/t
621 no changes made to subrepo t since last push to $TESTTMP/t/t
622 searching for changes
622 searching for changes
623 no changes found
623 no changes found
624 [1]
624 [1]
625 $ hg push ../tcc
625 $ hg push ../tcc
626 pushing to ../tcc
626 pushing to ../tcc
627 pushing subrepo s/ss to ../tcc/s/ss (glob)
627 pushing subrepo s/ss to ../tcc/s/ss (glob)
628 searching for changes
628 searching for changes
629 adding changesets
629 adding changesets
630 adding manifests
630 adding manifests
631 adding file changes
631 adding file changes
632 added 1 changesets with 1 changes to 1 files
632 added 1 changesets with 1 changes to 1 files
633 no changes made to subrepo s since last push to ../tcc/s
633 no changes made to subrepo s since last push to ../tcc/s
634 no changes made to subrepo t since last push to ../tcc/t
634 no changes made to subrepo t since last push to ../tcc/t
635 searching for changes
635 searching for changes
636 no changes found
636 no changes found
637 [1]
637 [1]
638
638
639 update
639 update
640
640
641 $ cd ../t
641 $ cd ../t
642 $ hg up -C # discard our earlier merge
642 $ hg up -C # discard our earlier merge
643 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
643 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
644 $ echo blah > t/t
644 $ echo blah > t/t
645 $ hg ci -m13
645 $ hg ci -m13
646 committing subrepository t
646 committing subrepository t
647
647
648 backout calls revert internally with minimal opts, which should not raise
648 backout calls revert internally with minimal opts, which should not raise
649 KeyError
649 KeyError
650
650
651 $ hg backout ".^"
651 $ hg backout ".^"
652 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
652 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
653 changeset c373c8102e68 backed out, don't forget to commit.
653 changeset c373c8102e68 backed out, don't forget to commit.
654
654
655 $ hg up -C # discard changes
655 $ hg up -C # discard changes
656 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
656 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
657
657
658 pull
658 pull
659
659
660 $ cd ../tc
660 $ cd ../tc
661 $ hg pull
661 $ hg pull
662 pulling from $TESTTMP/t (glob)
662 pulling from $TESTTMP/t (glob)
663 searching for changes
663 searching for changes
664 adding changesets
664 adding changesets
665 adding manifests
665 adding manifests
666 adding file changes
666 adding file changes
667 added 1 changesets with 1 changes to 1 files
667 added 1 changesets with 1 changes to 1 files
668 (run 'hg update' to get a working copy)
668 (run 'hg update' to get a working copy)
669
669
670 should pull t
670 should pull t
671
671
672 $ hg up
672 $ hg up
673 pulling subrepo t from $TESTTMP/t/t
673 pulling subrepo t from $TESTTMP/t/t
674 searching for changes
674 searching for changes
675 adding changesets
675 adding changesets
676 adding manifests
676 adding manifests
677 adding file changes
677 adding file changes
678 added 1 changesets with 1 changes to 1 files
678 added 1 changesets with 1 changes to 1 files
679 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
679 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
680 $ cat t/t
680 $ cat t/t
681 blah
681 blah
682
682
683 bogus subrepo path aborts
683 bogus subrepo path aborts
684
684
685 $ echo 'bogus=[boguspath' >> .hgsub
685 $ echo 'bogus=[boguspath' >> .hgsub
686 $ hg ci -m 'bogus subrepo path'
686 $ hg ci -m 'bogus subrepo path'
687 abort: missing ] in subrepo source
687 abort: missing ] in subrepo source
688 [255]
688 [255]
689
689
690 Issue1986: merge aborts when trying to merge a subrepo that
690 Issue1986: merge aborts when trying to merge a subrepo that
691 shouldn't need merging
691 shouldn't need merging
692
692
693 # subrepo layout
693 # subrepo layout
694 #
694 #
695 # o 5 br
695 # o 5 br
696 # /|
696 # /|
697 # o | 4 default
697 # o | 4 default
698 # | |
698 # | |
699 # | o 3 br
699 # | o 3 br
700 # |/|
700 # |/|
701 # o | 2 default
701 # o | 2 default
702 # | |
702 # | |
703 # | o 1 br
703 # | o 1 br
704 # |/
704 # |/
705 # o 0 default
705 # o 0 default
706
706
707 $ cd ..
707 $ cd ..
708 $ rm -rf sub
708 $ rm -rf sub
709 $ hg init main
709 $ hg init main
710 $ cd main
710 $ cd main
711 $ hg init s
711 $ hg init s
712 $ cd s
712 $ cd s
713 $ echo a > a
713 $ echo a > a
714 $ hg ci -Am1
714 $ hg ci -Am1
715 adding a
715 adding a
716 $ hg branch br
716 $ hg branch br
717 marked working directory as branch br
717 marked working directory as branch br
718 (branches are permanent and global, did you want a bookmark?)
718 (branches are permanent and global, did you want a bookmark?)
719 $ echo a >> a
719 $ echo a >> a
720 $ hg ci -m1
720 $ hg ci -m1
721 $ hg up default
721 $ hg up default
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
723 $ echo b > b
723 $ echo b > b
724 $ hg ci -Am1
724 $ hg ci -Am1
725 adding b
725 adding b
726 $ hg up br
726 $ hg up br
727 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
727 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
728 $ hg merge tip
728 $ hg merge tip
729 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
729 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
730 (branch merge, don't forget to commit)
730 (branch merge, don't forget to commit)
731 $ hg ci -m1
731 $ hg ci -m1
732 $ hg up 2
732 $ hg up 2
733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
734 $ echo c > c
734 $ echo c > c
735 $ hg ci -Am1
735 $ hg ci -Am1
736 adding c
736 adding c
737 $ hg up 3
737 $ hg up 3
738 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
738 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
739 $ hg merge 4
739 $ hg merge 4
740 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
740 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
741 (branch merge, don't forget to commit)
741 (branch merge, don't forget to commit)
742 $ hg ci -m1
742 $ hg ci -m1
743
743
744 # main repo layout:
744 # main repo layout:
745 #
745 #
746 # * <-- try to merge default into br again
746 # * <-- try to merge default into br again
747 # .`|
747 # .`|
748 # . o 5 br --> substate = 5
748 # . o 5 br --> substate = 5
749 # . |
749 # . |
750 # o | 4 default --> substate = 4
750 # o | 4 default --> substate = 4
751 # | |
751 # | |
752 # | o 3 br --> substate = 2
752 # | o 3 br --> substate = 2
753 # |/|
753 # |/|
754 # o | 2 default --> substate = 2
754 # o | 2 default --> substate = 2
755 # | |
755 # | |
756 # | o 1 br --> substate = 3
756 # | o 1 br --> substate = 3
757 # |/
757 # |/
758 # o 0 default --> substate = 2
758 # o 0 default --> substate = 2
759
759
760 $ cd ..
760 $ cd ..
761 $ echo 's = s' > .hgsub
761 $ echo 's = s' > .hgsub
762 $ hg -R s up 2
762 $ hg -R s up 2
763 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
763 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
764 $ hg ci -Am1
764 $ hg ci -Am1
765 adding .hgsub
765 adding .hgsub
766 $ hg branch br
766 $ hg branch br
767 marked working directory as branch br
767 marked working directory as branch br
768 (branches are permanent and global, did you want a bookmark?)
768 (branches are permanent and global, did you want a bookmark?)
769 $ echo b > b
769 $ echo b > b
770 $ hg -R s up 3
770 $ hg -R s up 3
771 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
771 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
772 $ hg ci -Am1
772 $ hg ci -Am1
773 adding b
773 adding b
774 $ hg up default
774 $ hg up default
775 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
775 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
776 $ echo c > c
776 $ echo c > c
777 $ hg ci -Am1
777 $ hg ci -Am1
778 adding c
778 adding c
779 $ hg up 1
779 $ hg up 1
780 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
780 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
781 $ hg merge 2
781 $ hg merge 2
782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
783 (branch merge, don't forget to commit)
783 (branch merge, don't forget to commit)
784 $ hg ci -m1
784 $ hg ci -m1
785 $ hg up 2
785 $ hg up 2
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 -R s up 4
787 $ hg -R s up 4
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 $ echo d > d
789 $ echo d > d
790 $ hg ci -Am1
790 $ hg ci -Am1
791 adding d
791 adding d
792 $ hg up 3
792 $ hg up 3
793 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
793 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
794 $ hg -R s up 5
794 $ hg -R s up 5
795 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
795 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 $ echo e > e
796 $ echo e > e
797 $ hg ci -Am1
797 $ hg ci -Am1
798 adding e
798 adding e
799
799
800 $ hg up 5
800 $ hg up 5
801 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
802 $ hg merge 4 # try to merge default into br again
802 $ hg merge 4 # try to merge default into br again
803 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
803 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
804 (M)erge, keep (l)ocal or keep (r)emote? m
804 (M)erge, keep (l)ocal or keep (r)emote? m
805 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
805 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
806 (branch merge, don't forget to commit)
806 (branch merge, don't forget to commit)
807 $ cd ..
807 $ cd ..
808
808
809 test subrepo delete from .hgsubstate
809 test subrepo delete from .hgsubstate
810
810
811 $ hg init testdelete
811 $ hg init testdelete
812 $ mkdir testdelete/nested testdelete/nested2
812 $ mkdir testdelete/nested testdelete/nested2
813 $ hg init testdelete/nested
813 $ hg init testdelete/nested
814 $ hg init testdelete/nested2
814 $ hg init testdelete/nested2
815 $ echo test > testdelete/nested/foo
815 $ echo test > testdelete/nested/foo
816 $ echo test > testdelete/nested2/foo
816 $ echo test > testdelete/nested2/foo
817 $ hg -R testdelete/nested add
817 $ hg -R testdelete/nested add
818 adding testdelete/nested/foo (glob)
818 adding testdelete/nested/foo (glob)
819 $ hg -R testdelete/nested2 add
819 $ hg -R testdelete/nested2 add
820 adding testdelete/nested2/foo (glob)
820 adding testdelete/nested2/foo (glob)
821 $ hg -R testdelete/nested ci -m test
821 $ hg -R testdelete/nested ci -m test
822 $ hg -R testdelete/nested2 ci -m test
822 $ hg -R testdelete/nested2 ci -m test
823 $ echo nested = nested > testdelete/.hgsub
823 $ echo nested = nested > testdelete/.hgsub
824 $ echo nested2 = nested2 >> testdelete/.hgsub
824 $ echo nested2 = nested2 >> testdelete/.hgsub
825 $ hg -R testdelete add
825 $ hg -R testdelete add
826 adding testdelete/.hgsub (glob)
826 adding testdelete/.hgsub (glob)
827 $ hg -R testdelete ci -m "nested 1 & 2 added"
827 $ hg -R testdelete ci -m "nested 1 & 2 added"
828 $ echo nested = nested > testdelete/.hgsub
828 $ echo nested = nested > testdelete/.hgsub
829 $ hg -R testdelete ci -m "nested 2 deleted"
829 $ hg -R testdelete ci -m "nested 2 deleted"
830 $ cat testdelete/.hgsubstate
830 $ cat testdelete/.hgsubstate
831 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
831 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
832 $ hg -R testdelete remove testdelete/.hgsub
832 $ hg -R testdelete remove testdelete/.hgsub
833 $ hg -R testdelete ci -m ".hgsub deleted"
833 $ hg -R testdelete ci -m ".hgsub deleted"
834 $ cat testdelete/.hgsubstate
834 $ cat testdelete/.hgsubstate
835 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
835 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
836
836
837 test repository cloning
837 test repository cloning
838
838
839 $ mkdir mercurial mercurial2
839 $ mkdir mercurial mercurial2
840 $ hg init nested_absolute
840 $ hg init nested_absolute
841 $ echo test > nested_absolute/foo
841 $ echo test > nested_absolute/foo
842 $ hg -R nested_absolute add
842 $ hg -R nested_absolute add
843 adding nested_absolute/foo (glob)
843 adding nested_absolute/foo (glob)
844 $ hg -R nested_absolute ci -mtest
844 $ hg -R nested_absolute ci -mtest
845 $ cd mercurial
845 $ cd mercurial
846 $ hg init nested_relative
846 $ hg init nested_relative
847 $ echo test2 > nested_relative/foo2
847 $ echo test2 > nested_relative/foo2
848 $ hg -R nested_relative add
848 $ hg -R nested_relative add
849 adding nested_relative/foo2 (glob)
849 adding nested_relative/foo2 (glob)
850 $ hg -R nested_relative ci -mtest2
850 $ hg -R nested_relative ci -mtest2
851 $ hg init main
851 $ hg init main
852 $ echo "nested_relative = ../nested_relative" > main/.hgsub
852 $ echo "nested_relative = ../nested_relative" > main/.hgsub
853 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
853 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
854 $ hg -R main add
854 $ hg -R main add
855 adding main/.hgsub (glob)
855 adding main/.hgsub (glob)
856 $ hg -R main ci -m "add subrepos"
856 $ hg -R main ci -m "add subrepos"
857 $ cd ..
857 $ cd ..
858 $ hg clone mercurial/main mercurial2/main
858 $ hg clone mercurial/main mercurial2/main
859 updating to branch default
859 updating to branch default
860 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
860 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
861 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
861 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
862 > mercurial2/main/nested_relative/.hg/hgrc
862 > mercurial2/main/nested_relative/.hg/hgrc
863 [paths]
863 [paths]
864 default = $TESTTMP/mercurial/nested_absolute
864 default = $TESTTMP/mercurial/nested_absolute
865 [paths]
865 [paths]
866 default = $TESTTMP/mercurial/nested_relative
866 default = $TESTTMP/mercurial/nested_relative
867 $ rm -rf mercurial mercurial2
867 $ rm -rf mercurial mercurial2
868
868
869 Issue1977: multirepo push should fail if subrepo push fails
869 Issue1977: multirepo push should fail if subrepo push fails
870
870
871 $ hg init repo
871 $ hg init repo
872 $ hg init repo/s
872 $ hg init repo/s
873 $ echo a > repo/s/a
873 $ echo a > repo/s/a
874 $ hg -R repo/s ci -Am0
874 $ hg -R repo/s ci -Am0
875 adding a
875 adding a
876 $ echo s = s > repo/.hgsub
876 $ echo s = s > repo/.hgsub
877 $ hg -R repo ci -Am1
877 $ hg -R repo ci -Am1
878 adding .hgsub
878 adding .hgsub
879 $ hg clone repo repo2
879 $ hg clone repo repo2
880 updating to branch default
880 updating to branch default
881 cloning subrepo s from $TESTTMP/repo/s
881 cloning subrepo s from $TESTTMP/repo/s
882 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 $ hg -q -R repo2 pull -u
883 $ hg -q -R repo2 pull -u
884 $ echo 1 > repo2/s/a
884 $ echo 1 > repo2/s/a
885 $ hg -R repo2/s ci -m2
885 $ hg -R repo2/s ci -m2
886 $ hg -q -R repo2/s push
886 $ hg -q -R repo2/s push
887 $ hg -R repo2/s up -C 0
887 $ hg -R repo2/s up -C 0
888 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
888 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
889 $ echo 2 > repo2/s/b
889 $ echo 2 > repo2/s/b
890 $ hg -R repo2/s ci -m3 -A
890 $ hg -R repo2/s ci -m3 -A
891 adding b
891 adding b
892 created new head
892 created new head
893 $ hg -R repo2 ci -m3
893 $ hg -R repo2 ci -m3
894 $ hg -q -R repo2 push
894 $ hg -q -R repo2 push
895 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
895 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
896 (merge or see "hg help push" for details about pushing new heads)
896 (merge or see "hg help push" for details about pushing new heads)
897 [255]
897 [255]
898 $ hg -R repo update
898 $ hg -R repo update
899 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
899 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
900
900
901 test if untracked file is not overwritten
901 test if untracked file is not overwritten
902
902
903 $ echo issue3276_ok > repo/s/b
903 $ echo issue3276_ok > repo/s/b
904 $ hg -R repo2 push -f -q
904 $ hg -R repo2 push -f -q
905 $ touch -t 200001010000 repo/.hgsubstate
905 $ touch -t 200001010000 repo/.hgsubstate
906 $ hg -R repo status --config debug.dirstate.delaywrite=2 repo/.hgsubstate
906 $ hg -R repo status --config debug.dirstate.delaywrite=2 repo/.hgsubstate
907 $ hg -R repo update
907 $ hg -R repo update
908 b: untracked file differs
908 b: untracked file differs
909 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
909 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
910 [255]
910 [255]
911
911
912 $ cat repo/s/b
912 $ cat repo/s/b
913 issue3276_ok
913 issue3276_ok
914 $ rm repo/s/b
914 $ rm repo/s/b
915 $ touch -t 200001010000 repo/.hgsubstate
915 $ touch -t 200001010000 repo/.hgsubstate
916 $ hg -R repo revert --all
916 $ hg -R repo revert --all
917 reverting repo/.hgsubstate (glob)
917 reverting repo/.hgsubstate (glob)
918 reverting subrepo s
918 reverting subrepo s
919 $ hg -R repo update
919 $ hg -R repo update
920 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
921 $ cat repo/s/b
921 $ cat repo/s/b
922 2
922 2
923 $ rm -rf repo2 repo
923 $ rm -rf repo2 repo
924
924
925
925
926 Issue1852 subrepos with relative paths always push/pull relative to default
926 Issue1852 subrepos with relative paths always push/pull relative to default
927
927
928 Prepare a repo with subrepo
928 Prepare a repo with subrepo
929
929
930 $ hg init issue1852a
930 $ hg init issue1852a
931 $ cd issue1852a
931 $ cd issue1852a
932 $ hg init sub/repo
932 $ hg init sub/repo
933 $ echo test > sub/repo/foo
933 $ echo test > sub/repo/foo
934 $ hg -R sub/repo add sub/repo/foo
934 $ hg -R sub/repo add sub/repo/foo
935 $ echo sub/repo = sub/repo > .hgsub
935 $ echo sub/repo = sub/repo > .hgsub
936 $ hg add .hgsub
936 $ hg add .hgsub
937 $ hg ci -mtest
937 $ hg ci -mtest
938 committing subrepository sub/repo (glob)
938 committing subrepository sub/repo (glob)
939 $ echo test >> sub/repo/foo
939 $ echo test >> sub/repo/foo
940 $ hg ci -mtest
940 $ hg ci -mtest
941 committing subrepository sub/repo (glob)
941 committing subrepository sub/repo (glob)
942 $ hg cat sub/repo/foo
942 $ hg cat sub/repo/foo
943 test
943 test
944 test
944 test
945 $ mkdir -p tmp/sub/repo
945 $ mkdir -p tmp/sub/repo
946 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
946 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
947 $ cat tmp/sub/repo/foo_p
947 $ cat tmp/sub/repo/foo_p
948 test
948 test
949 $ mv sub/repo sub_
949 $ mv sub/repo sub_
950 $ hg cat sub/repo/baz
950 $ hg cat sub/repo/baz
951 skipping missing subrepository: sub/repo
951 skipping missing subrepository: sub/repo
952 [1]
952 [1]
953 $ rm -rf sub/repo
953 $ rm -rf sub/repo
954 $ mv sub_ sub/repo
954 $ mv sub_ sub/repo
955 $ cd ..
955 $ cd ..
956
956
957 Create repo without default path, pull top repo, and see what happens on update
957 Create repo without default path, pull top repo, and see what happens on update
958
958
959 $ hg init issue1852b
959 $ hg init issue1852b
960 $ hg -R issue1852b pull issue1852a
960 $ hg -R issue1852b pull issue1852a
961 pulling from issue1852a
961 pulling from issue1852a
962 requesting all changes
962 requesting all changes
963 adding changesets
963 adding changesets
964 adding manifests
964 adding manifests
965 adding file changes
965 adding file changes
966 added 2 changesets with 3 changes to 2 files
966 added 2 changesets with 3 changes to 2 files
967 (run 'hg update' to get a working copy)
967 (run 'hg update' to get a working copy)
968 $ hg -R issue1852b update
968 $ hg -R issue1852b update
969 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
969 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
970 [255]
970 [255]
971
971
972 Ensure a full traceback, not just the SubrepoAbort part
972 Ensure a full traceback, not just the SubrepoAbort part
973
973
974 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort'
974 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort'
975 raise util.Abort(_("default path for subrepository not found"))
975 raise util.Abort(_("default path for subrepository not found"))
976
976
977 Pull -u now doesn't help
977 Pull -u now doesn't help
978
978
979 $ hg -R issue1852b pull -u issue1852a
979 $ hg -R issue1852b pull -u issue1852a
980 pulling from issue1852a
980 pulling from issue1852a
981 searching for changes
981 searching for changes
982 no changes found
982 no changes found
983
983
984 Try the same, but with pull -u
984 Try the same, but with pull -u
985
985
986 $ hg init issue1852c
986 $ hg init issue1852c
987 $ hg -R issue1852c pull -r0 -u issue1852a
987 $ hg -R issue1852c pull -r0 -u issue1852a
988 pulling from issue1852a
988 pulling from issue1852a
989 adding changesets
989 adding changesets
990 adding manifests
990 adding manifests
991 adding file changes
991 adding file changes
992 added 1 changesets with 2 changes to 2 files
992 added 1 changesets with 2 changes to 2 files
993 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
993 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
994 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
994 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
995
995
996 Try to push from the other side
996 Try to push from the other side
997
997
998 $ hg -R issue1852a push `pwd`/issue1852c
998 $ hg -R issue1852a push `pwd`/issue1852c
999 pushing to $TESTTMP/issue1852c (glob)
999 pushing to $TESTTMP/issue1852c (glob)
1000 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1000 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1001 searching for changes
1001 searching for changes
1002 no changes found
1002 no changes found
1003 searching for changes
1003 searching for changes
1004 adding changesets
1004 adding changesets
1005 adding manifests
1005 adding manifests
1006 adding file changes
1006 adding file changes
1007 added 1 changesets with 1 changes to 1 files
1007 added 1 changesets with 1 changes to 1 files
1008
1008
1009 Incoming and outgoing should not use the default path:
1009 Incoming and outgoing should not use the default path:
1010
1010
1011 $ hg clone -q issue1852a issue1852d
1011 $ hg clone -q issue1852a issue1852d
1012 $ hg -R issue1852d outgoing --subrepos issue1852c
1012 $ hg -R issue1852d outgoing --subrepos issue1852c
1013 comparing with issue1852c
1013 comparing with issue1852c
1014 searching for changes
1014 searching for changes
1015 no changes found
1015 no changes found
1016 comparing with issue1852c/sub/repo
1016 comparing with issue1852c/sub/repo
1017 searching for changes
1017 searching for changes
1018 no changes found
1018 no changes found
1019 [1]
1019 [1]
1020 $ hg -R issue1852d incoming --subrepos issue1852c
1020 $ hg -R issue1852d incoming --subrepos issue1852c
1021 comparing with issue1852c
1021 comparing with issue1852c
1022 searching for changes
1022 searching for changes
1023 no changes found
1023 no changes found
1024 comparing with issue1852c/sub/repo
1024 comparing with issue1852c/sub/repo
1025 searching for changes
1025 searching for changes
1026 no changes found
1026 no changes found
1027 [1]
1027 [1]
1028
1028
1029 Check status of files when none of them belong to the first
1029 Check status of files when none of them belong to the first
1030 subrepository:
1030 subrepository:
1031
1031
1032 $ hg init subrepo-status
1032 $ hg init subrepo-status
1033 $ cd subrepo-status
1033 $ cd subrepo-status
1034 $ hg init subrepo-1
1034 $ hg init subrepo-1
1035 $ hg init subrepo-2
1035 $ hg init subrepo-2
1036 $ cd subrepo-2
1036 $ cd subrepo-2
1037 $ touch file
1037 $ touch file
1038 $ hg add file
1038 $ hg add file
1039 $ cd ..
1039 $ cd ..
1040 $ echo subrepo-1 = subrepo-1 > .hgsub
1040 $ echo subrepo-1 = subrepo-1 > .hgsub
1041 $ echo subrepo-2 = subrepo-2 >> .hgsub
1041 $ echo subrepo-2 = subrepo-2 >> .hgsub
1042 $ hg add .hgsub
1042 $ hg add .hgsub
1043 $ hg ci -m 'Added subrepos'
1043 $ hg ci -m 'Added subrepos'
1044 committing subrepository subrepo-2
1044 committing subrepository subrepo-2
1045 $ hg st subrepo-2/file
1045 $ hg st subrepo-2/file
1046
1046
1047 Check that share works with subrepo
1047 Check that share works with subrepo
1048 $ hg --config extensions.share= share . ../shared
1048 $ hg --config extensions.share= share . ../shared
1049 updating working directory
1049 updating working directory
1050 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1050 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1051 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1051 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1052 $ test -f ../shared/subrepo-1/.hg/sharedpath
1052 $ test -f ../shared/subrepo-1/.hg/sharedpath
1053 [1]
1053 [1]
1054 $ hg -R ../shared in
1054 $ hg -R ../shared in
1055 abort: repository default not found!
1055 abort: repository default not found!
1056 [255]
1056 [255]
1057 $ hg -R ../shared/subrepo-2 showconfig paths
1057 $ hg -R ../shared/subrepo-2 showconfig paths
1058 paths.default=$TESTTMP/subrepo-status/subrepo-2
1058 paths.default=$TESTTMP/subrepo-status/subrepo-2
1059 $ hg -R ../shared/subrepo-1 sum --remote
1059 $ hg -R ../shared/subrepo-1 sum --remote
1060 parent: -1:000000000000 tip (empty repository)
1060 parent: -1:000000000000 tip (empty repository)
1061 branch: default
1061 branch: default
1062 commit: (clean)
1062 commit: (clean)
1063 update: (current)
1063 update: (current)
1064 remote: (synced)
1064 remote: (synced)
1065
1065
1066 Check hg update --clean
1066 Check hg update --clean
1067 $ cd $TESTTMP/t
1067 $ cd $TESTTMP/t
1068 $ rm -r t/t.orig
1068 $ rm -r t/t.orig
1069 $ hg status -S --all
1069 $ hg status -S --all
1070 C .hgsub
1070 C .hgsub
1071 C .hgsubstate
1071 C .hgsubstate
1072 C a
1072 C a
1073 C s/.hgsub
1073 C s/.hgsub
1074 C s/.hgsubstate
1074 C s/.hgsubstate
1075 C s/a
1075 C s/a
1076 C s/ss/a
1076 C s/ss/a
1077 C t/t
1077 C t/t
1078 $ echo c1 > s/a
1078 $ echo c1 > s/a
1079 $ cd s
1079 $ cd s
1080 $ echo c1 > b
1080 $ echo c1 > b
1081 $ echo c1 > c
1081 $ echo c1 > c
1082 $ hg add b
1082 $ hg add b
1083 $ cd ..
1083 $ cd ..
1084 $ hg status -S
1084 $ hg status -S
1085 M s/a
1085 M s/a
1086 A s/b
1086 A s/b
1087 ? s/c
1087 ? s/c
1088 $ hg update -C
1088 $ hg update -C
1089 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1089 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1090 $ hg status -S
1090 $ hg status -S
1091 ? s/b
1091 ? s/b
1092 ? s/c
1092 ? s/c
1093
1093
1094 Sticky subrepositories, no changes
1094 Sticky subrepositories, no changes
1095 $ cd $TESTTMP/t
1095 $ cd $TESTTMP/t
1096 $ hg id
1096 $ hg id
1097 925c17564ef8 tip
1097 925c17564ef8 tip
1098 $ hg -R s id
1098 $ hg -R s id
1099 12a213df6fa9 tip
1099 12a213df6fa9 tip
1100 $ hg -R t id
1100 $ hg -R t id
1101 52c0adc0515a tip
1101 52c0adc0515a tip
1102 $ hg update 11
1102 $ hg update 11
1103 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1103 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1104 $ hg id
1104 $ hg id
1105 365661e5936a
1105 365661e5936a
1106 $ hg -R s id
1106 $ hg -R s id
1107 fc627a69481f
1107 fc627a69481f
1108 $ hg -R t id
1108 $ hg -R t id
1109 e95bcfa18a35
1109 e95bcfa18a35
1110
1110
1111 Sticky subrepositories, file changes
1111 Sticky subrepositories, file changes
1112 $ touch s/f1
1112 $ touch s/f1
1113 $ touch t/f1
1113 $ touch t/f1
1114 $ hg add -S s/f1
1114 $ hg add -S s/f1
1115 $ hg add -S t/f1
1115 $ hg add -S t/f1
1116 $ hg id
1116 $ hg id
1117 365661e5936a+
1117 365661e5936a+
1118 $ hg -R s id
1118 $ hg -R s id
1119 fc627a69481f+
1119 fc627a69481f+
1120 $ hg -R t id
1120 $ hg -R t id
1121 e95bcfa18a35+
1121 e95bcfa18a35+
1122 $ hg update tip
1122 $ hg update tip
1123 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1123 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1124 (M)erge, keep (l)ocal or keep (r)emote? m
1124 (M)erge, keep (l)ocal or keep (r)emote? m
1125 subrepository sources for s differ
1125 subrepository sources for s differ
1126 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1126 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1127 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1127 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1128 (M)erge, keep (l)ocal or keep (r)emote? m
1128 (M)erge, keep (l)ocal or keep (r)emote? m
1129 subrepository sources for t differ
1129 subrepository sources for t differ
1130 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1130 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1131 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1131 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1132 $ hg id
1132 $ hg id
1133 925c17564ef8+ tip
1133 925c17564ef8+ tip
1134 $ hg -R s id
1134 $ hg -R s id
1135 fc627a69481f+
1135 fc627a69481f+
1136 $ hg -R t id
1136 $ hg -R t id
1137 e95bcfa18a35+
1137 e95bcfa18a35+
1138 $ hg update --clean tip
1138 $ hg update --clean tip
1139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1140
1140
1141 Sticky subrepository, revision updates
1141 Sticky subrepository, revision updates
1142 $ hg id
1142 $ hg id
1143 925c17564ef8 tip
1143 925c17564ef8 tip
1144 $ hg -R s id
1144 $ hg -R s id
1145 12a213df6fa9 tip
1145 12a213df6fa9 tip
1146 $ hg -R t id
1146 $ hg -R t id
1147 52c0adc0515a tip
1147 52c0adc0515a tip
1148 $ cd s
1148 $ cd s
1149 $ hg update -r -2
1149 $ hg update -r -2
1150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1151 $ cd ../t
1151 $ cd ../t
1152 $ hg update -r 2
1152 $ hg update -r 2
1153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1154 $ cd ..
1154 $ cd ..
1155 $ hg update 10
1155 $ hg update 10
1156 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1156 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1157 (M)erge, keep (l)ocal or keep (r)emote? m
1157 (M)erge, keep (l)ocal or keep (r)emote? m
1158 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1158 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1159 (M)erge, keep (l)ocal or keep (r)emote? m
1159 (M)erge, keep (l)ocal or keep (r)emote? m
1160 subrepository sources for t differ (in checked out version)
1160 subrepository sources for t differ (in checked out version)
1161 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1161 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1162 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1162 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1163 $ hg id
1163 $ hg id
1164 e45c8b14af55+
1164 e45c8b14af55+
1165 $ hg -R s id
1165 $ hg -R s id
1166 02dcf1d70411
1166 02dcf1d70411
1167 $ hg -R t id
1167 $ hg -R t id
1168 7af322bc1198
1168 7af322bc1198
1169
1169
1170 Sticky subrepository, file changes and revision updates
1170 Sticky subrepository, file changes and revision updates
1171 $ touch s/f1
1171 $ touch s/f1
1172 $ touch t/f1
1172 $ touch t/f1
1173 $ hg add -S s/f1
1173 $ hg add -S s/f1
1174 $ hg add -S t/f1
1174 $ hg add -S t/f1
1175 $ hg id
1175 $ hg id
1176 e45c8b14af55+
1176 e45c8b14af55+
1177 $ hg -R s id
1177 $ hg -R s id
1178 02dcf1d70411+
1178 02dcf1d70411+
1179 $ hg -R t id
1179 $ hg -R t id
1180 7af322bc1198+
1180 7af322bc1198+
1181 $ hg update tip
1181 $ hg update tip
1182 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1182 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1183 (M)erge, keep (l)ocal or keep (r)emote? m
1183 (M)erge, keep (l)ocal or keep (r)emote? m
1184 subrepository sources for s differ
1184 subrepository sources for s differ
1185 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1185 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1186 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1186 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1187 (M)erge, keep (l)ocal or keep (r)emote? m
1187 (M)erge, keep (l)ocal or keep (r)emote? m
1188 subrepository sources for t differ
1188 subrepository sources for t differ
1189 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1189 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1190 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1190 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1191 $ hg id
1191 $ hg id
1192 925c17564ef8+ tip
1192 925c17564ef8+ tip
1193 $ hg -R s id
1193 $ hg -R s id
1194 02dcf1d70411+
1194 02dcf1d70411+
1195 $ hg -R t id
1195 $ hg -R t id
1196 7af322bc1198+
1196 7af322bc1198+
1197
1197
1198 Sticky repository, update --clean
1198 Sticky repository, update --clean
1199 $ hg update --clean tip
1199 $ hg update --clean tip
1200 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1200 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1201 $ hg id
1201 $ hg id
1202 925c17564ef8 tip
1202 925c17564ef8 tip
1203 $ hg -R s id
1203 $ hg -R s id
1204 12a213df6fa9 tip
1204 12a213df6fa9 tip
1205 $ hg -R t id
1205 $ hg -R t id
1206 52c0adc0515a tip
1206 52c0adc0515a tip
1207
1207
1208 Test subrepo already at intended revision:
1208 Test subrepo already at intended revision:
1209 $ cd s
1209 $ cd s
1210 $ hg update fc627a69481f
1210 $ hg update fc627a69481f
1211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1212 $ cd ..
1212 $ cd ..
1213 $ hg update 11
1213 $ hg update 11
1214 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1214 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1215 (M)erge, keep (l)ocal or keep (r)emote? m
1215 (M)erge, keep (l)ocal or keep (r)emote? m
1216 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1216 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1217 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1217 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1218 $ hg id -n
1218 $ hg id -n
1219 11+
1219 11+
1220 $ hg -R s id
1220 $ hg -R s id
1221 fc627a69481f
1221 fc627a69481f
1222 $ hg -R t id
1222 $ hg -R t id
1223 e95bcfa18a35
1223 e95bcfa18a35
1224
1224
1225 Test that removing .hgsubstate doesn't break anything:
1225 Test that removing .hgsubstate doesn't break anything:
1226
1226
1227 $ hg rm -f .hgsubstate
1227 $ hg rm -f .hgsubstate
1228 $ hg ci -mrm
1228 $ hg ci -mrm
1229 nothing changed
1229 nothing changed
1230 [1]
1230 [1]
1231 $ hg log -vr tip
1231 $ hg log -vr tip
1232 changeset: 13:925c17564ef8
1232 changeset: 13:925c17564ef8
1233 tag: tip
1233 tag: tip
1234 user: test
1234 user: test
1235 date: Thu Jan 01 00:00:00 1970 +0000
1235 date: Thu Jan 01 00:00:00 1970 +0000
1236 files: .hgsubstate
1236 files: .hgsubstate
1237 description:
1237 description:
1238 13
1238 13
1239
1239
1240
1240
1241
1241
1242 Test that removing .hgsub removes .hgsubstate:
1242 Test that removing .hgsub removes .hgsubstate:
1243
1243
1244 $ hg rm .hgsub
1244 $ hg rm .hgsub
1245 $ hg ci -mrm2
1245 $ hg ci -mrm2
1246 created new head
1246 created new head
1247 $ hg log -vr tip
1247 $ hg log -vr tip
1248 changeset: 14:2400bccd50af
1248 changeset: 14:2400bccd50af
1249 tag: tip
1249 tag: tip
1250 parent: 11:365661e5936a
1250 parent: 11:365661e5936a
1251 user: test
1251 user: test
1252 date: Thu Jan 01 00:00:00 1970 +0000
1252 date: Thu Jan 01 00:00:00 1970 +0000
1253 files: .hgsub .hgsubstate
1253 files: .hgsub .hgsubstate
1254 description:
1254 description:
1255 rm2
1255 rm2
1256
1256
1257
1257
1258 Test issue3153: diff -S with deleted subrepos
1258 Test issue3153: diff -S with deleted subrepos
1259
1259
1260 $ hg diff --nodates -S -c .
1260 $ hg diff --nodates -S -c .
1261 diff -r 365661e5936a -r 2400bccd50af .hgsub
1261 diff -r 365661e5936a -r 2400bccd50af .hgsub
1262 --- a/.hgsub
1262 --- a/.hgsub
1263 +++ /dev/null
1263 +++ /dev/null
1264 @@ -1,2 +0,0 @@
1264 @@ -1,2 +0,0 @@
1265 -s = s
1265 -s = s
1266 -t = t
1266 -t = t
1267 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1267 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1268 --- a/.hgsubstate
1268 --- a/.hgsubstate
1269 +++ /dev/null
1269 +++ /dev/null
1270 @@ -1,2 +0,0 @@
1270 @@ -1,2 +0,0 @@
1271 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1271 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1272 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1272 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1273
1273
1274 Test behavior of add for explicit path in subrepo:
1274 Test behavior of add for explicit path in subrepo:
1275 $ cd ..
1275 $ cd ..
1276 $ hg init explicit
1276 $ hg init explicit
1277 $ cd explicit
1277 $ cd explicit
1278 $ echo s = s > .hgsub
1278 $ echo s = s > .hgsub
1279 $ hg add .hgsub
1279 $ hg add .hgsub
1280 $ hg init s
1280 $ hg init s
1281 $ hg ci -m0
1281 $ hg ci -m0
1282 Adding with an explicit path in a subrepo adds the file
1282 Adding with an explicit path in a subrepo adds the file
1283 $ echo c1 > f1
1283 $ echo c1 > f1
1284 $ echo c2 > s/f2
1284 $ echo c2 > s/f2
1285 $ hg st -S
1285 $ hg st -S
1286 ? f1
1286 ? f1
1287 ? s/f2
1287 ? s/f2
1288 $ hg add s/f2
1288 $ hg add s/f2
1289 $ hg st -S
1289 $ hg st -S
1290 A s/f2
1290 A s/f2
1291 ? f1
1291 ? f1
1292 $ hg ci -R s -m0
1292 $ hg ci -R s -m0
1293 $ hg ci -Am1
1293 $ hg ci -Am1
1294 adding f1
1294 adding f1
1295 Adding with an explicit path in a subrepo with -S has the same behavior
1295 Adding with an explicit path in a subrepo with -S has the same behavior
1296 $ echo c3 > f3
1296 $ echo c3 > f3
1297 $ echo c4 > s/f4
1297 $ echo c4 > s/f4
1298 $ hg st -S
1298 $ hg st -S
1299 ? f3
1299 ? f3
1300 ? s/f4
1300 ? s/f4
1301 $ hg add -S s/f4
1301 $ hg add -S s/f4
1302 $ hg st -S
1302 $ hg st -S
1303 A s/f4
1303 A s/f4
1304 ? f3
1304 ? f3
1305 $ hg ci -R s -m1
1305 $ hg ci -R s -m1
1306 $ hg ci -Ama2
1306 $ hg ci -Ama2
1307 adding f3
1307 adding f3
1308 Adding without a path or pattern silently ignores subrepos
1308 Adding without a path or pattern silently ignores subrepos
1309 $ echo c5 > f5
1309 $ echo c5 > f5
1310 $ echo c6 > s/f6
1310 $ echo c6 > s/f6
1311 $ echo c7 > s/f7
1311 $ echo c7 > s/f7
1312 $ hg st -S
1312 $ hg st -S
1313 ? f5
1313 ? f5
1314 ? s/f6
1314 ? s/f6
1315 ? s/f7
1315 ? s/f7
1316 $ hg add
1316 $ hg add
1317 adding f5
1317 adding f5
1318 $ hg st -S
1318 $ hg st -S
1319 A f5
1319 A f5
1320 ? s/f6
1320 ? s/f6
1321 ? s/f7
1321 ? s/f7
1322 $ hg ci -R s -Am2
1322 $ hg ci -R s -Am2
1323 adding f6
1323 adding f6
1324 adding f7
1324 adding f7
1325 $ hg ci -m3
1325 $ hg ci -m3
1326 Adding without a path or pattern with -S also adds files in subrepos
1326 Adding without a path or pattern with -S also adds files in subrepos
1327 $ echo c8 > f8
1327 $ echo c8 > f8
1328 $ echo c9 > s/f9
1328 $ echo c9 > s/f9
1329 $ echo c10 > s/f10
1329 $ echo c10 > s/f10
1330 $ hg st -S
1330 $ hg st -S
1331 ? f8
1331 ? f8
1332 ? s/f10
1332 ? s/f10
1333 ? s/f9
1333 ? s/f9
1334 $ hg add -S
1334 $ hg add -S
1335 adding f8
1335 adding f8
1336 adding s/f10 (glob)
1336 adding s/f10 (glob)
1337 adding s/f9 (glob)
1337 adding s/f9 (glob)
1338 $ hg st -S
1338 $ hg st -S
1339 A f8
1339 A f8
1340 A s/f10
1340 A s/f10
1341 A s/f9
1341 A s/f9
1342 $ hg ci -R s -m3
1342 $ hg ci -R s -m3
1343 $ hg ci -m4
1343 $ hg ci -m4
1344 Adding with a pattern silently ignores subrepos
1344 Adding with a pattern silently ignores subrepos
1345 $ echo c11 > fm11
1345 $ echo c11 > fm11
1346 $ echo c12 > fn12
1346 $ echo c12 > fn12
1347 $ echo c13 > s/fm13
1347 $ echo c13 > s/fm13
1348 $ echo c14 > s/fn14
1348 $ echo c14 > s/fn14
1349 $ hg st -S
1349 $ hg st -S
1350 ? fm11
1350 ? fm11
1351 ? fn12
1351 ? fn12
1352 ? s/fm13
1352 ? s/fm13
1353 ? s/fn14
1353 ? s/fn14
1354 $ hg add 'glob:**fm*'
1354 $ hg add 'glob:**fm*'
1355 adding fm11
1355 adding fm11
1356 $ hg st -S
1356 $ hg st -S
1357 A fm11
1357 A fm11
1358 ? fn12
1358 ? fn12
1359 ? s/fm13
1359 ? s/fm13
1360 ? s/fn14
1360 ? s/fn14
1361 $ hg ci -R s -Am4
1361 $ hg ci -R s -Am4
1362 adding fm13
1362 adding fm13
1363 adding fn14
1363 adding fn14
1364 $ hg ci -Am5
1364 $ hg ci -Am5
1365 adding fn12
1365 adding fn12
1366 Adding with a pattern with -S also adds matches in subrepos
1366 Adding with a pattern with -S also adds matches in subrepos
1367 $ echo c15 > fm15
1367 $ echo c15 > fm15
1368 $ echo c16 > fn16
1368 $ echo c16 > fn16
1369 $ echo c17 > s/fm17
1369 $ echo c17 > s/fm17
1370 $ echo c18 > s/fn18
1370 $ echo c18 > s/fn18
1371 $ hg st -S
1371 $ hg st -S
1372 ? fm15
1372 ? fm15
1373 ? fn16
1373 ? fn16
1374 ? s/fm17
1374 ? s/fm17
1375 ? s/fn18
1375 ? s/fn18
1376 $ hg add -S 'glob:**fm*'
1376 $ hg add -S 'glob:**fm*'
1377 adding fm15
1377 adding fm15
1378 adding s/fm17 (glob)
1378 adding s/fm17 (glob)
1379 $ hg st -S
1379 $ hg st -S
1380 A fm15
1380 A fm15
1381 A s/fm17
1381 A s/fm17
1382 ? fn16
1382 ? fn16
1383 ? s/fn18
1383 ? s/fn18
1384 $ hg ci -R s -Am5
1384 $ hg ci -R s -Am5
1385 adding fn18
1385 adding fn18
1386 $ hg ci -Am6
1386 $ hg ci -Am6
1387 adding fn16
1387 adding fn16
1388
1388
1389 Test behavior of forget for explicit path in subrepo:
1389 Test behavior of forget for explicit path in subrepo:
1390 Forgetting an explicit path in a subrepo untracks the file
1390 Forgetting an explicit path in a subrepo untracks the file
1391 $ echo c19 > s/f19
1391 $ echo c19 > s/f19
1392 $ hg add s/f19
1392 $ hg add s/f19
1393 $ hg st -S
1393 $ hg st -S
1394 A s/f19
1394 A s/f19
1395 $ hg forget s/f19
1395 $ hg forget s/f19
1396 $ hg st -S
1396 $ hg st -S
1397 ? s/f19
1397 ? s/f19
1398 $ rm s/f19
1398 $ rm s/f19
1399 $ cd ..
1399 $ cd ..
1400
1400
1401 Courtesy phases synchronisation to publishing server does not block the push
1401 Courtesy phases synchronisation to publishing server does not block the push
1402 (issue3781)
1402 (issue3781)
1403
1403
1404 $ cp -r main issue3781
1404 $ cp -r main issue3781
1405 $ cp -r main issue3781-dest
1405 $ cp -r main issue3781-dest
1406 $ cd issue3781-dest/s
1406 $ cd issue3781-dest/s
1407 $ hg phase tip # show we have draft changeset
1407 $ hg phase tip # show we have draft changeset
1408 5: draft
1408 5: draft
1409 $ chmod a-w .hg/store/phaseroots # prevent phase push
1409 $ chmod a-w .hg/store/phaseroots # prevent phase push
1410 $ cd ../../issue3781
1410 $ cd ../../issue3781
1411 $ cat >> .hg/hgrc << EOF
1411 $ cat >> .hg/hgrc << EOF
1412 > [paths]
1412 > [paths]
1413 > default=../issue3781-dest/
1413 > default=../issue3781-dest/
1414 > EOF
1414 > EOF
1415 $ hg push
1415 $ hg push
1416 pushing to $TESTTMP/issue3781-dest (glob)
1416 pushing to $TESTTMP/issue3781-dest (glob)
1417 pushing subrepo s to $TESTTMP/issue3781-dest/s
1417 pushing subrepo s to $TESTTMP/issue3781-dest/s
1418 searching for changes
1418 searching for changes
1419 no changes found
1419 no changes found
1420 searching for changes
1420 searching for changes
1421 no changes found
1421 no changes found
1422 [1]
1422 [1]
1423 $ cd ..
1423 $ cd ..
1424
1424
1425 Test phase choice for newly created commit with "phases.subrepochecks"
1425 Test phase choice for newly created commit with "phases.subrepochecks"
1426 configuration
1426 configuration
1427
1427
1428 $ cd t
1428 $ cd t
1429 $ hg update -q -r 12
1429 $ hg update -q -r 12
1430
1430
1431 $ cat >> s/ss/.hg/hgrc <<EOF
1431 $ cat >> s/ss/.hg/hgrc <<EOF
1432 > [phases]
1432 > [phases]
1433 > new-commit = secret
1433 > new-commit = secret
1434 > EOF
1434 > EOF
1435 $ cat >> s/.hg/hgrc <<EOF
1435 $ cat >> s/.hg/hgrc <<EOF
1436 > [phases]
1436 > [phases]
1437 > new-commit = draft
1437 > new-commit = draft
1438 > EOF
1438 > EOF
1439 $ echo phasecheck1 >> s/ss/a
1439 $ echo phasecheck1 >> s/ss/a
1440 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1440 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1441 committing subrepository ss
1441 committing subrepository ss
1442 transaction abort!
1442 transaction abort!
1443 rollback completed
1443 rollback completed
1444 abort: can't commit in draft phase conflicting secret from subrepository ss
1444 abort: can't commit in draft phase conflicting secret from subrepository ss
1445 [255]
1445 [255]
1446 $ echo phasecheck2 >> s/ss/a
1446 $ echo phasecheck2 >> s/ss/a
1447 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1447 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1448 committing subrepository ss
1448 committing subrepository ss
1449 $ hg -R s/ss phase tip
1449 $ hg -R s/ss phase tip
1450 3: secret
1450 3: secret
1451 $ hg -R s phase tip
1451 $ hg -R s phase tip
1452 6: draft
1452 6: draft
1453 $ echo phasecheck3 >> s/ss/a
1453 $ echo phasecheck3 >> s/ss/a
1454 $ hg -R s commit -S -m phasecheck3
1454 $ hg -R s commit -S -m phasecheck3
1455 committing subrepository ss
1455 committing subrepository ss
1456 warning: changes are committed in secret phase from subrepository ss
1456 warning: changes are committed in secret phase from subrepository ss
1457 $ hg -R s/ss phase tip
1457 $ hg -R s/ss phase tip
1458 4: secret
1458 4: secret
1459 $ hg -R s phase tip
1459 $ hg -R s phase tip
1460 7: secret
1460 7: secret
1461
1461
1462 $ cat >> t/.hg/hgrc <<EOF
1462 $ cat >> t/.hg/hgrc <<EOF
1463 > [phases]
1463 > [phases]
1464 > new-commit = draft
1464 > new-commit = draft
1465 > EOF
1465 > EOF
1466 $ cat >> .hg/hgrc <<EOF
1466 $ cat >> .hg/hgrc <<EOF
1467 > [phases]
1467 > [phases]
1468 > new-commit = public
1468 > new-commit = public
1469 > EOF
1469 > EOF
1470 $ echo phasecheck4 >> s/ss/a
1470 $ echo phasecheck4 >> s/ss/a
1471 $ echo phasecheck4 >> t/t
1471 $ echo phasecheck4 >> t/t
1472 $ hg commit -S -m phasecheck4
1472 $ hg commit -S -m phasecheck4
1473 committing subrepository s
1473 committing subrepository s
1474 committing subrepository s/ss (glob)
1474 committing subrepository s/ss (glob)
1475 warning: changes are committed in secret phase from subrepository ss
1475 warning: changes are committed in secret phase from subrepository ss
1476 committing subrepository t
1476 committing subrepository t
1477 warning: changes are committed in secret phase from subrepository s
1477 warning: changes are committed in secret phase from subrepository s
1478 created new head
1478 created new head
1479 $ hg -R s/ss phase tip
1479 $ hg -R s/ss phase tip
1480 5: secret
1480 5: secret
1481 $ hg -R s phase tip
1481 $ hg -R s phase tip
1482 8: secret
1482 8: secret
1483 $ hg -R t phase tip
1483 $ hg -R t phase tip
1484 6: draft
1484 6: draft
1485 $ hg phase tip
1485 $ hg phase tip
1486 15: secret
1486 15: secret
1487
1487
1488 $ cd ..
1488 $ cd ..
1489
1489
1490
1490
1491 Test that commit --secret works on both repo and subrepo (issue4182)
1491 Test that commit --secret works on both repo and subrepo (issue4182)
1492
1492
1493 $ cd main
1493 $ cd main
1494 $ echo secret >> b
1494 $ echo secret >> b
1495 $ echo secret >> s/b
1495 $ echo secret >> s/b
1496 $ hg commit --secret --subrepo -m "secret"
1496 $ hg commit --secret --subrepo -m "secret"
1497 committing subrepository s
1497 committing subrepository s
1498 $ hg phase -r .
1498 $ hg phase -r .
1499 6: secret
1499 6: secret
1500 $ cd s
1500 $ cd s
1501 $ hg phase -r .
1501 $ hg phase -r .
1502 6: secret
1502 6: secret
1503 $ cd ../../
1503 $ cd ../../
1504
1504
1505 Test "subrepos" template keyword
1505 Test "subrepos" template keyword
1506
1506
1507 $ cd t
1507 $ cd t
1508 $ hg update -q 15
1508 $ hg update -q 15
1509 $ cat > .hgsub <<EOF
1509 $ cat > .hgsub <<EOF
1510 > s = s
1510 > s = s
1511 > EOF
1511 > EOF
1512 $ hg commit -m "16"
1512 $ hg commit -m "16"
1513 warning: changes are committed in secret phase from subrepository s
1513 warning: changes are committed in secret phase from subrepository s
1514
1514
1515 (addition of ".hgsub" itself)
1515 (addition of ".hgsub" itself)
1516
1516
1517 $ hg diff --nodates -c 1 .hgsubstate
1517 $ hg diff --nodates -c 1 .hgsubstate
1518 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1518 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1519 --- /dev/null
1519 --- /dev/null
1520 +++ b/.hgsubstate
1520 +++ b/.hgsubstate
1521 @@ -0,0 +1,1 @@
1521 @@ -0,0 +1,1 @@
1522 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1522 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1523 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1523 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1524 f7b1eb17ad24 000000000000
1524 f7b1eb17ad24 000000000000
1525 s
1525 s
1526
1526
1527 (modification of existing entry)
1527 (modification of existing entry)
1528
1528
1529 $ hg diff --nodates -c 2 .hgsubstate
1529 $ hg diff --nodates -c 2 .hgsubstate
1530 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1530 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1531 --- a/.hgsubstate
1531 --- a/.hgsubstate
1532 +++ b/.hgsubstate
1532 +++ b/.hgsubstate
1533 @@ -1,1 +1,1 @@
1533 @@ -1,1 +1,1 @@
1534 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1534 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1535 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1535 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1536 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1536 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1537 7cf8cfea66e4 000000000000
1537 7cf8cfea66e4 000000000000
1538 s
1538 s
1539
1539
1540 (addition of entry)
1540 (addition of entry)
1541
1541
1542 $ hg diff --nodates -c 5 .hgsubstate
1542 $ hg diff --nodates -c 5 .hgsubstate
1543 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1543 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1544 --- a/.hgsubstate
1544 --- a/.hgsubstate
1545 +++ b/.hgsubstate
1545 +++ b/.hgsubstate
1546 @@ -1,1 +1,2 @@
1546 @@ -1,1 +1,2 @@
1547 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1547 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1548 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1548 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1549 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1549 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1550 7cf8cfea66e4 000000000000
1550 7cf8cfea66e4 000000000000
1551 t
1551 t
1552
1552
1553 (removal of existing entry)
1553 (removal of existing entry)
1554
1554
1555 $ hg diff --nodates -c 16 .hgsubstate
1555 $ hg diff --nodates -c 16 .hgsubstate
1556 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1556 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1557 --- a/.hgsubstate
1557 --- a/.hgsubstate
1558 +++ b/.hgsubstate
1558 +++ b/.hgsubstate
1559 @@ -1,2 +1,1 @@
1559 @@ -1,2 +1,1 @@
1560 0731af8ca9423976d3743119d0865097c07bdc1b s
1560 0731af8ca9423976d3743119d0865097c07bdc1b s
1561 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1561 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1562 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1562 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1563 8bec38d2bd0b 000000000000
1563 8bec38d2bd0b 000000000000
1564 t
1564 t
1565
1565
1566 (merging)
1566 (merging)
1567
1567
1568 $ hg diff --nodates -c 9 .hgsubstate
1568 $ hg diff --nodates -c 9 .hgsubstate
1569 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1569 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1570 --- a/.hgsubstate
1570 --- a/.hgsubstate
1571 +++ b/.hgsubstate
1571 +++ b/.hgsubstate
1572 @@ -1,1 +1,2 @@
1572 @@ -1,1 +1,2 @@
1573 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1573 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1574 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1574 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1575 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1575 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1576 f6affe3fbfaa 1f14a2e2d3ec
1576 f6affe3fbfaa 1f14a2e2d3ec
1577 t
1577 t
1578
1578
1579 (removal of ".hgsub" itself)
1579 (removal of ".hgsub" itself)
1580
1580
1581 $ hg diff --nodates -c 8 .hgsubstate
1581 $ hg diff --nodates -c 8 .hgsubstate
1582 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1582 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1583 --- a/.hgsubstate
1583 --- a/.hgsubstate
1584 +++ /dev/null
1584 +++ /dev/null
1585 @@ -1,2 +0,0 @@
1585 @@ -1,2 +0,0 @@
1586 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1586 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1587 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1587 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1588 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1588 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1589 f94576341bcf 000000000000
1589 f94576341bcf 000000000000
1590
1590
1591 Test that '[paths]' is configured correctly at subrepo creation
1591 Test that '[paths]' is configured correctly at subrepo creation
1592
1592
1593 $ cd $TESTTMP/tc
1593 $ cd $TESTTMP/tc
1594 $ cat > .hgsub <<EOF
1594 $ cat > .hgsub <<EOF
1595 > # to clear bogus subrepo path 'bogus=[boguspath'
1595 > # to clear bogus subrepo path 'bogus=[boguspath'
1596 > s = s
1596 > s = s
1597 > t = t
1597 > t = t
1598 > EOF
1598 > EOF
1599 $ hg update -q --clean null
1599 $ hg update -q --clean null
1600 $ rm -rf s t
1600 $ rm -rf s t
1601 $ cat >> .hg/hgrc <<EOF
1601 $ cat >> .hg/hgrc <<EOF
1602 > [paths]
1602 > [paths]
1603 > default-push = /foo/bar
1603 > default-push = /foo/bar
1604 > EOF
1604 > EOF
1605 $ hg update -q
1605 $ hg update -q
1606 $ cat s/.hg/hgrc
1606 $ cat s/.hg/hgrc
1607 [paths]
1607 [paths]
1608 default = $TESTTMP/t/s
1608 default = $TESTTMP/t/s
1609 default-push = /foo/bar/s
1609 default-push = /foo/bar/s
1610 $ cat s/ss/.hg/hgrc
1610 $ cat s/ss/.hg/hgrc
1611 [paths]
1611 [paths]
1612 default = $TESTTMP/t/s/ss
1612 default = $TESTTMP/t/s/ss
1613 default-push = /foo/bar/s/ss
1613 default-push = /foo/bar/s/ss
1614 $ cat t/.hg/hgrc
1614 $ cat t/.hg/hgrc
1615 [paths]
1615 [paths]
1616 default = $TESTTMP/t/t
1616 default = $TESTTMP/t/t
1617 default-push = /foo/bar/t
1617 default-push = /foo/bar/t
1618 $ cd ..
1618 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now