##// END OF EJS Templates
subrepo: move prompts out of the if (issue5505)...
Simon Farnsworth -
r31516:2915cc1d stable
parent child Browse files
Show More
@@ -1,1960 +1,1965 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import copy
10 import copy
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import posixpath
14 import posixpath
15 import re
15 import re
16 import stat
16 import stat
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tarfile
19 import tarfile
20 import xml.dom.minidom
20 import xml.dom.minidom
21
21
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 cmdutil,
25 cmdutil,
26 config,
26 config,
27 encoding,
27 encoding,
28 error,
28 error,
29 exchange,
29 exchange,
30 filemerge,
30 filemerge,
31 match as matchmod,
31 match as matchmod,
32 node,
32 node,
33 pathutil,
33 pathutil,
34 phases,
34 phases,
35 pycompat,
35 pycompat,
36 scmutil,
36 scmutil,
37 util,
37 util,
38 )
38 )
39
39
40 hg = None
40 hg = None
41 propertycache = util.propertycache
41 propertycache = util.propertycache
42
42
43 nullstate = ('', '', 'empty')
43 nullstate = ('', '', 'empty')
44
44
45 def _expandedabspath(path):
45 def _expandedabspath(path):
46 '''
46 '''
47 get a path or url and if it is a path expand it and return an absolute path
47 get a path or url and if it is a path expand it and return an absolute path
48 '''
48 '''
49 expandedpath = util.urllocalpath(util.expandpath(path))
49 expandedpath = util.urllocalpath(util.expandpath(path))
50 u = util.url(expandedpath)
50 u = util.url(expandedpath)
51 if not u.scheme:
51 if not u.scheme:
52 path = util.normpath(os.path.abspath(u.path))
52 path = util.normpath(os.path.abspath(u.path))
53 return path
53 return path
54
54
55 def _getstorehashcachename(remotepath):
55 def _getstorehashcachename(remotepath):
56 '''get a unique filename for the store hash cache of a remote repository'''
56 '''get a unique filename for the store hash cache of a remote repository'''
57 return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
57 return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12]
58
58
59 class SubrepoAbort(error.Abort):
59 class SubrepoAbort(error.Abort):
60 """Exception class used to avoid handling a subrepo error more than once"""
60 """Exception class used to avoid handling a subrepo error more than once"""
61 def __init__(self, *args, **kw):
61 def __init__(self, *args, **kw):
62 self.subrepo = kw.pop('subrepo', None)
62 self.subrepo = kw.pop('subrepo', None)
63 self.cause = kw.pop('cause', None)
63 self.cause = kw.pop('cause', None)
64 error.Abort.__init__(self, *args, **kw)
64 error.Abort.__init__(self, *args, **kw)
65
65
66 def annotatesubrepoerror(func):
66 def annotatesubrepoerror(func):
67 def decoratedmethod(self, *args, **kargs):
67 def decoratedmethod(self, *args, **kargs):
68 try:
68 try:
69 res = func(self, *args, **kargs)
69 res = func(self, *args, **kargs)
70 except SubrepoAbort as ex:
70 except SubrepoAbort as ex:
71 # This exception has already been handled
71 # This exception has already been handled
72 raise ex
72 raise ex
73 except error.Abort as ex:
73 except error.Abort as ex:
74 subrepo = subrelpath(self)
74 subrepo = subrelpath(self)
75 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
75 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
76 # avoid handling this exception by raising a SubrepoAbort exception
76 # avoid handling this exception by raising a SubrepoAbort exception
77 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
77 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
78 cause=sys.exc_info())
78 cause=sys.exc_info())
79 return res
79 return res
80 return decoratedmethod
80 return decoratedmethod
81
81
82 def state(ctx, ui):
82 def state(ctx, ui):
83 """return a state dict, mapping subrepo paths configured in .hgsub
83 """return a state dict, mapping subrepo paths configured in .hgsub
84 to tuple: (source from .hgsub, revision from .hgsubstate, kind
84 to tuple: (source from .hgsub, revision from .hgsubstate, kind
85 (key in types dict))
85 (key in types dict))
86 """
86 """
87 p = config.config()
87 p = config.config()
88 repo = ctx.repo()
88 repo = ctx.repo()
89 def read(f, sections=None, remap=None):
89 def read(f, sections=None, remap=None):
90 if f in ctx:
90 if f in ctx:
91 try:
91 try:
92 data = ctx[f].data()
92 data = ctx[f].data()
93 except IOError as err:
93 except IOError as err:
94 if err.errno != errno.ENOENT:
94 if err.errno != errno.ENOENT:
95 raise
95 raise
96 # handle missing subrepo spec files as removed
96 # handle missing subrepo spec files as removed
97 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
97 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
98 repo.pathto(f))
98 repo.pathto(f))
99 return
99 return
100 p.parse(f, data, sections, remap, read)
100 p.parse(f, data, sections, remap, read)
101 else:
101 else:
102 raise error.Abort(_("subrepo spec file \'%s\' not found") %
102 raise error.Abort(_("subrepo spec file \'%s\' not found") %
103 repo.pathto(f))
103 repo.pathto(f))
104 if '.hgsub' in ctx:
104 if '.hgsub' in ctx:
105 read('.hgsub')
105 read('.hgsub')
106
106
107 for path, src in ui.configitems('subpaths'):
107 for path, src in ui.configitems('subpaths'):
108 p.set('subpaths', path, src, ui.configsource('subpaths', path))
108 p.set('subpaths', path, src, ui.configsource('subpaths', path))
109
109
110 rev = {}
110 rev = {}
111 if '.hgsubstate' in ctx:
111 if '.hgsubstate' in ctx:
112 try:
112 try:
113 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
113 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
114 l = l.lstrip()
114 l = l.lstrip()
115 if not l:
115 if not l:
116 continue
116 continue
117 try:
117 try:
118 revision, path = l.split(" ", 1)
118 revision, path = l.split(" ", 1)
119 except ValueError:
119 except ValueError:
120 raise error.Abort(_("invalid subrepository revision "
120 raise error.Abort(_("invalid subrepository revision "
121 "specifier in \'%s\' line %d")
121 "specifier in \'%s\' line %d")
122 % (repo.pathto('.hgsubstate'), (i + 1)))
122 % (repo.pathto('.hgsubstate'), (i + 1)))
123 rev[path] = revision
123 rev[path] = revision
124 except IOError as err:
124 except IOError as err:
125 if err.errno != errno.ENOENT:
125 if err.errno != errno.ENOENT:
126 raise
126 raise
127
127
128 def remap(src):
128 def remap(src):
129 for pattern, repl in p.items('subpaths'):
129 for pattern, repl in p.items('subpaths'):
130 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
130 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
131 # does a string decode.
131 # does a string decode.
132 repl = repl.encode('string-escape')
132 repl = repl.encode('string-escape')
133 # However, we still want to allow back references to go
133 # However, we still want to allow back references to go
134 # through unharmed, so we turn r'\\1' into r'\1'. Again,
134 # through unharmed, so we turn r'\\1' into r'\1'. Again,
135 # extra escapes are needed because re.sub string decodes.
135 # extra escapes are needed because re.sub string decodes.
136 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
136 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
137 try:
137 try:
138 src = re.sub(pattern, repl, src, 1)
138 src = re.sub(pattern, repl, src, 1)
139 except re.error as e:
139 except re.error as e:
140 raise error.Abort(_("bad subrepository pattern in %s: %s")
140 raise error.Abort(_("bad subrepository pattern in %s: %s")
141 % (p.source('subpaths', pattern), e))
141 % (p.source('subpaths', pattern), e))
142 return src
142 return src
143
143
144 state = {}
144 state = {}
145 for path, src in p[''].items():
145 for path, src in p[''].items():
146 kind = 'hg'
146 kind = 'hg'
147 if src.startswith('['):
147 if src.startswith('['):
148 if ']' not in src:
148 if ']' not in src:
149 raise error.Abort(_('missing ] in subrepo source'))
149 raise error.Abort(_('missing ] in subrepo source'))
150 kind, src = src.split(']', 1)
150 kind, src = src.split(']', 1)
151 kind = kind[1:]
151 kind = kind[1:]
152 src = src.lstrip() # strip any extra whitespace after ']'
152 src = src.lstrip() # strip any extra whitespace after ']'
153
153
154 if not util.url(src).isabs():
154 if not util.url(src).isabs():
155 parent = _abssource(repo, abort=False)
155 parent = _abssource(repo, abort=False)
156 if parent:
156 if parent:
157 parent = util.url(parent)
157 parent = util.url(parent)
158 parent.path = posixpath.join(parent.path or '', src)
158 parent.path = posixpath.join(parent.path or '', src)
159 parent.path = posixpath.normpath(parent.path)
159 parent.path = posixpath.normpath(parent.path)
160 joined = str(parent)
160 joined = str(parent)
161 # Remap the full joined path and use it if it changes,
161 # Remap the full joined path and use it if it changes,
162 # else remap the original source.
162 # else remap the original source.
163 remapped = remap(joined)
163 remapped = remap(joined)
164 if remapped == joined:
164 if remapped == joined:
165 src = remap(src)
165 src = remap(src)
166 else:
166 else:
167 src = remapped
167 src = remapped
168
168
169 src = remap(src)
169 src = remap(src)
170 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
170 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
171
171
172 return state
172 return state
173
173
174 def writestate(repo, state):
174 def writestate(repo, state):
175 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
175 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
176 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
176 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
177 if state[s][1] != nullstate[1]]
177 if state[s][1] != nullstate[1]]
178 repo.wwrite('.hgsubstate', ''.join(lines), '')
178 repo.wwrite('.hgsubstate', ''.join(lines), '')
179
179
180 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
180 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
181 """delegated from merge.applyupdates: merging of .hgsubstate file
181 """delegated from merge.applyupdates: merging of .hgsubstate file
182 in working context, merging context and ancestor context"""
182 in working context, merging context and ancestor context"""
183 if mctx == actx: # backwards?
183 if mctx == actx: # backwards?
184 actx = wctx.p1()
184 actx = wctx.p1()
185 s1 = wctx.substate
185 s1 = wctx.substate
186 s2 = mctx.substate
186 s2 = mctx.substate
187 sa = actx.substate
187 sa = actx.substate
188 sm = {}
188 sm = {}
189
189
190 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
190 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
191
191
192 def debug(s, msg, r=""):
192 def debug(s, msg, r=""):
193 if r:
193 if r:
194 r = "%s:%s:%s" % r
194 r = "%s:%s:%s" % r
195 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
195 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
196
196
197 promptssrc = filemerge.partextras(labels)
197 for s, l in sorted(s1.iteritems()):
198 for s, l in sorted(s1.iteritems()):
199 prompts = None
198 a = sa.get(s, nullstate)
200 a = sa.get(s, nullstate)
199 ld = l # local state with possible dirty flag for compares
201 ld = l # local state with possible dirty flag for compares
200 if wctx.sub(s).dirty():
202 if wctx.sub(s).dirty():
201 ld = (l[0], l[1] + "+")
203 ld = (l[0], l[1] + "+")
202 if wctx == actx: # overwrite
204 if wctx == actx: # overwrite
203 a = ld
205 a = ld
204
206
207 prompts = promptssrc.copy()
208 prompts['s'] = s
205 if s in s2:
209 if s in s2:
206 prompts = filemerge.partextras(labels)
207 prompts['s'] = s
208 r = s2[s]
210 r = s2[s]
209 if ld == r or r == a: # no change or local is newer
211 if ld == r or r == a: # no change or local is newer
210 sm[s] = l
212 sm[s] = l
211 continue
213 continue
212 elif ld == a: # other side changed
214 elif ld == a: # other side changed
213 debug(s, "other changed, get", r)
215 debug(s, "other changed, get", r)
214 wctx.sub(s).get(r, overwrite)
216 wctx.sub(s).get(r, overwrite)
215 sm[s] = r
217 sm[s] = r
216 elif ld[0] != r[0]: # sources differ
218 elif ld[0] != r[0]: # sources differ
217 prompts['lo'] = l[0]
219 prompts['lo'] = l[0]
218 prompts['ro'] = r[0]
220 prompts['ro'] = r[0]
219 if repo.ui.promptchoice(
221 if repo.ui.promptchoice(
220 _(' subrepository sources for %(s)s differ\n'
222 _(' subrepository sources for %(s)s differ\n'
221 'use (l)ocal%(l)s source (%(lo)s)'
223 'use (l)ocal%(l)s source (%(lo)s)'
222 ' or (r)emote%(o)s source (%(ro)s)?'
224 ' or (r)emote%(o)s source (%(ro)s)?'
223 '$$ &Local $$ &Remote') % prompts, 0):
225 '$$ &Local $$ &Remote') % prompts, 0):
224 debug(s, "prompt changed, get", r)
226 debug(s, "prompt changed, get", r)
225 wctx.sub(s).get(r, overwrite)
227 wctx.sub(s).get(r, overwrite)
226 sm[s] = r
228 sm[s] = r
227 elif ld[1] == a[1]: # local side is unchanged
229 elif ld[1] == a[1]: # local side is unchanged
228 debug(s, "other side changed, get", r)
230 debug(s, "other side changed, get", r)
229 wctx.sub(s).get(r, overwrite)
231 wctx.sub(s).get(r, overwrite)
230 sm[s] = r
232 sm[s] = r
231 else:
233 else:
232 debug(s, "both sides changed")
234 debug(s, "both sides changed")
233 srepo = wctx.sub(s)
235 srepo = wctx.sub(s)
234 prompts['sl'] = srepo.shortid(l[1])
236 prompts['sl'] = srepo.shortid(l[1])
235 prompts['sr'] = srepo.shortid(r[1])
237 prompts['sr'] = srepo.shortid(r[1])
236 option = repo.ui.promptchoice(
238 option = repo.ui.promptchoice(
237 _(' subrepository %(s)s diverged (local revision: %(sl)s, '
239 _(' subrepository %(s)s diverged (local revision: %(sl)s, '
238 'remote revision: %(sr)s)\n'
240 'remote revision: %(sr)s)\n'
239 '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?'
241 '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?'
240 '$$ &Merge $$ &Local $$ &Remote')
242 '$$ &Merge $$ &Local $$ &Remote')
241 % prompts, 0)
243 % prompts, 0)
242 if option == 0:
244 if option == 0:
243 wctx.sub(s).merge(r)
245 wctx.sub(s).merge(r)
244 sm[s] = l
246 sm[s] = l
245 debug(s, "merge with", r)
247 debug(s, "merge with", r)
246 elif option == 1:
248 elif option == 1:
247 sm[s] = l
249 sm[s] = l
248 debug(s, "keep local subrepo revision", l)
250 debug(s, "keep local subrepo revision", l)
249 else:
251 else:
250 wctx.sub(s).get(r, overwrite)
252 wctx.sub(s).get(r, overwrite)
251 sm[s] = r
253 sm[s] = r
252 debug(s, "get remote subrepo revision", r)
254 debug(s, "get remote subrepo revision", r)
253 elif ld == a: # remote removed, local unchanged
255 elif ld == a: # remote removed, local unchanged
254 debug(s, "remote removed, remove")
256 debug(s, "remote removed, remove")
255 wctx.sub(s).remove()
257 wctx.sub(s).remove()
256 elif a == nullstate: # not present in remote or ancestor
258 elif a == nullstate: # not present in remote or ancestor
257 debug(s, "local added, keep")
259 debug(s, "local added, keep")
258 sm[s] = l
260 sm[s] = l
259 continue
261 continue
260 else:
262 else:
261 if repo.ui.promptchoice(
263 if repo.ui.promptchoice(
262 _(' local%(l)s changed subrepository %(s)s'
264 _(' local%(l)s changed subrepository %(s)s'
263 ' which remote%(o)s removed\n'
265 ' which remote%(o)s removed\n'
264 'use (c)hanged version or (d)elete?'
266 'use (c)hanged version or (d)elete?'
265 '$$ &Changed $$ &Delete') % prompts, 0):
267 '$$ &Changed $$ &Delete') % prompts, 0):
266 debug(s, "prompt remove")
268 debug(s, "prompt remove")
267 wctx.sub(s).remove()
269 wctx.sub(s).remove()
268
270
269 for s, r in sorted(s2.items()):
271 for s, r in sorted(s2.items()):
272 prompts = None
270 if s in s1:
273 if s in s1:
271 continue
274 continue
272 elif s not in sa:
275 elif s not in sa:
273 debug(s, "remote added, get", r)
276 debug(s, "remote added, get", r)
274 mctx.sub(s).get(r)
277 mctx.sub(s).get(r)
275 sm[s] = r
278 sm[s] = r
276 elif r != sa[s]:
279 elif r != sa[s]:
280 prompts = promptssrc.copy()
281 prompts['s'] = s
277 if repo.ui.promptchoice(
282 if repo.ui.promptchoice(
278 _(' remote%(o)s changed subrepository %(s)s'
283 _(' remote%(o)s changed subrepository %(s)s'
279 ' which local%(l)s removed\n'
284 ' which local%(l)s removed\n'
280 'use (c)hanged version or (d)elete?'
285 'use (c)hanged version or (d)elete?'
281 '$$ &Changed $$ &Delete') % prompts, 0) == 0:
286 '$$ &Changed $$ &Delete') % prompts, 0) == 0:
282 debug(s, "prompt recreate", r)
287 debug(s, "prompt recreate", r)
283 mctx.sub(s).get(r)
288 mctx.sub(s).get(r)
284 sm[s] = r
289 sm[s] = r
285
290
286 # record merged .hgsubstate
291 # record merged .hgsubstate
287 writestate(repo, sm)
292 writestate(repo, sm)
288 return sm
293 return sm
289
294
290 def _updateprompt(ui, sub, dirty, local, remote):
295 def _updateprompt(ui, sub, dirty, local, remote):
291 if dirty:
296 if dirty:
292 msg = (_(' subrepository sources for %s differ\n'
297 msg = (_(' subrepository sources for %s differ\n'
293 'use (l)ocal source (%s) or (r)emote source (%s)?'
298 'use (l)ocal source (%s) or (r)emote source (%s)?'
294 '$$ &Local $$ &Remote')
299 '$$ &Local $$ &Remote')
295 % (subrelpath(sub), local, remote))
300 % (subrelpath(sub), local, remote))
296 else:
301 else:
297 msg = (_(' subrepository sources for %s differ (in checked out '
302 msg = (_(' subrepository sources for %s differ (in checked out '
298 'version)\n'
303 'version)\n'
299 'use (l)ocal source (%s) or (r)emote source (%s)?'
304 'use (l)ocal source (%s) or (r)emote source (%s)?'
300 '$$ &Local $$ &Remote')
305 '$$ &Local $$ &Remote')
301 % (subrelpath(sub), local, remote))
306 % (subrelpath(sub), local, remote))
302 return ui.promptchoice(msg, 0)
307 return ui.promptchoice(msg, 0)
303
308
304 def reporelpath(repo):
309 def reporelpath(repo):
305 """return path to this (sub)repo as seen from outermost repo"""
310 """return path to this (sub)repo as seen from outermost repo"""
306 parent = repo
311 parent = repo
307 while util.safehasattr(parent, '_subparent'):
312 while util.safehasattr(parent, '_subparent'):
308 parent = parent._subparent
313 parent = parent._subparent
309 return repo.root[len(pathutil.normasprefix(parent.root)):]
314 return repo.root[len(pathutil.normasprefix(parent.root)):]
310
315
311 def subrelpath(sub):
316 def subrelpath(sub):
312 """return path to this subrepo as seen from outermost repo"""
317 """return path to this subrepo as seen from outermost repo"""
313 return sub._relpath
318 return sub._relpath
314
319
315 def _abssource(repo, push=False, abort=True):
320 def _abssource(repo, push=False, abort=True):
316 """return pull/push path of repo - either based on parent repo .hgsub info
321 """return pull/push path of repo - either based on parent repo .hgsub info
317 or on the top repo config. Abort or return None if no source found."""
322 or on the top repo config. Abort or return None if no source found."""
318 if util.safehasattr(repo, '_subparent'):
323 if util.safehasattr(repo, '_subparent'):
319 source = util.url(repo._subsource)
324 source = util.url(repo._subsource)
320 if source.isabs():
325 if source.isabs():
321 return str(source)
326 return str(source)
322 source.path = posixpath.normpath(source.path)
327 source.path = posixpath.normpath(source.path)
323 parent = _abssource(repo._subparent, push, abort=False)
328 parent = _abssource(repo._subparent, push, abort=False)
324 if parent:
329 if parent:
325 parent = util.url(util.pconvert(parent))
330 parent = util.url(util.pconvert(parent))
326 parent.path = posixpath.join(parent.path or '', source.path)
331 parent.path = posixpath.join(parent.path or '', source.path)
327 parent.path = posixpath.normpath(parent.path)
332 parent.path = posixpath.normpath(parent.path)
328 return str(parent)
333 return str(parent)
329 else: # recursion reached top repo
334 else: # recursion reached top repo
330 if util.safehasattr(repo, '_subtoppath'):
335 if util.safehasattr(repo, '_subtoppath'):
331 return repo._subtoppath
336 return repo._subtoppath
332 if push and repo.ui.config('paths', 'default-push'):
337 if push and repo.ui.config('paths', 'default-push'):
333 return repo.ui.config('paths', 'default-push')
338 return repo.ui.config('paths', 'default-push')
334 if repo.ui.config('paths', 'default'):
339 if repo.ui.config('paths', 'default'):
335 return repo.ui.config('paths', 'default')
340 return repo.ui.config('paths', 'default')
336 if repo.shared():
341 if repo.shared():
337 # chop off the .hg component to get the default path form
342 # chop off the .hg component to get the default path form
338 return os.path.dirname(repo.sharedpath)
343 return os.path.dirname(repo.sharedpath)
339 if abort:
344 if abort:
340 raise error.Abort(_("default path for subrepository not found"))
345 raise error.Abort(_("default path for subrepository not found"))
341
346
342 def _sanitize(ui, vfs, ignore):
347 def _sanitize(ui, vfs, ignore):
343 for dirname, dirs, names in vfs.walk():
348 for dirname, dirs, names in vfs.walk():
344 for i, d in enumerate(dirs):
349 for i, d in enumerate(dirs):
345 if d.lower() == ignore:
350 if d.lower() == ignore:
346 del dirs[i]
351 del dirs[i]
347 break
352 break
348 if vfs.basename(dirname).lower() != '.hg':
353 if vfs.basename(dirname).lower() != '.hg':
349 continue
354 continue
350 for f in names:
355 for f in names:
351 if f.lower() == 'hgrc':
356 if f.lower() == 'hgrc':
352 ui.warn(_("warning: removing potentially hostile 'hgrc' "
357 ui.warn(_("warning: removing potentially hostile 'hgrc' "
353 "in '%s'\n") % vfs.join(dirname))
358 "in '%s'\n") % vfs.join(dirname))
354 vfs.unlink(vfs.reljoin(dirname, f))
359 vfs.unlink(vfs.reljoin(dirname, f))
355
360
356 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
361 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
357 """return instance of the right subrepo class for subrepo in path"""
362 """return instance of the right subrepo class for subrepo in path"""
358 # subrepo inherently violates our import layering rules
363 # subrepo inherently violates our import layering rules
359 # because it wants to make repo objects from deep inside the stack
364 # because it wants to make repo objects from deep inside the stack
360 # so we manually delay the circular imports to not break
365 # so we manually delay the circular imports to not break
361 # scripts that don't use our demand-loading
366 # scripts that don't use our demand-loading
362 global hg
367 global hg
363 from . import hg as h
368 from . import hg as h
364 hg = h
369 hg = h
365
370
366 pathutil.pathauditor(ctx.repo().root)(path)
371 pathutil.pathauditor(ctx.repo().root)(path)
367 state = ctx.substate[path]
372 state = ctx.substate[path]
368 if state[2] not in types:
373 if state[2] not in types:
369 raise error.Abort(_('unknown subrepo type %s') % state[2])
374 raise error.Abort(_('unknown subrepo type %s') % state[2])
370 if allowwdir:
375 if allowwdir:
371 state = (state[0], ctx.subrev(path), state[2])
376 state = (state[0], ctx.subrev(path), state[2])
372 return types[state[2]](ctx, path, state[:2], allowcreate)
377 return types[state[2]](ctx, path, state[:2], allowcreate)
373
378
374 def nullsubrepo(ctx, path, pctx):
379 def nullsubrepo(ctx, path, pctx):
375 """return an empty subrepo in pctx for the extant subrepo in ctx"""
380 """return an empty subrepo in pctx for the extant subrepo in ctx"""
376 # subrepo inherently violates our import layering rules
381 # subrepo inherently violates our import layering rules
377 # because it wants to make repo objects from deep inside the stack
382 # because it wants to make repo objects from deep inside the stack
378 # so we manually delay the circular imports to not break
383 # so we manually delay the circular imports to not break
379 # scripts that don't use our demand-loading
384 # scripts that don't use our demand-loading
380 global hg
385 global hg
381 from . import hg as h
386 from . import hg as h
382 hg = h
387 hg = h
383
388
384 pathutil.pathauditor(ctx.repo().root)(path)
389 pathutil.pathauditor(ctx.repo().root)(path)
385 state = ctx.substate[path]
390 state = ctx.substate[path]
386 if state[2] not in types:
391 if state[2] not in types:
387 raise error.Abort(_('unknown subrepo type %s') % state[2])
392 raise error.Abort(_('unknown subrepo type %s') % state[2])
388 subrev = ''
393 subrev = ''
389 if state[2] == 'hg':
394 if state[2] == 'hg':
390 subrev = "0" * 40
395 subrev = "0" * 40
391 return types[state[2]](pctx, path, (state[0], subrev), True)
396 return types[state[2]](pctx, path, (state[0], subrev), True)
392
397
393 def newcommitphase(ui, ctx):
398 def newcommitphase(ui, ctx):
394 commitphase = phases.newcommitphase(ui)
399 commitphase = phases.newcommitphase(ui)
395 substate = getattr(ctx, "substate", None)
400 substate = getattr(ctx, "substate", None)
396 if not substate:
401 if not substate:
397 return commitphase
402 return commitphase
398 check = ui.config('phases', 'checksubrepos', 'follow')
403 check = ui.config('phases', 'checksubrepos', 'follow')
399 if check not in ('ignore', 'follow', 'abort'):
404 if check not in ('ignore', 'follow', 'abort'):
400 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
405 raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
401 % (check))
406 % (check))
402 if check == 'ignore':
407 if check == 'ignore':
403 return commitphase
408 return commitphase
404 maxphase = phases.public
409 maxphase = phases.public
405 maxsub = None
410 maxsub = None
406 for s in sorted(substate):
411 for s in sorted(substate):
407 sub = ctx.sub(s)
412 sub = ctx.sub(s)
408 subphase = sub.phase(substate[s][1])
413 subphase = sub.phase(substate[s][1])
409 if maxphase < subphase:
414 if maxphase < subphase:
410 maxphase = subphase
415 maxphase = subphase
411 maxsub = s
416 maxsub = s
412 if commitphase < maxphase:
417 if commitphase < maxphase:
413 if check == 'abort':
418 if check == 'abort':
414 raise error.Abort(_("can't commit in %s phase"
419 raise error.Abort(_("can't commit in %s phase"
415 " conflicting %s from subrepository %s") %
420 " conflicting %s from subrepository %s") %
416 (phases.phasenames[commitphase],
421 (phases.phasenames[commitphase],
417 phases.phasenames[maxphase], maxsub))
422 phases.phasenames[maxphase], maxsub))
418 ui.warn(_("warning: changes are committed in"
423 ui.warn(_("warning: changes are committed in"
419 " %s phase from subrepository %s\n") %
424 " %s phase from subrepository %s\n") %
420 (phases.phasenames[maxphase], maxsub))
425 (phases.phasenames[maxphase], maxsub))
421 return maxphase
426 return maxphase
422 return commitphase
427 return commitphase
423
428
424 # subrepo classes need to implement the following abstract class:
429 # subrepo classes need to implement the following abstract class:
425
430
426 class abstractsubrepo(object):
431 class abstractsubrepo(object):
427
432
428 def __init__(self, ctx, path):
433 def __init__(self, ctx, path):
429 """Initialize abstractsubrepo part
434 """Initialize abstractsubrepo part
430
435
431 ``ctx`` is the context referring this subrepository in the
436 ``ctx`` is the context referring this subrepository in the
432 parent repository.
437 parent repository.
433
438
434 ``path`` is the path to this subrepository as seen from
439 ``path`` is the path to this subrepository as seen from
435 innermost repository.
440 innermost repository.
436 """
441 """
437 self.ui = ctx.repo().ui
442 self.ui = ctx.repo().ui
438 self._ctx = ctx
443 self._ctx = ctx
439 self._path = path
444 self._path = path
440
445
441 def storeclean(self, path):
446 def storeclean(self, path):
442 """
447 """
443 returns true if the repository has not changed since it was last
448 returns true if the repository has not changed since it was last
444 cloned from or pushed to a given repository.
449 cloned from or pushed to a given repository.
445 """
450 """
446 return False
451 return False
447
452
448 def dirty(self, ignoreupdate=False):
453 def dirty(self, ignoreupdate=False):
449 """returns true if the dirstate of the subrepo is dirty or does not
454 """returns true if the dirstate of the subrepo is dirty or does not
450 match current stored state. If ignoreupdate is true, only check
455 match current stored state. If ignoreupdate is true, only check
451 whether the subrepo has uncommitted changes in its dirstate.
456 whether the subrepo has uncommitted changes in its dirstate.
452 """
457 """
453 raise NotImplementedError
458 raise NotImplementedError
454
459
455 def dirtyreason(self, ignoreupdate=False):
460 def dirtyreason(self, ignoreupdate=False):
456 """return reason string if it is ``dirty()``
461 """return reason string if it is ``dirty()``
457
462
458 Returned string should have enough information for the message
463 Returned string should have enough information for the message
459 of exception.
464 of exception.
460
465
461 This returns None, otherwise.
466 This returns None, otherwise.
462 """
467 """
463 if self.dirty(ignoreupdate=ignoreupdate):
468 if self.dirty(ignoreupdate=ignoreupdate):
464 return _("uncommitted changes in subrepository '%s'"
469 return _("uncommitted changes in subrepository '%s'"
465 ) % subrelpath(self)
470 ) % subrelpath(self)
466
471
467 def bailifchanged(self, ignoreupdate=False, hint=None):
472 def bailifchanged(self, ignoreupdate=False, hint=None):
468 """raise Abort if subrepository is ``dirty()``
473 """raise Abort if subrepository is ``dirty()``
469 """
474 """
470 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
475 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
471 if dirtyreason:
476 if dirtyreason:
472 raise error.Abort(dirtyreason, hint=hint)
477 raise error.Abort(dirtyreason, hint=hint)
473
478
474 def basestate(self):
479 def basestate(self):
475 """current working directory base state, disregarding .hgsubstate
480 """current working directory base state, disregarding .hgsubstate
476 state and working directory modifications"""
481 state and working directory modifications"""
477 raise NotImplementedError
482 raise NotImplementedError
478
483
479 def checknested(self, path):
484 def checknested(self, path):
480 """check if path is a subrepository within this repository"""
485 """check if path is a subrepository within this repository"""
481 return False
486 return False
482
487
483 def commit(self, text, user, date):
488 def commit(self, text, user, date):
484 """commit the current changes to the subrepo with the given
489 """commit the current changes to the subrepo with the given
485 log message. Use given user and date if possible. Return the
490 log message. Use given user and date if possible. Return the
486 new state of the subrepo.
491 new state of the subrepo.
487 """
492 """
488 raise NotImplementedError
493 raise NotImplementedError
489
494
490 def phase(self, state):
495 def phase(self, state):
491 """returns phase of specified state in the subrepository.
496 """returns phase of specified state in the subrepository.
492 """
497 """
493 return phases.public
498 return phases.public
494
499
495 def remove(self):
500 def remove(self):
496 """remove the subrepo
501 """remove the subrepo
497
502
498 (should verify the dirstate is not dirty first)
503 (should verify the dirstate is not dirty first)
499 """
504 """
500 raise NotImplementedError
505 raise NotImplementedError
501
506
502 def get(self, state, overwrite=False):
507 def get(self, state, overwrite=False):
503 """run whatever commands are needed to put the subrepo into
508 """run whatever commands are needed to put the subrepo into
504 this state
509 this state
505 """
510 """
506 raise NotImplementedError
511 raise NotImplementedError
507
512
508 def merge(self, state):
513 def merge(self, state):
509 """merge currently-saved state with the new state."""
514 """merge currently-saved state with the new state."""
510 raise NotImplementedError
515 raise NotImplementedError
511
516
512 def push(self, opts):
517 def push(self, opts):
513 """perform whatever action is analogous to 'hg push'
518 """perform whatever action is analogous to 'hg push'
514
519
515 This may be a no-op on some systems.
520 This may be a no-op on some systems.
516 """
521 """
517 raise NotImplementedError
522 raise NotImplementedError
518
523
519 def add(self, ui, match, prefix, explicitonly, **opts):
524 def add(self, ui, match, prefix, explicitonly, **opts):
520 return []
525 return []
521
526
522 def addremove(self, matcher, prefix, opts, dry_run, similarity):
527 def addremove(self, matcher, prefix, opts, dry_run, similarity):
523 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
528 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
524 return 1
529 return 1
525
530
526 def cat(self, match, prefix, **opts):
531 def cat(self, match, prefix, **opts):
527 return 1
532 return 1
528
533
529 def status(self, rev2, **opts):
534 def status(self, rev2, **opts):
530 return scmutil.status([], [], [], [], [], [], [])
535 return scmutil.status([], [], [], [], [], [], [])
531
536
532 def diff(self, ui, diffopts, node2, match, prefix, **opts):
537 def diff(self, ui, diffopts, node2, match, prefix, **opts):
533 pass
538 pass
534
539
535 def outgoing(self, ui, dest, opts):
540 def outgoing(self, ui, dest, opts):
536 return 1
541 return 1
537
542
538 def incoming(self, ui, source, opts):
543 def incoming(self, ui, source, opts):
539 return 1
544 return 1
540
545
541 def files(self):
546 def files(self):
542 """return filename iterator"""
547 """return filename iterator"""
543 raise NotImplementedError
548 raise NotImplementedError
544
549
545 def filedata(self, name):
550 def filedata(self, name):
546 """return file data"""
551 """return file data"""
547 raise NotImplementedError
552 raise NotImplementedError
548
553
549 def fileflags(self, name):
554 def fileflags(self, name):
550 """return file flags"""
555 """return file flags"""
551 return ''
556 return ''
552
557
553 def getfileset(self, expr):
558 def getfileset(self, expr):
554 """Resolve the fileset expression for this repo"""
559 """Resolve the fileset expression for this repo"""
555 return set()
560 return set()
556
561
557 def printfiles(self, ui, m, fm, fmt, subrepos):
562 def printfiles(self, ui, m, fm, fmt, subrepos):
558 """handle the files command for this subrepo"""
563 """handle the files command for this subrepo"""
559 return 1
564 return 1
560
565
561 def archive(self, archiver, prefix, match=None):
566 def archive(self, archiver, prefix, match=None):
562 if match is not None:
567 if match is not None:
563 files = [f for f in self.files() if match(f)]
568 files = [f for f in self.files() if match(f)]
564 else:
569 else:
565 files = self.files()
570 files = self.files()
566 total = len(files)
571 total = len(files)
567 relpath = subrelpath(self)
572 relpath = subrelpath(self)
568 self.ui.progress(_('archiving (%s)') % relpath, 0,
573 self.ui.progress(_('archiving (%s)') % relpath, 0,
569 unit=_('files'), total=total)
574 unit=_('files'), total=total)
570 for i, name in enumerate(files):
575 for i, name in enumerate(files):
571 flags = self.fileflags(name)
576 flags = self.fileflags(name)
572 mode = 'x' in flags and 0o755 or 0o644
577 mode = 'x' in flags and 0o755 or 0o644
573 symlink = 'l' in flags
578 symlink = 'l' in flags
574 archiver.addfile(prefix + self._path + '/' + name,
579 archiver.addfile(prefix + self._path + '/' + name,
575 mode, symlink, self.filedata(name))
580 mode, symlink, self.filedata(name))
576 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
581 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
577 unit=_('files'), total=total)
582 unit=_('files'), total=total)
578 self.ui.progress(_('archiving (%s)') % relpath, None)
583 self.ui.progress(_('archiving (%s)') % relpath, None)
579 return total
584 return total
580
585
581 def walk(self, match):
586 def walk(self, match):
582 '''
587 '''
583 walk recursively through the directory tree, finding all files
588 walk recursively through the directory tree, finding all files
584 matched by the match function
589 matched by the match function
585 '''
590 '''
586 pass
591 pass
587
592
588 def forget(self, match, prefix):
593 def forget(self, match, prefix):
589 return ([], [])
594 return ([], [])
590
595
591 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
596 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
592 """remove the matched files from the subrepository and the filesystem,
597 """remove the matched files from the subrepository and the filesystem,
593 possibly by force and/or after the file has been removed from the
598 possibly by force and/or after the file has been removed from the
594 filesystem. Return 0 on success, 1 on any warning.
599 filesystem. Return 0 on success, 1 on any warning.
595 """
600 """
596 warnings.append(_("warning: removefiles not implemented (%s)")
601 warnings.append(_("warning: removefiles not implemented (%s)")
597 % self._path)
602 % self._path)
598 return 1
603 return 1
599
604
600 def revert(self, substate, *pats, **opts):
605 def revert(self, substate, *pats, **opts):
601 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
606 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
602 % (substate[0], substate[2]))
607 % (substate[0], substate[2]))
603 return []
608 return []
604
609
605 def shortid(self, revid):
610 def shortid(self, revid):
606 return revid
611 return revid
607
612
608 def verify(self):
613 def verify(self):
609 '''verify the integrity of the repository. Return 0 on success or
614 '''verify the integrity of the repository. Return 0 on success or
610 warning, 1 on any error.
615 warning, 1 on any error.
611 '''
616 '''
612 return 0
617 return 0
613
618
614 @propertycache
619 @propertycache
615 def wvfs(self):
620 def wvfs(self):
616 """return vfs to access the working directory of this subrepository
621 """return vfs to access the working directory of this subrepository
617 """
622 """
618 return scmutil.vfs(self._ctx.repo().wvfs.join(self._path))
623 return scmutil.vfs(self._ctx.repo().wvfs.join(self._path))
619
624
620 @propertycache
625 @propertycache
621 def _relpath(self):
626 def _relpath(self):
622 """return path to this subrepository as seen from outermost repository
627 """return path to this subrepository as seen from outermost repository
623 """
628 """
624 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
629 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
625
630
626 class hgsubrepo(abstractsubrepo):
631 class hgsubrepo(abstractsubrepo):
627 def __init__(self, ctx, path, state, allowcreate):
632 def __init__(self, ctx, path, state, allowcreate):
628 super(hgsubrepo, self).__init__(ctx, path)
633 super(hgsubrepo, self).__init__(ctx, path)
629 self._state = state
634 self._state = state
630 r = ctx.repo()
635 r = ctx.repo()
631 root = r.wjoin(path)
636 root = r.wjoin(path)
632 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
637 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
633 self._repo = hg.repository(r.baseui, root, create=create)
638 self._repo = hg.repository(r.baseui, root, create=create)
634
639
635 # Propagate the parent's --hidden option
640 # Propagate the parent's --hidden option
636 if r is r.unfiltered():
641 if r is r.unfiltered():
637 self._repo = self._repo.unfiltered()
642 self._repo = self._repo.unfiltered()
638
643
639 self.ui = self._repo.ui
644 self.ui = self._repo.ui
640 for s, k in [('ui', 'commitsubrepos')]:
645 for s, k in [('ui', 'commitsubrepos')]:
641 v = r.ui.config(s, k)
646 v = r.ui.config(s, k)
642 if v:
647 if v:
643 self.ui.setconfig(s, k, v, 'subrepo')
648 self.ui.setconfig(s, k, v, 'subrepo')
644 # internal config: ui._usedassubrepo
649 # internal config: ui._usedassubrepo
645 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
650 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
646 self._initrepo(r, state[0], create)
651 self._initrepo(r, state[0], create)
647
652
648 def storeclean(self, path):
653 def storeclean(self, path):
649 with self._repo.lock():
654 with self._repo.lock():
650 return self._storeclean(path)
655 return self._storeclean(path)
651
656
652 def _storeclean(self, path):
657 def _storeclean(self, path):
653 clean = True
658 clean = True
654 itercache = self._calcstorehash(path)
659 itercache = self._calcstorehash(path)
655 for filehash in self._readstorehashcache(path):
660 for filehash in self._readstorehashcache(path):
656 if filehash != next(itercache, None):
661 if filehash != next(itercache, None):
657 clean = False
662 clean = False
658 break
663 break
659 if clean:
664 if clean:
660 # if not empty:
665 # if not empty:
661 # the cached and current pull states have a different size
666 # the cached and current pull states have a different size
662 clean = next(itercache, None) is None
667 clean = next(itercache, None) is None
663 return clean
668 return clean
664
669
665 def _calcstorehash(self, remotepath):
670 def _calcstorehash(self, remotepath):
666 '''calculate a unique "store hash"
671 '''calculate a unique "store hash"
667
672
668 This method is used to to detect when there are changes that may
673 This method is used to to detect when there are changes that may
669 require a push to a given remote path.'''
674 require a push to a given remote path.'''
670 # sort the files that will be hashed in increasing (likely) file size
675 # sort the files that will be hashed in increasing (likely) file size
671 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
676 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
672 yield '# %s\n' % _expandedabspath(remotepath)
677 yield '# %s\n' % _expandedabspath(remotepath)
673 vfs = self._repo.vfs
678 vfs = self._repo.vfs
674 for relname in filelist:
679 for relname in filelist:
675 filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest()
680 filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest()
676 yield '%s = %s\n' % (relname, filehash)
681 yield '%s = %s\n' % (relname, filehash)
677
682
678 @propertycache
683 @propertycache
679 def _cachestorehashvfs(self):
684 def _cachestorehashvfs(self):
680 return scmutil.vfs(self._repo.join('cache/storehash'))
685 return scmutil.vfs(self._repo.join('cache/storehash'))
681
686
682 def _readstorehashcache(self, remotepath):
687 def _readstorehashcache(self, remotepath):
683 '''read the store hash cache for a given remote repository'''
688 '''read the store hash cache for a given remote repository'''
684 cachefile = _getstorehashcachename(remotepath)
689 cachefile = _getstorehashcachename(remotepath)
685 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
690 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
686
691
687 def _cachestorehash(self, remotepath):
692 def _cachestorehash(self, remotepath):
688 '''cache the current store hash
693 '''cache the current store hash
689
694
690 Each remote repo requires its own store hash cache, because a subrepo
695 Each remote repo requires its own store hash cache, because a subrepo
691 store may be "clean" versus a given remote repo, but not versus another
696 store may be "clean" versus a given remote repo, but not versus another
692 '''
697 '''
693 cachefile = _getstorehashcachename(remotepath)
698 cachefile = _getstorehashcachename(remotepath)
694 with self._repo.lock():
699 with self._repo.lock():
695 storehash = list(self._calcstorehash(remotepath))
700 storehash = list(self._calcstorehash(remotepath))
696 vfs = self._cachestorehashvfs
701 vfs = self._cachestorehashvfs
697 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
702 vfs.writelines(cachefile, storehash, mode='w', notindexed=True)
698
703
699 def _getctx(self):
704 def _getctx(self):
700 '''fetch the context for this subrepo revision, possibly a workingctx
705 '''fetch the context for this subrepo revision, possibly a workingctx
701 '''
706 '''
702 if self._ctx.rev() is None:
707 if self._ctx.rev() is None:
703 return self._repo[None] # workingctx if parent is workingctx
708 return self._repo[None] # workingctx if parent is workingctx
704 else:
709 else:
705 rev = self._state[1]
710 rev = self._state[1]
706 return self._repo[rev]
711 return self._repo[rev]
707
712
708 @annotatesubrepoerror
713 @annotatesubrepoerror
709 def _initrepo(self, parentrepo, source, create):
714 def _initrepo(self, parentrepo, source, create):
710 self._repo._subparent = parentrepo
715 self._repo._subparent = parentrepo
711 self._repo._subsource = source
716 self._repo._subsource = source
712
717
713 if create:
718 if create:
714 lines = ['[paths]\n']
719 lines = ['[paths]\n']
715
720
716 def addpathconfig(key, value):
721 def addpathconfig(key, value):
717 if value:
722 if value:
718 lines.append('%s = %s\n' % (key, value))
723 lines.append('%s = %s\n' % (key, value))
719 self.ui.setconfig('paths', key, value, 'subrepo')
724 self.ui.setconfig('paths', key, value, 'subrepo')
720
725
721 defpath = _abssource(self._repo, abort=False)
726 defpath = _abssource(self._repo, abort=False)
722 defpushpath = _abssource(self._repo, True, abort=False)
727 defpushpath = _abssource(self._repo, True, abort=False)
723 addpathconfig('default', defpath)
728 addpathconfig('default', defpath)
724 if defpath != defpushpath:
729 if defpath != defpushpath:
725 addpathconfig('default-push', defpushpath)
730 addpathconfig('default-push', defpushpath)
726
731
727 fp = self._repo.vfs("hgrc", "w", text=True)
732 fp = self._repo.vfs("hgrc", "w", text=True)
728 try:
733 try:
729 fp.write(''.join(lines))
734 fp.write(''.join(lines))
730 finally:
735 finally:
731 fp.close()
736 fp.close()
732
737
733 @annotatesubrepoerror
738 @annotatesubrepoerror
734 def add(self, ui, match, prefix, explicitonly, **opts):
739 def add(self, ui, match, prefix, explicitonly, **opts):
735 return cmdutil.add(ui, self._repo, match,
740 return cmdutil.add(ui, self._repo, match,
736 self.wvfs.reljoin(prefix, self._path),
741 self.wvfs.reljoin(prefix, self._path),
737 explicitonly, **opts)
742 explicitonly, **opts)
738
743
739 @annotatesubrepoerror
744 @annotatesubrepoerror
740 def addremove(self, m, prefix, opts, dry_run, similarity):
745 def addremove(self, m, prefix, opts, dry_run, similarity):
741 # In the same way as sub directories are processed, once in a subrepo,
746 # In the same way as sub directories are processed, once in a subrepo,
742 # always entry any of its subrepos. Don't corrupt the options that will
747 # always entry any of its subrepos. Don't corrupt the options that will
743 # be used to process sibling subrepos however.
748 # be used to process sibling subrepos however.
744 opts = copy.copy(opts)
749 opts = copy.copy(opts)
745 opts['subrepos'] = True
750 opts['subrepos'] = True
746 return scmutil.addremove(self._repo, m,
751 return scmutil.addremove(self._repo, m,
747 self.wvfs.reljoin(prefix, self._path), opts,
752 self.wvfs.reljoin(prefix, self._path), opts,
748 dry_run, similarity)
753 dry_run, similarity)
749
754
750 @annotatesubrepoerror
755 @annotatesubrepoerror
751 def cat(self, match, prefix, **opts):
756 def cat(self, match, prefix, **opts):
752 rev = self._state[1]
757 rev = self._state[1]
753 ctx = self._repo[rev]
758 ctx = self._repo[rev]
754 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
759 return cmdutil.cat(self.ui, self._repo, ctx, match, prefix, **opts)
755
760
756 @annotatesubrepoerror
761 @annotatesubrepoerror
757 def status(self, rev2, **opts):
762 def status(self, rev2, **opts):
758 try:
763 try:
759 rev1 = self._state[1]
764 rev1 = self._state[1]
760 ctx1 = self._repo[rev1]
765 ctx1 = self._repo[rev1]
761 ctx2 = self._repo[rev2]
766 ctx2 = self._repo[rev2]
762 return self._repo.status(ctx1, ctx2, **opts)
767 return self._repo.status(ctx1, ctx2, **opts)
763 except error.RepoLookupError as inst:
768 except error.RepoLookupError as inst:
764 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
769 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
765 % (inst, subrelpath(self)))
770 % (inst, subrelpath(self)))
766 return scmutil.status([], [], [], [], [], [], [])
771 return scmutil.status([], [], [], [], [], [], [])
767
772
768 @annotatesubrepoerror
773 @annotatesubrepoerror
769 def diff(self, ui, diffopts, node2, match, prefix, **opts):
774 def diff(self, ui, diffopts, node2, match, prefix, **opts):
770 try:
775 try:
771 node1 = node.bin(self._state[1])
776 node1 = node.bin(self._state[1])
772 # We currently expect node2 to come from substate and be
777 # We currently expect node2 to come from substate and be
773 # in hex format
778 # in hex format
774 if node2 is not None:
779 if node2 is not None:
775 node2 = node.bin(node2)
780 node2 = node.bin(node2)
776 cmdutil.diffordiffstat(ui, self._repo, diffopts,
781 cmdutil.diffordiffstat(ui, self._repo, diffopts,
777 node1, node2, match,
782 node1, node2, match,
778 prefix=posixpath.join(prefix, self._path),
783 prefix=posixpath.join(prefix, self._path),
779 listsubrepos=True, **opts)
784 listsubrepos=True, **opts)
780 except error.RepoLookupError as inst:
785 except error.RepoLookupError as inst:
781 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
786 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
782 % (inst, subrelpath(self)))
787 % (inst, subrelpath(self)))
783
788
784 @annotatesubrepoerror
789 @annotatesubrepoerror
785 def archive(self, archiver, prefix, match=None):
790 def archive(self, archiver, prefix, match=None):
786 self._get(self._state + ('hg',))
791 self._get(self._state + ('hg',))
787 total = abstractsubrepo.archive(self, archiver, prefix, match)
792 total = abstractsubrepo.archive(self, archiver, prefix, match)
788 rev = self._state[1]
793 rev = self._state[1]
789 ctx = self._repo[rev]
794 ctx = self._repo[rev]
790 for subpath in ctx.substate:
795 for subpath in ctx.substate:
791 s = subrepo(ctx, subpath, True)
796 s = subrepo(ctx, subpath, True)
792 submatch = matchmod.subdirmatcher(subpath, match)
797 submatch = matchmod.subdirmatcher(subpath, match)
793 total += s.archive(archiver, prefix + self._path + '/', submatch)
798 total += s.archive(archiver, prefix + self._path + '/', submatch)
794 return total
799 return total
795
800
796 @annotatesubrepoerror
801 @annotatesubrepoerror
797 def dirty(self, ignoreupdate=False):
802 def dirty(self, ignoreupdate=False):
798 r = self._state[1]
803 r = self._state[1]
799 if r == '' and not ignoreupdate: # no state recorded
804 if r == '' and not ignoreupdate: # no state recorded
800 return True
805 return True
801 w = self._repo[None]
806 w = self._repo[None]
802 if r != w.p1().hex() and not ignoreupdate:
807 if r != w.p1().hex() and not ignoreupdate:
803 # different version checked out
808 # different version checked out
804 return True
809 return True
805 return w.dirty() # working directory changed
810 return w.dirty() # working directory changed
806
811
807 def basestate(self):
812 def basestate(self):
808 return self._repo['.'].hex()
813 return self._repo['.'].hex()
809
814
810 def checknested(self, path):
815 def checknested(self, path):
811 return self._repo._checknested(self._repo.wjoin(path))
816 return self._repo._checknested(self._repo.wjoin(path))
812
817
813 @annotatesubrepoerror
818 @annotatesubrepoerror
814 def commit(self, text, user, date):
819 def commit(self, text, user, date):
815 # don't bother committing in the subrepo if it's only been
820 # don't bother committing in the subrepo if it's only been
816 # updated
821 # updated
817 if not self.dirty(True):
822 if not self.dirty(True):
818 return self._repo['.'].hex()
823 return self._repo['.'].hex()
819 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
824 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
820 n = self._repo.commit(text, user, date)
825 n = self._repo.commit(text, user, date)
821 if not n:
826 if not n:
822 return self._repo['.'].hex() # different version checked out
827 return self._repo['.'].hex() # different version checked out
823 return node.hex(n)
828 return node.hex(n)
824
829
825 @annotatesubrepoerror
830 @annotatesubrepoerror
826 def phase(self, state):
831 def phase(self, state):
827 return self._repo[state].phase()
832 return self._repo[state].phase()
828
833
829 @annotatesubrepoerror
834 @annotatesubrepoerror
830 def remove(self):
835 def remove(self):
831 # we can't fully delete the repository as it may contain
836 # we can't fully delete the repository as it may contain
832 # local-only history
837 # local-only history
833 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
838 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
834 hg.clean(self._repo, node.nullid, False)
839 hg.clean(self._repo, node.nullid, False)
835
840
836 def _get(self, state):
841 def _get(self, state):
837 source, revision, kind = state
842 source, revision, kind = state
838 if revision in self._repo.unfiltered():
843 if revision in self._repo.unfiltered():
839 return True
844 return True
840 self._repo._subsource = source
845 self._repo._subsource = source
841 srcurl = _abssource(self._repo)
846 srcurl = _abssource(self._repo)
842 other = hg.peer(self._repo, {}, srcurl)
847 other = hg.peer(self._repo, {}, srcurl)
843 if len(self._repo) == 0:
848 if len(self._repo) == 0:
844 self.ui.status(_('cloning subrepo %s from %s\n')
849 self.ui.status(_('cloning subrepo %s from %s\n')
845 % (subrelpath(self), srcurl))
850 % (subrelpath(self), srcurl))
846 parentrepo = self._repo._subparent
851 parentrepo = self._repo._subparent
847 # use self._repo.vfs instead of self.wvfs to remove .hg only
852 # use self._repo.vfs instead of self.wvfs to remove .hg only
848 self._repo.vfs.rmtree()
853 self._repo.vfs.rmtree()
849 other, cloned = hg.clone(self._repo._subparent.baseui, {},
854 other, cloned = hg.clone(self._repo._subparent.baseui, {},
850 other, self._repo.root,
855 other, self._repo.root,
851 update=False)
856 update=False)
852 self._repo = cloned.local()
857 self._repo = cloned.local()
853 self._initrepo(parentrepo, source, create=True)
858 self._initrepo(parentrepo, source, create=True)
854 self._cachestorehash(srcurl)
859 self._cachestorehash(srcurl)
855 else:
860 else:
856 self.ui.status(_('pulling subrepo %s from %s\n')
861 self.ui.status(_('pulling subrepo %s from %s\n')
857 % (subrelpath(self), srcurl))
862 % (subrelpath(self), srcurl))
858 cleansub = self.storeclean(srcurl)
863 cleansub = self.storeclean(srcurl)
859 exchange.pull(self._repo, other)
864 exchange.pull(self._repo, other)
860 if cleansub:
865 if cleansub:
861 # keep the repo clean after pull
866 # keep the repo clean after pull
862 self._cachestorehash(srcurl)
867 self._cachestorehash(srcurl)
863 return False
868 return False
864
869
865 @annotatesubrepoerror
870 @annotatesubrepoerror
866 def get(self, state, overwrite=False):
871 def get(self, state, overwrite=False):
867 inrepo = self._get(state)
872 inrepo = self._get(state)
868 source, revision, kind = state
873 source, revision, kind = state
869 repo = self._repo
874 repo = self._repo
870 repo.ui.debug("getting subrepo %s\n" % self._path)
875 repo.ui.debug("getting subrepo %s\n" % self._path)
871 if inrepo:
876 if inrepo:
872 urepo = repo.unfiltered()
877 urepo = repo.unfiltered()
873 ctx = urepo[revision]
878 ctx = urepo[revision]
874 if ctx.hidden():
879 if ctx.hidden():
875 urepo.ui.warn(
880 urepo.ui.warn(
876 _('revision %s in subrepo %s is hidden\n') \
881 _('revision %s in subrepo %s is hidden\n') \
877 % (revision[0:12], self._path))
882 % (revision[0:12], self._path))
878 repo = urepo
883 repo = urepo
879 hg.updaterepo(repo, revision, overwrite)
884 hg.updaterepo(repo, revision, overwrite)
880
885
881 @annotatesubrepoerror
886 @annotatesubrepoerror
882 def merge(self, state):
887 def merge(self, state):
883 self._get(state)
888 self._get(state)
884 cur = self._repo['.']
889 cur = self._repo['.']
885 dst = self._repo[state[1]]
890 dst = self._repo[state[1]]
886 anc = dst.ancestor(cur)
891 anc = dst.ancestor(cur)
887
892
888 def mergefunc():
893 def mergefunc():
889 if anc == cur and dst.branch() == cur.branch():
894 if anc == cur and dst.branch() == cur.branch():
890 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
895 self.ui.debug("updating subrepo %s\n" % subrelpath(self))
891 hg.update(self._repo, state[1])
896 hg.update(self._repo, state[1])
892 elif anc == dst:
897 elif anc == dst:
893 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
898 self.ui.debug("skipping subrepo %s\n" % subrelpath(self))
894 else:
899 else:
895 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
900 self.ui.debug("merging subrepo %s\n" % subrelpath(self))
896 hg.merge(self._repo, state[1], remind=False)
901 hg.merge(self._repo, state[1], remind=False)
897
902
898 wctx = self._repo[None]
903 wctx = self._repo[None]
899 if self.dirty():
904 if self.dirty():
900 if anc != dst:
905 if anc != dst:
901 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
906 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
902 mergefunc()
907 mergefunc()
903 else:
908 else:
904 mergefunc()
909 mergefunc()
905 else:
910 else:
906 mergefunc()
911 mergefunc()
907
912
908 @annotatesubrepoerror
913 @annotatesubrepoerror
909 def push(self, opts):
914 def push(self, opts):
910 force = opts.get('force')
915 force = opts.get('force')
911 newbranch = opts.get('new_branch')
916 newbranch = opts.get('new_branch')
912 ssh = opts.get('ssh')
917 ssh = opts.get('ssh')
913
918
914 # push subrepos depth-first for coherent ordering
919 # push subrepos depth-first for coherent ordering
915 c = self._repo['']
920 c = self._repo['']
916 subs = c.substate # only repos that are committed
921 subs = c.substate # only repos that are committed
917 for s in sorted(subs):
922 for s in sorted(subs):
918 if c.sub(s).push(opts) == 0:
923 if c.sub(s).push(opts) == 0:
919 return False
924 return False
920
925
921 dsturl = _abssource(self._repo, True)
926 dsturl = _abssource(self._repo, True)
922 if not force:
927 if not force:
923 if self.storeclean(dsturl):
928 if self.storeclean(dsturl):
924 self.ui.status(
929 self.ui.status(
925 _('no changes made to subrepo %s since last push to %s\n')
930 _('no changes made to subrepo %s since last push to %s\n')
926 % (subrelpath(self), dsturl))
931 % (subrelpath(self), dsturl))
927 return None
932 return None
928 self.ui.status(_('pushing subrepo %s to %s\n') %
933 self.ui.status(_('pushing subrepo %s to %s\n') %
929 (subrelpath(self), dsturl))
934 (subrelpath(self), dsturl))
930 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
935 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
931 res = exchange.push(self._repo, other, force, newbranch=newbranch)
936 res = exchange.push(self._repo, other, force, newbranch=newbranch)
932
937
933 # the repo is now clean
938 # the repo is now clean
934 self._cachestorehash(dsturl)
939 self._cachestorehash(dsturl)
935 return res.cgresult
940 return res.cgresult
936
941
937 @annotatesubrepoerror
942 @annotatesubrepoerror
938 def outgoing(self, ui, dest, opts):
943 def outgoing(self, ui, dest, opts):
939 if 'rev' in opts or 'branch' in opts:
944 if 'rev' in opts or 'branch' in opts:
940 opts = copy.copy(opts)
945 opts = copy.copy(opts)
941 opts.pop('rev', None)
946 opts.pop('rev', None)
942 opts.pop('branch', None)
947 opts.pop('branch', None)
943 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
948 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
944
949
945 @annotatesubrepoerror
950 @annotatesubrepoerror
946 def incoming(self, ui, source, opts):
951 def incoming(self, ui, source, opts):
947 if 'rev' in opts or 'branch' in opts:
952 if 'rev' in opts or 'branch' in opts:
948 opts = copy.copy(opts)
953 opts = copy.copy(opts)
949 opts.pop('rev', None)
954 opts.pop('rev', None)
950 opts.pop('branch', None)
955 opts.pop('branch', None)
951 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
956 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
952
957
953 @annotatesubrepoerror
958 @annotatesubrepoerror
954 def files(self):
959 def files(self):
955 rev = self._state[1]
960 rev = self._state[1]
956 ctx = self._repo[rev]
961 ctx = self._repo[rev]
957 return ctx.manifest().keys()
962 return ctx.manifest().keys()
958
963
959 def filedata(self, name):
964 def filedata(self, name):
960 rev = self._state[1]
965 rev = self._state[1]
961 return self._repo[rev][name].data()
966 return self._repo[rev][name].data()
962
967
963 def fileflags(self, name):
968 def fileflags(self, name):
964 rev = self._state[1]
969 rev = self._state[1]
965 ctx = self._repo[rev]
970 ctx = self._repo[rev]
966 return ctx.flags(name)
971 return ctx.flags(name)
967
972
968 @annotatesubrepoerror
973 @annotatesubrepoerror
969 def printfiles(self, ui, m, fm, fmt, subrepos):
974 def printfiles(self, ui, m, fm, fmt, subrepos):
970 # If the parent context is a workingctx, use the workingctx here for
975 # If the parent context is a workingctx, use the workingctx here for
971 # consistency.
976 # consistency.
972 if self._ctx.rev() is None:
977 if self._ctx.rev() is None:
973 ctx = self._repo[None]
978 ctx = self._repo[None]
974 else:
979 else:
975 rev = self._state[1]
980 rev = self._state[1]
976 ctx = self._repo[rev]
981 ctx = self._repo[rev]
977 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
982 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
978
983
979 @annotatesubrepoerror
984 @annotatesubrepoerror
980 def getfileset(self, expr):
985 def getfileset(self, expr):
981 if self._ctx.rev() is None:
986 if self._ctx.rev() is None:
982 ctx = self._repo[None]
987 ctx = self._repo[None]
983 else:
988 else:
984 rev = self._state[1]
989 rev = self._state[1]
985 ctx = self._repo[rev]
990 ctx = self._repo[rev]
986
991
987 files = ctx.getfileset(expr)
992 files = ctx.getfileset(expr)
988
993
989 for subpath in ctx.substate:
994 for subpath in ctx.substate:
990 sub = ctx.sub(subpath)
995 sub = ctx.sub(subpath)
991
996
992 try:
997 try:
993 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
998 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
994 except error.LookupError:
999 except error.LookupError:
995 self.ui.status(_("skipping missing subrepository: %s\n")
1000 self.ui.status(_("skipping missing subrepository: %s\n")
996 % self.wvfs.reljoin(reporelpath(self), subpath))
1001 % self.wvfs.reljoin(reporelpath(self), subpath))
997 return files
1002 return files
998
1003
999 def walk(self, match):
1004 def walk(self, match):
1000 ctx = self._repo[None]
1005 ctx = self._repo[None]
1001 return ctx.walk(match)
1006 return ctx.walk(match)
1002
1007
1003 @annotatesubrepoerror
1008 @annotatesubrepoerror
1004 def forget(self, match, prefix):
1009 def forget(self, match, prefix):
1005 return cmdutil.forget(self.ui, self._repo, match,
1010 return cmdutil.forget(self.ui, self._repo, match,
1006 self.wvfs.reljoin(prefix, self._path), True)
1011 self.wvfs.reljoin(prefix, self._path), True)
1007
1012
1008 @annotatesubrepoerror
1013 @annotatesubrepoerror
1009 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
1014 def removefiles(self, matcher, prefix, after, force, subrepos, warnings):
1010 return cmdutil.remove(self.ui, self._repo, matcher,
1015 return cmdutil.remove(self.ui, self._repo, matcher,
1011 self.wvfs.reljoin(prefix, self._path),
1016 self.wvfs.reljoin(prefix, self._path),
1012 after, force, subrepos)
1017 after, force, subrepos)
1013
1018
1014 @annotatesubrepoerror
1019 @annotatesubrepoerror
1015 def revert(self, substate, *pats, **opts):
1020 def revert(self, substate, *pats, **opts):
1016 # reverting a subrepo is a 2 step process:
1021 # reverting a subrepo is a 2 step process:
1017 # 1. if the no_backup is not set, revert all modified
1022 # 1. if the no_backup is not set, revert all modified
1018 # files inside the subrepo
1023 # files inside the subrepo
1019 # 2. update the subrepo to the revision specified in
1024 # 2. update the subrepo to the revision specified in
1020 # the corresponding substate dictionary
1025 # the corresponding substate dictionary
1021 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1026 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1022 if not opts.get('no_backup'):
1027 if not opts.get('no_backup'):
1023 # Revert all files on the subrepo, creating backups
1028 # Revert all files on the subrepo, creating backups
1024 # Note that this will not recursively revert subrepos
1029 # Note that this will not recursively revert subrepos
1025 # We could do it if there was a set:subrepos() predicate
1030 # We could do it if there was a set:subrepos() predicate
1026 opts = opts.copy()
1031 opts = opts.copy()
1027 opts['date'] = None
1032 opts['date'] = None
1028 opts['rev'] = substate[1]
1033 opts['rev'] = substate[1]
1029
1034
1030 self.filerevert(*pats, **opts)
1035 self.filerevert(*pats, **opts)
1031
1036
1032 # Update the repo to the revision specified in the given substate
1037 # Update the repo to the revision specified in the given substate
1033 if not opts.get('dry_run'):
1038 if not opts.get('dry_run'):
1034 self.get(substate, overwrite=True)
1039 self.get(substate, overwrite=True)
1035
1040
1036 def filerevert(self, *pats, **opts):
1041 def filerevert(self, *pats, **opts):
1037 ctx = self._repo[opts['rev']]
1042 ctx = self._repo[opts['rev']]
1038 parents = self._repo.dirstate.parents()
1043 parents = self._repo.dirstate.parents()
1039 if opts.get('all'):
1044 if opts.get('all'):
1040 pats = ['set:modified()']
1045 pats = ['set:modified()']
1041 else:
1046 else:
1042 pats = []
1047 pats = []
1043 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
1048 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
1044
1049
1045 def shortid(self, revid):
1050 def shortid(self, revid):
1046 return revid[:12]
1051 return revid[:12]
1047
1052
1048 def verify(self):
1053 def verify(self):
1049 try:
1054 try:
1050 rev = self._state[1]
1055 rev = self._state[1]
1051 ctx = self._repo.unfiltered()[rev]
1056 ctx = self._repo.unfiltered()[rev]
1052 if ctx.hidden():
1057 if ctx.hidden():
1053 # Since hidden revisions aren't pushed/pulled, it seems worth an
1058 # Since hidden revisions aren't pushed/pulled, it seems worth an
1054 # explicit warning.
1059 # explicit warning.
1055 ui = self._repo.ui
1060 ui = self._repo.ui
1056 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
1061 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
1057 (self._relpath, node.short(self._ctx.node())))
1062 (self._relpath, node.short(self._ctx.node())))
1058 return 0
1063 return 0
1059 except error.RepoLookupError:
1064 except error.RepoLookupError:
1060 # A missing subrepo revision may be a case of needing to pull it, so
1065 # A missing subrepo revision may be a case of needing to pull it, so
1061 # don't treat this as an error.
1066 # don't treat this as an error.
1062 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
1067 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
1063 (self._relpath, node.short(self._ctx.node())))
1068 (self._relpath, node.short(self._ctx.node())))
1064 return 0
1069 return 0
1065
1070
1066 @propertycache
1071 @propertycache
1067 def wvfs(self):
1072 def wvfs(self):
1068 """return own wvfs for efficiency and consistency
1073 """return own wvfs for efficiency and consistency
1069 """
1074 """
1070 return self._repo.wvfs
1075 return self._repo.wvfs
1071
1076
1072 @propertycache
1077 @propertycache
1073 def _relpath(self):
1078 def _relpath(self):
1074 """return path to this subrepository as seen from outermost repository
1079 """return path to this subrepository as seen from outermost repository
1075 """
1080 """
1076 # Keep consistent dir separators by avoiding vfs.join(self._path)
1081 # Keep consistent dir separators by avoiding vfs.join(self._path)
1077 return reporelpath(self._repo)
1082 return reporelpath(self._repo)
1078
1083
1079 class svnsubrepo(abstractsubrepo):
1084 class svnsubrepo(abstractsubrepo):
1080 def __init__(self, ctx, path, state, allowcreate):
1085 def __init__(self, ctx, path, state, allowcreate):
1081 super(svnsubrepo, self).__init__(ctx, path)
1086 super(svnsubrepo, self).__init__(ctx, path)
1082 self._state = state
1087 self._state = state
1083 self._exe = util.findexe('svn')
1088 self._exe = util.findexe('svn')
1084 if not self._exe:
1089 if not self._exe:
1085 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1090 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
1086 % self._path)
1091 % self._path)
1087
1092
1088 def _svncommand(self, commands, filename='', failok=False):
1093 def _svncommand(self, commands, filename='', failok=False):
1089 cmd = [self._exe]
1094 cmd = [self._exe]
1090 extrakw = {}
1095 extrakw = {}
1091 if not self.ui.interactive():
1096 if not self.ui.interactive():
1092 # Making stdin be a pipe should prevent svn from behaving
1097 # Making stdin be a pipe should prevent svn from behaving
1093 # interactively even if we can't pass --non-interactive.
1098 # interactively even if we can't pass --non-interactive.
1094 extrakw['stdin'] = subprocess.PIPE
1099 extrakw['stdin'] = subprocess.PIPE
1095 # Starting in svn 1.5 --non-interactive is a global flag
1100 # Starting in svn 1.5 --non-interactive is a global flag
1096 # instead of being per-command, but we need to support 1.4 so
1101 # instead of being per-command, but we need to support 1.4 so
1097 # we have to be intelligent about what commands take
1102 # we have to be intelligent about what commands take
1098 # --non-interactive.
1103 # --non-interactive.
1099 if commands[0] in ('update', 'checkout', 'commit'):
1104 if commands[0] in ('update', 'checkout', 'commit'):
1100 cmd.append('--non-interactive')
1105 cmd.append('--non-interactive')
1101 cmd.extend(commands)
1106 cmd.extend(commands)
1102 if filename is not None:
1107 if filename is not None:
1103 path = self.wvfs.reljoin(self._ctx.repo().origroot,
1108 path = self.wvfs.reljoin(self._ctx.repo().origroot,
1104 self._path, filename)
1109 self._path, filename)
1105 cmd.append(path)
1110 cmd.append(path)
1106 env = dict(encoding.environ)
1111 env = dict(encoding.environ)
1107 # Avoid localized output, preserve current locale for everything else.
1112 # Avoid localized output, preserve current locale for everything else.
1108 lc_all = env.get('LC_ALL')
1113 lc_all = env.get('LC_ALL')
1109 if lc_all:
1114 if lc_all:
1110 env['LANG'] = lc_all
1115 env['LANG'] = lc_all
1111 del env['LC_ALL']
1116 del env['LC_ALL']
1112 env['LC_MESSAGES'] = 'C'
1117 env['LC_MESSAGES'] = 'C'
1113 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
1118 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
1114 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1119 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1115 universal_newlines=True, env=env, **extrakw)
1120 universal_newlines=True, env=env, **extrakw)
1116 stdout, stderr = p.communicate()
1121 stdout, stderr = p.communicate()
1117 stderr = stderr.strip()
1122 stderr = stderr.strip()
1118 if not failok:
1123 if not failok:
1119 if p.returncode:
1124 if p.returncode:
1120 raise error.Abort(stderr or 'exited with code %d'
1125 raise error.Abort(stderr or 'exited with code %d'
1121 % p.returncode)
1126 % p.returncode)
1122 if stderr:
1127 if stderr:
1123 self.ui.warn(stderr + '\n')
1128 self.ui.warn(stderr + '\n')
1124 return stdout, stderr
1129 return stdout, stderr
1125
1130
1126 @propertycache
1131 @propertycache
1127 def _svnversion(self):
1132 def _svnversion(self):
1128 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1133 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1129 m = re.search(r'^(\d+)\.(\d+)', output)
1134 m = re.search(r'^(\d+)\.(\d+)', output)
1130 if not m:
1135 if not m:
1131 raise error.Abort(_('cannot retrieve svn tool version'))
1136 raise error.Abort(_('cannot retrieve svn tool version'))
1132 return (int(m.group(1)), int(m.group(2)))
1137 return (int(m.group(1)), int(m.group(2)))
1133
1138
1134 def _wcrevs(self):
1139 def _wcrevs(self):
1135 # Get the working directory revision as well as the last
1140 # Get the working directory revision as well as the last
1136 # commit revision so we can compare the subrepo state with
1141 # commit revision so we can compare the subrepo state with
1137 # both. We used to store the working directory one.
1142 # both. We used to store the working directory one.
1138 output, err = self._svncommand(['info', '--xml'])
1143 output, err = self._svncommand(['info', '--xml'])
1139 doc = xml.dom.minidom.parseString(output)
1144 doc = xml.dom.minidom.parseString(output)
1140 entries = doc.getElementsByTagName('entry')
1145 entries = doc.getElementsByTagName('entry')
1141 lastrev, rev = '0', '0'
1146 lastrev, rev = '0', '0'
1142 if entries:
1147 if entries:
1143 rev = str(entries[0].getAttribute('revision')) or '0'
1148 rev = str(entries[0].getAttribute('revision')) or '0'
1144 commits = entries[0].getElementsByTagName('commit')
1149 commits = entries[0].getElementsByTagName('commit')
1145 if commits:
1150 if commits:
1146 lastrev = str(commits[0].getAttribute('revision')) or '0'
1151 lastrev = str(commits[0].getAttribute('revision')) or '0'
1147 return (lastrev, rev)
1152 return (lastrev, rev)
1148
1153
1149 def _wcrev(self):
1154 def _wcrev(self):
1150 return self._wcrevs()[0]
1155 return self._wcrevs()[0]
1151
1156
1152 def _wcchanged(self):
1157 def _wcchanged(self):
1153 """Return (changes, extchanges, missing) where changes is True
1158 """Return (changes, extchanges, missing) where changes is True
1154 if the working directory was changed, extchanges is
1159 if the working directory was changed, extchanges is
1155 True if any of these changes concern an external entry and missing
1160 True if any of these changes concern an external entry and missing
1156 is True if any change is a missing entry.
1161 is True if any change is a missing entry.
1157 """
1162 """
1158 output, err = self._svncommand(['status', '--xml'])
1163 output, err = self._svncommand(['status', '--xml'])
1159 externals, changes, missing = [], [], []
1164 externals, changes, missing = [], [], []
1160 doc = xml.dom.minidom.parseString(output)
1165 doc = xml.dom.minidom.parseString(output)
1161 for e in doc.getElementsByTagName('entry'):
1166 for e in doc.getElementsByTagName('entry'):
1162 s = e.getElementsByTagName('wc-status')
1167 s = e.getElementsByTagName('wc-status')
1163 if not s:
1168 if not s:
1164 continue
1169 continue
1165 item = s[0].getAttribute('item')
1170 item = s[0].getAttribute('item')
1166 props = s[0].getAttribute('props')
1171 props = s[0].getAttribute('props')
1167 path = e.getAttribute('path')
1172 path = e.getAttribute('path')
1168 if item == 'external':
1173 if item == 'external':
1169 externals.append(path)
1174 externals.append(path)
1170 elif item == 'missing':
1175 elif item == 'missing':
1171 missing.append(path)
1176 missing.append(path)
1172 if (item not in ('', 'normal', 'unversioned', 'external')
1177 if (item not in ('', 'normal', 'unversioned', 'external')
1173 or props not in ('', 'none', 'normal')):
1178 or props not in ('', 'none', 'normal')):
1174 changes.append(path)
1179 changes.append(path)
1175 for path in changes:
1180 for path in changes:
1176 for ext in externals:
1181 for ext in externals:
1177 if path == ext or path.startswith(ext + pycompat.ossep):
1182 if path == ext or path.startswith(ext + pycompat.ossep):
1178 return True, True, bool(missing)
1183 return True, True, bool(missing)
1179 return bool(changes), False, bool(missing)
1184 return bool(changes), False, bool(missing)
1180
1185
1181 def dirty(self, ignoreupdate=False):
1186 def dirty(self, ignoreupdate=False):
1182 if not self._wcchanged()[0]:
1187 if not self._wcchanged()[0]:
1183 if self._state[1] in self._wcrevs() or ignoreupdate:
1188 if self._state[1] in self._wcrevs() or ignoreupdate:
1184 return False
1189 return False
1185 return True
1190 return True
1186
1191
1187 def basestate(self):
1192 def basestate(self):
1188 lastrev, rev = self._wcrevs()
1193 lastrev, rev = self._wcrevs()
1189 if lastrev != rev:
1194 if lastrev != rev:
1190 # Last committed rev is not the same than rev. We would
1195 # Last committed rev is not the same than rev. We would
1191 # like to take lastrev but we do not know if the subrepo
1196 # like to take lastrev but we do not know if the subrepo
1192 # URL exists at lastrev. Test it and fallback to rev it
1197 # URL exists at lastrev. Test it and fallback to rev it
1193 # is not there.
1198 # is not there.
1194 try:
1199 try:
1195 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1200 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1196 return lastrev
1201 return lastrev
1197 except error.Abort:
1202 except error.Abort:
1198 pass
1203 pass
1199 return rev
1204 return rev
1200
1205
1201 @annotatesubrepoerror
1206 @annotatesubrepoerror
1202 def commit(self, text, user, date):
1207 def commit(self, text, user, date):
1203 # user and date are out of our hands since svn is centralized
1208 # user and date are out of our hands since svn is centralized
1204 changed, extchanged, missing = self._wcchanged()
1209 changed, extchanged, missing = self._wcchanged()
1205 if not changed:
1210 if not changed:
1206 return self.basestate()
1211 return self.basestate()
1207 if extchanged:
1212 if extchanged:
1208 # Do not try to commit externals
1213 # Do not try to commit externals
1209 raise error.Abort(_('cannot commit svn externals'))
1214 raise error.Abort(_('cannot commit svn externals'))
1210 if missing:
1215 if missing:
1211 # svn can commit with missing entries but aborting like hg
1216 # svn can commit with missing entries but aborting like hg
1212 # seems a better approach.
1217 # seems a better approach.
1213 raise error.Abort(_('cannot commit missing svn entries'))
1218 raise error.Abort(_('cannot commit missing svn entries'))
1214 commitinfo, err = self._svncommand(['commit', '-m', text])
1219 commitinfo, err = self._svncommand(['commit', '-m', text])
1215 self.ui.status(commitinfo)
1220 self.ui.status(commitinfo)
1216 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1221 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1217 if not newrev:
1222 if not newrev:
1218 if not commitinfo.strip():
1223 if not commitinfo.strip():
1219 # Sometimes, our definition of "changed" differs from
1224 # Sometimes, our definition of "changed" differs from
1220 # svn one. For instance, svn ignores missing files
1225 # svn one. For instance, svn ignores missing files
1221 # when committing. If there are only missing files, no
1226 # when committing. If there are only missing files, no
1222 # commit is made, no output and no error code.
1227 # commit is made, no output and no error code.
1223 raise error.Abort(_('failed to commit svn changes'))
1228 raise error.Abort(_('failed to commit svn changes'))
1224 raise error.Abort(commitinfo.splitlines()[-1])
1229 raise error.Abort(commitinfo.splitlines()[-1])
1225 newrev = newrev.groups()[0]
1230 newrev = newrev.groups()[0]
1226 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1231 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1227 return newrev
1232 return newrev
1228
1233
1229 @annotatesubrepoerror
1234 @annotatesubrepoerror
1230 def remove(self):
1235 def remove(self):
1231 if self.dirty():
1236 if self.dirty():
1232 self.ui.warn(_('not removing repo %s because '
1237 self.ui.warn(_('not removing repo %s because '
1233 'it has changes.\n') % self._path)
1238 'it has changes.\n') % self._path)
1234 return
1239 return
1235 self.ui.note(_('removing subrepo %s\n') % self._path)
1240 self.ui.note(_('removing subrepo %s\n') % self._path)
1236
1241
1237 self.wvfs.rmtree(forcibly=True)
1242 self.wvfs.rmtree(forcibly=True)
1238 try:
1243 try:
1239 pwvfs = self._ctx.repo().wvfs
1244 pwvfs = self._ctx.repo().wvfs
1240 pwvfs.removedirs(pwvfs.dirname(self._path))
1245 pwvfs.removedirs(pwvfs.dirname(self._path))
1241 except OSError:
1246 except OSError:
1242 pass
1247 pass
1243
1248
1244 @annotatesubrepoerror
1249 @annotatesubrepoerror
1245 def get(self, state, overwrite=False):
1250 def get(self, state, overwrite=False):
1246 if overwrite:
1251 if overwrite:
1247 self._svncommand(['revert', '--recursive'])
1252 self._svncommand(['revert', '--recursive'])
1248 args = ['checkout']
1253 args = ['checkout']
1249 if self._svnversion >= (1, 5):
1254 if self._svnversion >= (1, 5):
1250 args.append('--force')
1255 args.append('--force')
1251 # The revision must be specified at the end of the URL to properly
1256 # The revision must be specified at the end of the URL to properly
1252 # update to a directory which has since been deleted and recreated.
1257 # update to a directory which has since been deleted and recreated.
1253 args.append('%s@%s' % (state[0], state[1]))
1258 args.append('%s@%s' % (state[0], state[1]))
1254 status, err = self._svncommand(args, failok=True)
1259 status, err = self._svncommand(args, failok=True)
1255 _sanitize(self.ui, self.wvfs, '.svn')
1260 _sanitize(self.ui, self.wvfs, '.svn')
1256 if not re.search('Checked out revision [0-9]+.', status):
1261 if not re.search('Checked out revision [0-9]+.', status):
1257 if ('is already a working copy for a different URL' in err
1262 if ('is already a working copy for a different URL' in err
1258 and (self._wcchanged()[:2] == (False, False))):
1263 and (self._wcchanged()[:2] == (False, False))):
1259 # obstructed but clean working copy, so just blow it away.
1264 # obstructed but clean working copy, so just blow it away.
1260 self.remove()
1265 self.remove()
1261 self.get(state, overwrite=False)
1266 self.get(state, overwrite=False)
1262 return
1267 return
1263 raise error.Abort((status or err).splitlines()[-1])
1268 raise error.Abort((status or err).splitlines()[-1])
1264 self.ui.status(status)
1269 self.ui.status(status)
1265
1270
1266 @annotatesubrepoerror
1271 @annotatesubrepoerror
1267 def merge(self, state):
1272 def merge(self, state):
1268 old = self._state[1]
1273 old = self._state[1]
1269 new = state[1]
1274 new = state[1]
1270 wcrev = self._wcrev()
1275 wcrev = self._wcrev()
1271 if new != wcrev:
1276 if new != wcrev:
1272 dirty = old == wcrev or self._wcchanged()[0]
1277 dirty = old == wcrev or self._wcchanged()[0]
1273 if _updateprompt(self.ui, self, dirty, wcrev, new):
1278 if _updateprompt(self.ui, self, dirty, wcrev, new):
1274 self.get(state, False)
1279 self.get(state, False)
1275
1280
1276 def push(self, opts):
1281 def push(self, opts):
1277 # push is a no-op for SVN
1282 # push is a no-op for SVN
1278 return True
1283 return True
1279
1284
1280 @annotatesubrepoerror
1285 @annotatesubrepoerror
1281 def files(self):
1286 def files(self):
1282 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1287 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1283 doc = xml.dom.minidom.parseString(output)
1288 doc = xml.dom.minidom.parseString(output)
1284 paths = []
1289 paths = []
1285 for e in doc.getElementsByTagName('entry'):
1290 for e in doc.getElementsByTagName('entry'):
1286 kind = str(e.getAttribute('kind'))
1291 kind = str(e.getAttribute('kind'))
1287 if kind != 'file':
1292 if kind != 'file':
1288 continue
1293 continue
1289 name = ''.join(c.data for c
1294 name = ''.join(c.data for c
1290 in e.getElementsByTagName('name')[0].childNodes
1295 in e.getElementsByTagName('name')[0].childNodes
1291 if c.nodeType == c.TEXT_NODE)
1296 if c.nodeType == c.TEXT_NODE)
1292 paths.append(name.encode('utf-8'))
1297 paths.append(name.encode('utf-8'))
1293 return paths
1298 return paths
1294
1299
1295 def filedata(self, name):
1300 def filedata(self, name):
1296 return self._svncommand(['cat'], name)[0]
1301 return self._svncommand(['cat'], name)[0]
1297
1302
1298
1303
1299 class gitsubrepo(abstractsubrepo):
1304 class gitsubrepo(abstractsubrepo):
1300 def __init__(self, ctx, path, state, allowcreate):
1305 def __init__(self, ctx, path, state, allowcreate):
1301 super(gitsubrepo, self).__init__(ctx, path)
1306 super(gitsubrepo, self).__init__(ctx, path)
1302 self._state = state
1307 self._state = state
1303 self._abspath = ctx.repo().wjoin(path)
1308 self._abspath = ctx.repo().wjoin(path)
1304 self._subparent = ctx.repo()
1309 self._subparent = ctx.repo()
1305 self._ensuregit()
1310 self._ensuregit()
1306
1311
1307 def _ensuregit(self):
1312 def _ensuregit(self):
1308 try:
1313 try:
1309 self._gitexecutable = 'git'
1314 self._gitexecutable = 'git'
1310 out, err = self._gitnodir(['--version'])
1315 out, err = self._gitnodir(['--version'])
1311 except OSError as e:
1316 except OSError as e:
1312 genericerror = _("error executing git for subrepo '%s': %s")
1317 genericerror = _("error executing git for subrepo '%s': %s")
1313 notfoundhint = _("check git is installed and in your PATH")
1318 notfoundhint = _("check git is installed and in your PATH")
1314 if e.errno != errno.ENOENT:
1319 if e.errno != errno.ENOENT:
1315 raise error.Abort(genericerror % (self._path, e.strerror))
1320 raise error.Abort(genericerror % (self._path, e.strerror))
1316 elif pycompat.osname == 'nt':
1321 elif pycompat.osname == 'nt':
1317 try:
1322 try:
1318 self._gitexecutable = 'git.cmd'
1323 self._gitexecutable = 'git.cmd'
1319 out, err = self._gitnodir(['--version'])
1324 out, err = self._gitnodir(['--version'])
1320 except OSError as e2:
1325 except OSError as e2:
1321 if e2.errno == errno.ENOENT:
1326 if e2.errno == errno.ENOENT:
1322 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1327 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1323 " for subrepo '%s'") % self._path,
1328 " for subrepo '%s'") % self._path,
1324 hint=notfoundhint)
1329 hint=notfoundhint)
1325 else:
1330 else:
1326 raise error.Abort(genericerror % (self._path,
1331 raise error.Abort(genericerror % (self._path,
1327 e2.strerror))
1332 e2.strerror))
1328 else:
1333 else:
1329 raise error.Abort(_("couldn't find git for subrepo '%s'")
1334 raise error.Abort(_("couldn't find git for subrepo '%s'")
1330 % self._path, hint=notfoundhint)
1335 % self._path, hint=notfoundhint)
1331 versionstatus = self._checkversion(out)
1336 versionstatus = self._checkversion(out)
1332 if versionstatus == 'unknown':
1337 if versionstatus == 'unknown':
1333 self.ui.warn(_('cannot retrieve git version\n'))
1338 self.ui.warn(_('cannot retrieve git version\n'))
1334 elif versionstatus == 'abort':
1339 elif versionstatus == 'abort':
1335 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1340 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1336 elif versionstatus == 'warning':
1341 elif versionstatus == 'warning':
1337 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1342 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1338
1343
1339 @staticmethod
1344 @staticmethod
1340 def _gitversion(out):
1345 def _gitversion(out):
1341 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1346 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1342 if m:
1347 if m:
1343 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1348 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1344
1349
1345 m = re.search(r'^git version (\d+)\.(\d+)', out)
1350 m = re.search(r'^git version (\d+)\.(\d+)', out)
1346 if m:
1351 if m:
1347 return (int(m.group(1)), int(m.group(2)), 0)
1352 return (int(m.group(1)), int(m.group(2)), 0)
1348
1353
1349 return -1
1354 return -1
1350
1355
1351 @staticmethod
1356 @staticmethod
1352 def _checkversion(out):
1357 def _checkversion(out):
1353 '''ensure git version is new enough
1358 '''ensure git version is new enough
1354
1359
1355 >>> _checkversion = gitsubrepo._checkversion
1360 >>> _checkversion = gitsubrepo._checkversion
1356 >>> _checkversion('git version 1.6.0')
1361 >>> _checkversion('git version 1.6.0')
1357 'ok'
1362 'ok'
1358 >>> _checkversion('git version 1.8.5')
1363 >>> _checkversion('git version 1.8.5')
1359 'ok'
1364 'ok'
1360 >>> _checkversion('git version 1.4.0')
1365 >>> _checkversion('git version 1.4.0')
1361 'abort'
1366 'abort'
1362 >>> _checkversion('git version 1.5.0')
1367 >>> _checkversion('git version 1.5.0')
1363 'warning'
1368 'warning'
1364 >>> _checkversion('git version 1.9-rc0')
1369 >>> _checkversion('git version 1.9-rc0')
1365 'ok'
1370 'ok'
1366 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1371 >>> _checkversion('git version 1.9.0.265.g81cdec2')
1367 'ok'
1372 'ok'
1368 >>> _checkversion('git version 1.9.0.GIT')
1373 >>> _checkversion('git version 1.9.0.GIT')
1369 'ok'
1374 'ok'
1370 >>> _checkversion('git version 12345')
1375 >>> _checkversion('git version 12345')
1371 'unknown'
1376 'unknown'
1372 >>> _checkversion('no')
1377 >>> _checkversion('no')
1373 'unknown'
1378 'unknown'
1374 '''
1379 '''
1375 version = gitsubrepo._gitversion(out)
1380 version = gitsubrepo._gitversion(out)
1376 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1381 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1377 # despite the docstring comment. For now, error on 1.4.0, warn on
1382 # despite the docstring comment. For now, error on 1.4.0, warn on
1378 # 1.5.0 but attempt to continue.
1383 # 1.5.0 but attempt to continue.
1379 if version == -1:
1384 if version == -1:
1380 return 'unknown'
1385 return 'unknown'
1381 if version < (1, 5, 0):
1386 if version < (1, 5, 0):
1382 return 'abort'
1387 return 'abort'
1383 elif version < (1, 6, 0):
1388 elif version < (1, 6, 0):
1384 return 'warning'
1389 return 'warning'
1385 return 'ok'
1390 return 'ok'
1386
1391
1387 def _gitcommand(self, commands, env=None, stream=False):
1392 def _gitcommand(self, commands, env=None, stream=False):
1388 return self._gitdir(commands, env=env, stream=stream)[0]
1393 return self._gitdir(commands, env=env, stream=stream)[0]
1389
1394
1390 def _gitdir(self, commands, env=None, stream=False):
1395 def _gitdir(self, commands, env=None, stream=False):
1391 return self._gitnodir(commands, env=env, stream=stream,
1396 return self._gitnodir(commands, env=env, stream=stream,
1392 cwd=self._abspath)
1397 cwd=self._abspath)
1393
1398
1394 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1399 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1395 """Calls the git command
1400 """Calls the git command
1396
1401
1397 The methods tries to call the git command. versions prior to 1.6.0
1402 The methods tries to call the git command. versions prior to 1.6.0
1398 are not supported and very probably fail.
1403 are not supported and very probably fail.
1399 """
1404 """
1400 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1405 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1401 if env is None:
1406 if env is None:
1402 env = encoding.environ.copy()
1407 env = encoding.environ.copy()
1403 # disable localization for Git output (issue5176)
1408 # disable localization for Git output (issue5176)
1404 env['LC_ALL'] = 'C'
1409 env['LC_ALL'] = 'C'
1405 # fix for Git CVE-2015-7545
1410 # fix for Git CVE-2015-7545
1406 if 'GIT_ALLOW_PROTOCOL' not in env:
1411 if 'GIT_ALLOW_PROTOCOL' not in env:
1407 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1412 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1408 # unless ui.quiet is set, print git's stderr,
1413 # unless ui.quiet is set, print git's stderr,
1409 # which is mostly progress and useful info
1414 # which is mostly progress and useful info
1410 errpipe = None
1415 errpipe = None
1411 if self.ui.quiet:
1416 if self.ui.quiet:
1412 errpipe = open(os.devnull, 'w')
1417 errpipe = open(os.devnull, 'w')
1413 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1418 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1414 cwd=cwd, env=env, close_fds=util.closefds,
1419 cwd=cwd, env=env, close_fds=util.closefds,
1415 stdout=subprocess.PIPE, stderr=errpipe)
1420 stdout=subprocess.PIPE, stderr=errpipe)
1416 if stream:
1421 if stream:
1417 return p.stdout, None
1422 return p.stdout, None
1418
1423
1419 retdata = p.stdout.read().strip()
1424 retdata = p.stdout.read().strip()
1420 # wait for the child to exit to avoid race condition.
1425 # wait for the child to exit to avoid race condition.
1421 p.wait()
1426 p.wait()
1422
1427
1423 if p.returncode != 0 and p.returncode != 1:
1428 if p.returncode != 0 and p.returncode != 1:
1424 # there are certain error codes that are ok
1429 # there are certain error codes that are ok
1425 command = commands[0]
1430 command = commands[0]
1426 if command in ('cat-file', 'symbolic-ref'):
1431 if command in ('cat-file', 'symbolic-ref'):
1427 return retdata, p.returncode
1432 return retdata, p.returncode
1428 # for all others, abort
1433 # for all others, abort
1429 raise error.Abort(_('git %s error %d in %s') %
1434 raise error.Abort(_('git %s error %d in %s') %
1430 (command, p.returncode, self._relpath))
1435 (command, p.returncode, self._relpath))
1431
1436
1432 return retdata, p.returncode
1437 return retdata, p.returncode
1433
1438
1434 def _gitmissing(self):
1439 def _gitmissing(self):
1435 return not self.wvfs.exists('.git')
1440 return not self.wvfs.exists('.git')
1436
1441
1437 def _gitstate(self):
1442 def _gitstate(self):
1438 return self._gitcommand(['rev-parse', 'HEAD'])
1443 return self._gitcommand(['rev-parse', 'HEAD'])
1439
1444
1440 def _gitcurrentbranch(self):
1445 def _gitcurrentbranch(self):
1441 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1446 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1442 if err:
1447 if err:
1443 current = None
1448 current = None
1444 return current
1449 return current
1445
1450
1446 def _gitremote(self, remote):
1451 def _gitremote(self, remote):
1447 out = self._gitcommand(['remote', 'show', '-n', remote])
1452 out = self._gitcommand(['remote', 'show', '-n', remote])
1448 line = out.split('\n')[1]
1453 line = out.split('\n')[1]
1449 i = line.index('URL: ') + len('URL: ')
1454 i = line.index('URL: ') + len('URL: ')
1450 return line[i:]
1455 return line[i:]
1451
1456
1452 def _githavelocally(self, revision):
1457 def _githavelocally(self, revision):
1453 out, code = self._gitdir(['cat-file', '-e', revision])
1458 out, code = self._gitdir(['cat-file', '-e', revision])
1454 return code == 0
1459 return code == 0
1455
1460
1456 def _gitisancestor(self, r1, r2):
1461 def _gitisancestor(self, r1, r2):
1457 base = self._gitcommand(['merge-base', r1, r2])
1462 base = self._gitcommand(['merge-base', r1, r2])
1458 return base == r1
1463 return base == r1
1459
1464
1460 def _gitisbare(self):
1465 def _gitisbare(self):
1461 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1466 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1462
1467
1463 def _gitupdatestat(self):
1468 def _gitupdatestat(self):
1464 """This must be run before git diff-index.
1469 """This must be run before git diff-index.
1465 diff-index only looks at changes to file stat;
1470 diff-index only looks at changes to file stat;
1466 this command looks at file contents and updates the stat."""
1471 this command looks at file contents and updates the stat."""
1467 self._gitcommand(['update-index', '-q', '--refresh'])
1472 self._gitcommand(['update-index', '-q', '--refresh'])
1468
1473
1469 def _gitbranchmap(self):
1474 def _gitbranchmap(self):
1470 '''returns 2 things:
1475 '''returns 2 things:
1471 a map from git branch to revision
1476 a map from git branch to revision
1472 a map from revision to branches'''
1477 a map from revision to branches'''
1473 branch2rev = {}
1478 branch2rev = {}
1474 rev2branch = {}
1479 rev2branch = {}
1475
1480
1476 out = self._gitcommand(['for-each-ref', '--format',
1481 out = self._gitcommand(['for-each-ref', '--format',
1477 '%(objectname) %(refname)'])
1482 '%(objectname) %(refname)'])
1478 for line in out.split('\n'):
1483 for line in out.split('\n'):
1479 revision, ref = line.split(' ')
1484 revision, ref = line.split(' ')
1480 if (not ref.startswith('refs/heads/') and
1485 if (not ref.startswith('refs/heads/') and
1481 not ref.startswith('refs/remotes/')):
1486 not ref.startswith('refs/remotes/')):
1482 continue
1487 continue
1483 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1488 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1484 continue # ignore remote/HEAD redirects
1489 continue # ignore remote/HEAD redirects
1485 branch2rev[ref] = revision
1490 branch2rev[ref] = revision
1486 rev2branch.setdefault(revision, []).append(ref)
1491 rev2branch.setdefault(revision, []).append(ref)
1487 return branch2rev, rev2branch
1492 return branch2rev, rev2branch
1488
1493
1489 def _gittracking(self, branches):
1494 def _gittracking(self, branches):
1490 'return map of remote branch to local tracking branch'
1495 'return map of remote branch to local tracking branch'
1491 # assumes no more than one local tracking branch for each remote
1496 # assumes no more than one local tracking branch for each remote
1492 tracking = {}
1497 tracking = {}
1493 for b in branches:
1498 for b in branches:
1494 if b.startswith('refs/remotes/'):
1499 if b.startswith('refs/remotes/'):
1495 continue
1500 continue
1496 bname = b.split('/', 2)[2]
1501 bname = b.split('/', 2)[2]
1497 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1502 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1498 if remote:
1503 if remote:
1499 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1504 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1500 tracking['refs/remotes/%s/%s' %
1505 tracking['refs/remotes/%s/%s' %
1501 (remote, ref.split('/', 2)[2])] = b
1506 (remote, ref.split('/', 2)[2])] = b
1502 return tracking
1507 return tracking
1503
1508
1504 def _abssource(self, source):
1509 def _abssource(self, source):
1505 if '://' not in source:
1510 if '://' not in source:
1506 # recognize the scp syntax as an absolute source
1511 # recognize the scp syntax as an absolute source
1507 colon = source.find(':')
1512 colon = source.find(':')
1508 if colon != -1 and '/' not in source[:colon]:
1513 if colon != -1 and '/' not in source[:colon]:
1509 return source
1514 return source
1510 self._subsource = source
1515 self._subsource = source
1511 return _abssource(self)
1516 return _abssource(self)
1512
1517
1513 def _fetch(self, source, revision):
1518 def _fetch(self, source, revision):
1514 if self._gitmissing():
1519 if self._gitmissing():
1515 source = self._abssource(source)
1520 source = self._abssource(source)
1516 self.ui.status(_('cloning subrepo %s from %s\n') %
1521 self.ui.status(_('cloning subrepo %s from %s\n') %
1517 (self._relpath, source))
1522 (self._relpath, source))
1518 self._gitnodir(['clone', source, self._abspath])
1523 self._gitnodir(['clone', source, self._abspath])
1519 if self._githavelocally(revision):
1524 if self._githavelocally(revision):
1520 return
1525 return
1521 self.ui.status(_('pulling subrepo %s from %s\n') %
1526 self.ui.status(_('pulling subrepo %s from %s\n') %
1522 (self._relpath, self._gitremote('origin')))
1527 (self._relpath, self._gitremote('origin')))
1523 # try only origin: the originally cloned repo
1528 # try only origin: the originally cloned repo
1524 self._gitcommand(['fetch'])
1529 self._gitcommand(['fetch'])
1525 if not self._githavelocally(revision):
1530 if not self._githavelocally(revision):
1526 raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
1531 raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
1527 (revision, self._relpath))
1532 (revision, self._relpath))
1528
1533
1529 @annotatesubrepoerror
1534 @annotatesubrepoerror
1530 def dirty(self, ignoreupdate=False):
1535 def dirty(self, ignoreupdate=False):
1531 if self._gitmissing():
1536 if self._gitmissing():
1532 return self._state[1] != ''
1537 return self._state[1] != ''
1533 if self._gitisbare():
1538 if self._gitisbare():
1534 return True
1539 return True
1535 if not ignoreupdate and self._state[1] != self._gitstate():
1540 if not ignoreupdate and self._state[1] != self._gitstate():
1536 # different version checked out
1541 # different version checked out
1537 return True
1542 return True
1538 # check for staged changes or modified files; ignore untracked files
1543 # check for staged changes or modified files; ignore untracked files
1539 self._gitupdatestat()
1544 self._gitupdatestat()
1540 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1545 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1541 return code == 1
1546 return code == 1
1542
1547
1543 def basestate(self):
1548 def basestate(self):
1544 return self._gitstate()
1549 return self._gitstate()
1545
1550
1546 @annotatesubrepoerror
1551 @annotatesubrepoerror
1547 def get(self, state, overwrite=False):
1552 def get(self, state, overwrite=False):
1548 source, revision, kind = state
1553 source, revision, kind = state
1549 if not revision:
1554 if not revision:
1550 self.remove()
1555 self.remove()
1551 return
1556 return
1552 self._fetch(source, revision)
1557 self._fetch(source, revision)
1553 # if the repo was set to be bare, unbare it
1558 # if the repo was set to be bare, unbare it
1554 if self._gitisbare():
1559 if self._gitisbare():
1555 self._gitcommand(['config', 'core.bare', 'false'])
1560 self._gitcommand(['config', 'core.bare', 'false'])
1556 if self._gitstate() == revision:
1561 if self._gitstate() == revision:
1557 self._gitcommand(['reset', '--hard', 'HEAD'])
1562 self._gitcommand(['reset', '--hard', 'HEAD'])
1558 return
1563 return
1559 elif self._gitstate() == revision:
1564 elif self._gitstate() == revision:
1560 if overwrite:
1565 if overwrite:
1561 # first reset the index to unmark new files for commit, because
1566 # first reset the index to unmark new files for commit, because
1562 # reset --hard will otherwise throw away files added for commit,
1567 # reset --hard will otherwise throw away files added for commit,
1563 # not just unmark them.
1568 # not just unmark them.
1564 self._gitcommand(['reset', 'HEAD'])
1569 self._gitcommand(['reset', 'HEAD'])
1565 self._gitcommand(['reset', '--hard', 'HEAD'])
1570 self._gitcommand(['reset', '--hard', 'HEAD'])
1566 return
1571 return
1567 branch2rev, rev2branch = self._gitbranchmap()
1572 branch2rev, rev2branch = self._gitbranchmap()
1568
1573
1569 def checkout(args):
1574 def checkout(args):
1570 cmd = ['checkout']
1575 cmd = ['checkout']
1571 if overwrite:
1576 if overwrite:
1572 # first reset the index to unmark new files for commit, because
1577 # first reset the index to unmark new files for commit, because
1573 # the -f option will otherwise throw away files added for
1578 # the -f option will otherwise throw away files added for
1574 # commit, not just unmark them.
1579 # commit, not just unmark them.
1575 self._gitcommand(['reset', 'HEAD'])
1580 self._gitcommand(['reset', 'HEAD'])
1576 cmd.append('-f')
1581 cmd.append('-f')
1577 self._gitcommand(cmd + args)
1582 self._gitcommand(cmd + args)
1578 _sanitize(self.ui, self.wvfs, '.git')
1583 _sanitize(self.ui, self.wvfs, '.git')
1579
1584
1580 def rawcheckout():
1585 def rawcheckout():
1581 # no branch to checkout, check it out with no branch
1586 # no branch to checkout, check it out with no branch
1582 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1587 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1583 self._relpath)
1588 self._relpath)
1584 self.ui.warn(_('check out a git branch if you intend '
1589 self.ui.warn(_('check out a git branch if you intend '
1585 'to make changes\n'))
1590 'to make changes\n'))
1586 checkout(['-q', revision])
1591 checkout(['-q', revision])
1587
1592
1588 if revision not in rev2branch:
1593 if revision not in rev2branch:
1589 rawcheckout()
1594 rawcheckout()
1590 return
1595 return
1591 branches = rev2branch[revision]
1596 branches = rev2branch[revision]
1592 firstlocalbranch = None
1597 firstlocalbranch = None
1593 for b in branches:
1598 for b in branches:
1594 if b == 'refs/heads/master':
1599 if b == 'refs/heads/master':
1595 # master trumps all other branches
1600 # master trumps all other branches
1596 checkout(['refs/heads/master'])
1601 checkout(['refs/heads/master'])
1597 return
1602 return
1598 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1603 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1599 firstlocalbranch = b
1604 firstlocalbranch = b
1600 if firstlocalbranch:
1605 if firstlocalbranch:
1601 checkout([firstlocalbranch])
1606 checkout([firstlocalbranch])
1602 return
1607 return
1603
1608
1604 tracking = self._gittracking(branch2rev.keys())
1609 tracking = self._gittracking(branch2rev.keys())
1605 # choose a remote branch already tracked if possible
1610 # choose a remote branch already tracked if possible
1606 remote = branches[0]
1611 remote = branches[0]
1607 if remote not in tracking:
1612 if remote not in tracking:
1608 for b in branches:
1613 for b in branches:
1609 if b in tracking:
1614 if b in tracking:
1610 remote = b
1615 remote = b
1611 break
1616 break
1612
1617
1613 if remote not in tracking:
1618 if remote not in tracking:
1614 # create a new local tracking branch
1619 # create a new local tracking branch
1615 local = remote.split('/', 3)[3]
1620 local = remote.split('/', 3)[3]
1616 checkout(['-b', local, remote])
1621 checkout(['-b', local, remote])
1617 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1622 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1618 # When updating to a tracked remote branch,
1623 # When updating to a tracked remote branch,
1619 # if the local tracking branch is downstream of it,
1624 # if the local tracking branch is downstream of it,
1620 # a normal `git pull` would have performed a "fast-forward merge"
1625 # a normal `git pull` would have performed a "fast-forward merge"
1621 # which is equivalent to updating the local branch to the remote.
1626 # which is equivalent to updating the local branch to the remote.
1622 # Since we are only looking at branching at update, we need to
1627 # Since we are only looking at branching at update, we need to
1623 # detect this situation and perform this action lazily.
1628 # detect this situation and perform this action lazily.
1624 if tracking[remote] != self._gitcurrentbranch():
1629 if tracking[remote] != self._gitcurrentbranch():
1625 checkout([tracking[remote]])
1630 checkout([tracking[remote]])
1626 self._gitcommand(['merge', '--ff', remote])
1631 self._gitcommand(['merge', '--ff', remote])
1627 _sanitize(self.ui, self.wvfs, '.git')
1632 _sanitize(self.ui, self.wvfs, '.git')
1628 else:
1633 else:
1629 # a real merge would be required, just checkout the revision
1634 # a real merge would be required, just checkout the revision
1630 rawcheckout()
1635 rawcheckout()
1631
1636
1632 @annotatesubrepoerror
1637 @annotatesubrepoerror
1633 def commit(self, text, user, date):
1638 def commit(self, text, user, date):
1634 if self._gitmissing():
1639 if self._gitmissing():
1635 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1640 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1636 cmd = ['commit', '-a', '-m', text]
1641 cmd = ['commit', '-a', '-m', text]
1637 env = encoding.environ.copy()
1642 env = encoding.environ.copy()
1638 if user:
1643 if user:
1639 cmd += ['--author', user]
1644 cmd += ['--author', user]
1640 if date:
1645 if date:
1641 # git's date parser silently ignores when seconds < 1e9
1646 # git's date parser silently ignores when seconds < 1e9
1642 # convert to ISO8601
1647 # convert to ISO8601
1643 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1648 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1644 '%Y-%m-%dT%H:%M:%S %1%2')
1649 '%Y-%m-%dT%H:%M:%S %1%2')
1645 self._gitcommand(cmd, env=env)
1650 self._gitcommand(cmd, env=env)
1646 # make sure commit works otherwise HEAD might not exist under certain
1651 # make sure commit works otherwise HEAD might not exist under certain
1647 # circumstances
1652 # circumstances
1648 return self._gitstate()
1653 return self._gitstate()
1649
1654
1650 @annotatesubrepoerror
1655 @annotatesubrepoerror
1651 def merge(self, state):
1656 def merge(self, state):
1652 source, revision, kind = state
1657 source, revision, kind = state
1653 self._fetch(source, revision)
1658 self._fetch(source, revision)
1654 base = self._gitcommand(['merge-base', revision, self._state[1]])
1659 base = self._gitcommand(['merge-base', revision, self._state[1]])
1655 self._gitupdatestat()
1660 self._gitupdatestat()
1656 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1661 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1657
1662
1658 def mergefunc():
1663 def mergefunc():
1659 if base == revision:
1664 if base == revision:
1660 self.get(state) # fast forward merge
1665 self.get(state) # fast forward merge
1661 elif base != self._state[1]:
1666 elif base != self._state[1]:
1662 self._gitcommand(['merge', '--no-commit', revision])
1667 self._gitcommand(['merge', '--no-commit', revision])
1663 _sanitize(self.ui, self.wvfs, '.git')
1668 _sanitize(self.ui, self.wvfs, '.git')
1664
1669
1665 if self.dirty():
1670 if self.dirty():
1666 if self._gitstate() != revision:
1671 if self._gitstate() != revision:
1667 dirty = self._gitstate() == self._state[1] or code != 0
1672 dirty = self._gitstate() == self._state[1] or code != 0
1668 if _updateprompt(self.ui, self, dirty,
1673 if _updateprompt(self.ui, self, dirty,
1669 self._state[1][:7], revision[:7]):
1674 self._state[1][:7], revision[:7]):
1670 mergefunc()
1675 mergefunc()
1671 else:
1676 else:
1672 mergefunc()
1677 mergefunc()
1673
1678
1674 @annotatesubrepoerror
1679 @annotatesubrepoerror
1675 def push(self, opts):
1680 def push(self, opts):
1676 force = opts.get('force')
1681 force = opts.get('force')
1677
1682
1678 if not self._state[1]:
1683 if not self._state[1]:
1679 return True
1684 return True
1680 if self._gitmissing():
1685 if self._gitmissing():
1681 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1686 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1682 # if a branch in origin contains the revision, nothing to do
1687 # if a branch in origin contains the revision, nothing to do
1683 branch2rev, rev2branch = self._gitbranchmap()
1688 branch2rev, rev2branch = self._gitbranchmap()
1684 if self._state[1] in rev2branch:
1689 if self._state[1] in rev2branch:
1685 for b in rev2branch[self._state[1]]:
1690 for b in rev2branch[self._state[1]]:
1686 if b.startswith('refs/remotes/origin/'):
1691 if b.startswith('refs/remotes/origin/'):
1687 return True
1692 return True
1688 for b, revision in branch2rev.iteritems():
1693 for b, revision in branch2rev.iteritems():
1689 if b.startswith('refs/remotes/origin/'):
1694 if b.startswith('refs/remotes/origin/'):
1690 if self._gitisancestor(self._state[1], revision):
1695 if self._gitisancestor(self._state[1], revision):
1691 return True
1696 return True
1692 # otherwise, try to push the currently checked out branch
1697 # otherwise, try to push the currently checked out branch
1693 cmd = ['push']
1698 cmd = ['push']
1694 if force:
1699 if force:
1695 cmd.append('--force')
1700 cmd.append('--force')
1696
1701
1697 current = self._gitcurrentbranch()
1702 current = self._gitcurrentbranch()
1698 if current:
1703 if current:
1699 # determine if the current branch is even useful
1704 # determine if the current branch is even useful
1700 if not self._gitisancestor(self._state[1], current):
1705 if not self._gitisancestor(self._state[1], current):
1701 self.ui.warn(_('unrelated git branch checked out '
1706 self.ui.warn(_('unrelated git branch checked out '
1702 'in subrepo %s\n') % self._relpath)
1707 'in subrepo %s\n') % self._relpath)
1703 return False
1708 return False
1704 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1709 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1705 (current.split('/', 2)[2], self._relpath))
1710 (current.split('/', 2)[2], self._relpath))
1706 ret = self._gitdir(cmd + ['origin', current])
1711 ret = self._gitdir(cmd + ['origin', current])
1707 return ret[1] == 0
1712 return ret[1] == 0
1708 else:
1713 else:
1709 self.ui.warn(_('no branch checked out in subrepo %s\n'
1714 self.ui.warn(_('no branch checked out in subrepo %s\n'
1710 'cannot push revision %s\n') %
1715 'cannot push revision %s\n') %
1711 (self._relpath, self._state[1]))
1716 (self._relpath, self._state[1]))
1712 return False
1717 return False
1713
1718
1714 @annotatesubrepoerror
1719 @annotatesubrepoerror
1715 def add(self, ui, match, prefix, explicitonly, **opts):
1720 def add(self, ui, match, prefix, explicitonly, **opts):
1716 if self._gitmissing():
1721 if self._gitmissing():
1717 return []
1722 return []
1718
1723
1719 (modified, added, removed,
1724 (modified, added, removed,
1720 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1725 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1721 clean=True)
1726 clean=True)
1722
1727
1723 tracked = set()
1728 tracked = set()
1724 # dirstates 'amn' warn, 'r' is added again
1729 # dirstates 'amn' warn, 'r' is added again
1725 for l in (modified, added, deleted, clean):
1730 for l in (modified, added, deleted, clean):
1726 tracked.update(l)
1731 tracked.update(l)
1727
1732
1728 # Unknown files not of interest will be rejected by the matcher
1733 # Unknown files not of interest will be rejected by the matcher
1729 files = unknown
1734 files = unknown
1730 files.extend(match.files())
1735 files.extend(match.files())
1731
1736
1732 rejected = []
1737 rejected = []
1733
1738
1734 files = [f for f in sorted(set(files)) if match(f)]
1739 files = [f for f in sorted(set(files)) if match(f)]
1735 for f in files:
1740 for f in files:
1736 exact = match.exact(f)
1741 exact = match.exact(f)
1737 command = ["add"]
1742 command = ["add"]
1738 if exact:
1743 if exact:
1739 command.append("-f") #should be added, even if ignored
1744 command.append("-f") #should be added, even if ignored
1740 if ui.verbose or not exact:
1745 if ui.verbose or not exact:
1741 ui.status(_('adding %s\n') % match.rel(f))
1746 ui.status(_('adding %s\n') % match.rel(f))
1742
1747
1743 if f in tracked: # hg prints 'adding' even if already tracked
1748 if f in tracked: # hg prints 'adding' even if already tracked
1744 if exact:
1749 if exact:
1745 rejected.append(f)
1750 rejected.append(f)
1746 continue
1751 continue
1747 if not opts.get('dry_run'):
1752 if not opts.get('dry_run'):
1748 self._gitcommand(command + [f])
1753 self._gitcommand(command + [f])
1749
1754
1750 for f in rejected:
1755 for f in rejected:
1751 ui.warn(_("%s already tracked!\n") % match.abs(f))
1756 ui.warn(_("%s already tracked!\n") % match.abs(f))
1752
1757
1753 return rejected
1758 return rejected
1754
1759
1755 @annotatesubrepoerror
1760 @annotatesubrepoerror
1756 def remove(self):
1761 def remove(self):
1757 if self._gitmissing():
1762 if self._gitmissing():
1758 return
1763 return
1759 if self.dirty():
1764 if self.dirty():
1760 self.ui.warn(_('not removing repo %s because '
1765 self.ui.warn(_('not removing repo %s because '
1761 'it has changes.\n') % self._relpath)
1766 'it has changes.\n') % self._relpath)
1762 return
1767 return
1763 # we can't fully delete the repository as it may contain
1768 # we can't fully delete the repository as it may contain
1764 # local-only history
1769 # local-only history
1765 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1770 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1766 self._gitcommand(['config', 'core.bare', 'true'])
1771 self._gitcommand(['config', 'core.bare', 'true'])
1767 for f, kind in self.wvfs.readdir():
1772 for f, kind in self.wvfs.readdir():
1768 if f == '.git':
1773 if f == '.git':
1769 continue
1774 continue
1770 if kind == stat.S_IFDIR:
1775 if kind == stat.S_IFDIR:
1771 self.wvfs.rmtree(f)
1776 self.wvfs.rmtree(f)
1772 else:
1777 else:
1773 self.wvfs.unlink(f)
1778 self.wvfs.unlink(f)
1774
1779
1775 def archive(self, archiver, prefix, match=None):
1780 def archive(self, archiver, prefix, match=None):
1776 total = 0
1781 total = 0
1777 source, revision = self._state
1782 source, revision = self._state
1778 if not revision:
1783 if not revision:
1779 return total
1784 return total
1780 self._fetch(source, revision)
1785 self._fetch(source, revision)
1781
1786
1782 # Parse git's native archive command.
1787 # Parse git's native archive command.
1783 # This should be much faster than manually traversing the trees
1788 # This should be much faster than manually traversing the trees
1784 # and objects with many subprocess calls.
1789 # and objects with many subprocess calls.
1785 tarstream = self._gitcommand(['archive', revision], stream=True)
1790 tarstream = self._gitcommand(['archive', revision], stream=True)
1786 tar = tarfile.open(fileobj=tarstream, mode='r|')
1791 tar = tarfile.open(fileobj=tarstream, mode='r|')
1787 relpath = subrelpath(self)
1792 relpath = subrelpath(self)
1788 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1793 self.ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1789 for i, info in enumerate(tar):
1794 for i, info in enumerate(tar):
1790 if info.isdir():
1795 if info.isdir():
1791 continue
1796 continue
1792 if match and not match(info.name):
1797 if match and not match(info.name):
1793 continue
1798 continue
1794 if info.issym():
1799 if info.issym():
1795 data = info.linkname
1800 data = info.linkname
1796 else:
1801 else:
1797 data = tar.extractfile(info).read()
1802 data = tar.extractfile(info).read()
1798 archiver.addfile(prefix + self._path + '/' + info.name,
1803 archiver.addfile(prefix + self._path + '/' + info.name,
1799 info.mode, info.issym(), data)
1804 info.mode, info.issym(), data)
1800 total += 1
1805 total += 1
1801 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1806 self.ui.progress(_('archiving (%s)') % relpath, i + 1,
1802 unit=_('files'))
1807 unit=_('files'))
1803 self.ui.progress(_('archiving (%s)') % relpath, None)
1808 self.ui.progress(_('archiving (%s)') % relpath, None)
1804 return total
1809 return total
1805
1810
1806
1811
1807 @annotatesubrepoerror
1812 @annotatesubrepoerror
1808 def cat(self, match, prefix, **opts):
1813 def cat(self, match, prefix, **opts):
1809 rev = self._state[1]
1814 rev = self._state[1]
1810 if match.anypats():
1815 if match.anypats():
1811 return 1 #No support for include/exclude yet
1816 return 1 #No support for include/exclude yet
1812
1817
1813 if not match.files():
1818 if not match.files():
1814 return 1
1819 return 1
1815
1820
1816 for f in match.files():
1821 for f in match.files():
1817 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1822 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1818 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1823 fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
1819 self._ctx.node(),
1824 self._ctx.node(),
1820 pathname=self.wvfs.reljoin(prefix, f))
1825 pathname=self.wvfs.reljoin(prefix, f))
1821 fp.write(output)
1826 fp.write(output)
1822 fp.close()
1827 fp.close()
1823 return 0
1828 return 0
1824
1829
1825
1830
1826 @annotatesubrepoerror
1831 @annotatesubrepoerror
1827 def status(self, rev2, **opts):
1832 def status(self, rev2, **opts):
1828 rev1 = self._state[1]
1833 rev1 = self._state[1]
1829 if self._gitmissing() or not rev1:
1834 if self._gitmissing() or not rev1:
1830 # if the repo is missing, return no results
1835 # if the repo is missing, return no results
1831 return scmutil.status([], [], [], [], [], [], [])
1836 return scmutil.status([], [], [], [], [], [], [])
1832 modified, added, removed = [], [], []
1837 modified, added, removed = [], [], []
1833 self._gitupdatestat()
1838 self._gitupdatestat()
1834 if rev2:
1839 if rev2:
1835 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1840 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1836 else:
1841 else:
1837 command = ['diff-index', '--no-renames', rev1]
1842 command = ['diff-index', '--no-renames', rev1]
1838 out = self._gitcommand(command)
1843 out = self._gitcommand(command)
1839 for line in out.split('\n'):
1844 for line in out.split('\n'):
1840 tab = line.find('\t')
1845 tab = line.find('\t')
1841 if tab == -1:
1846 if tab == -1:
1842 continue
1847 continue
1843 status, f = line[tab - 1], line[tab + 1:]
1848 status, f = line[tab - 1], line[tab + 1:]
1844 if status == 'M':
1849 if status == 'M':
1845 modified.append(f)
1850 modified.append(f)
1846 elif status == 'A':
1851 elif status == 'A':
1847 added.append(f)
1852 added.append(f)
1848 elif status == 'D':
1853 elif status == 'D':
1849 removed.append(f)
1854 removed.append(f)
1850
1855
1851 deleted, unknown, ignored, clean = [], [], [], []
1856 deleted, unknown, ignored, clean = [], [], [], []
1852
1857
1853 command = ['status', '--porcelain', '-z']
1858 command = ['status', '--porcelain', '-z']
1854 if opts.get('unknown'):
1859 if opts.get('unknown'):
1855 command += ['--untracked-files=all']
1860 command += ['--untracked-files=all']
1856 if opts.get('ignored'):
1861 if opts.get('ignored'):
1857 command += ['--ignored']
1862 command += ['--ignored']
1858 out = self._gitcommand(command)
1863 out = self._gitcommand(command)
1859
1864
1860 changedfiles = set()
1865 changedfiles = set()
1861 changedfiles.update(modified)
1866 changedfiles.update(modified)
1862 changedfiles.update(added)
1867 changedfiles.update(added)
1863 changedfiles.update(removed)
1868 changedfiles.update(removed)
1864 for line in out.split('\0'):
1869 for line in out.split('\0'):
1865 if not line:
1870 if not line:
1866 continue
1871 continue
1867 st = line[0:2]
1872 st = line[0:2]
1868 #moves and copies show 2 files on one line
1873 #moves and copies show 2 files on one line
1869 if line.find('\0') >= 0:
1874 if line.find('\0') >= 0:
1870 filename1, filename2 = line[3:].split('\0')
1875 filename1, filename2 = line[3:].split('\0')
1871 else:
1876 else:
1872 filename1 = line[3:]
1877 filename1 = line[3:]
1873 filename2 = None
1878 filename2 = None
1874
1879
1875 changedfiles.add(filename1)
1880 changedfiles.add(filename1)
1876 if filename2:
1881 if filename2:
1877 changedfiles.add(filename2)
1882 changedfiles.add(filename2)
1878
1883
1879 if st == '??':
1884 if st == '??':
1880 unknown.append(filename1)
1885 unknown.append(filename1)
1881 elif st == '!!':
1886 elif st == '!!':
1882 ignored.append(filename1)
1887 ignored.append(filename1)
1883
1888
1884 if opts.get('clean'):
1889 if opts.get('clean'):
1885 out = self._gitcommand(['ls-files'])
1890 out = self._gitcommand(['ls-files'])
1886 for f in out.split('\n'):
1891 for f in out.split('\n'):
1887 if not f in changedfiles:
1892 if not f in changedfiles:
1888 clean.append(f)
1893 clean.append(f)
1889
1894
1890 return scmutil.status(modified, added, removed, deleted,
1895 return scmutil.status(modified, added, removed, deleted,
1891 unknown, ignored, clean)
1896 unknown, ignored, clean)
1892
1897
1893 @annotatesubrepoerror
1898 @annotatesubrepoerror
1894 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1899 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1895 node1 = self._state[1]
1900 node1 = self._state[1]
1896 cmd = ['diff', '--no-renames']
1901 cmd = ['diff', '--no-renames']
1897 if opts['stat']:
1902 if opts['stat']:
1898 cmd.append('--stat')
1903 cmd.append('--stat')
1899 else:
1904 else:
1900 # for Git, this also implies '-p'
1905 # for Git, this also implies '-p'
1901 cmd.append('-U%d' % diffopts.context)
1906 cmd.append('-U%d' % diffopts.context)
1902
1907
1903 gitprefix = self.wvfs.reljoin(prefix, self._path)
1908 gitprefix = self.wvfs.reljoin(prefix, self._path)
1904
1909
1905 if diffopts.noprefix:
1910 if diffopts.noprefix:
1906 cmd.extend(['--src-prefix=%s/' % gitprefix,
1911 cmd.extend(['--src-prefix=%s/' % gitprefix,
1907 '--dst-prefix=%s/' % gitprefix])
1912 '--dst-prefix=%s/' % gitprefix])
1908 else:
1913 else:
1909 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1914 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1910 '--dst-prefix=b/%s/' % gitprefix])
1915 '--dst-prefix=b/%s/' % gitprefix])
1911
1916
1912 if diffopts.ignorews:
1917 if diffopts.ignorews:
1913 cmd.append('--ignore-all-space')
1918 cmd.append('--ignore-all-space')
1914 if diffopts.ignorewsamount:
1919 if diffopts.ignorewsamount:
1915 cmd.append('--ignore-space-change')
1920 cmd.append('--ignore-space-change')
1916 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1921 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1917 and diffopts.ignoreblanklines:
1922 and diffopts.ignoreblanklines:
1918 cmd.append('--ignore-blank-lines')
1923 cmd.append('--ignore-blank-lines')
1919
1924
1920 cmd.append(node1)
1925 cmd.append(node1)
1921 if node2:
1926 if node2:
1922 cmd.append(node2)
1927 cmd.append(node2)
1923
1928
1924 output = ""
1929 output = ""
1925 if match.always():
1930 if match.always():
1926 output += self._gitcommand(cmd) + '\n'
1931 output += self._gitcommand(cmd) + '\n'
1927 else:
1932 else:
1928 st = self.status(node2)[:3]
1933 st = self.status(node2)[:3]
1929 files = [f for sublist in st for f in sublist]
1934 files = [f for sublist in st for f in sublist]
1930 for f in files:
1935 for f in files:
1931 if match(f):
1936 if match(f):
1932 output += self._gitcommand(cmd + ['--', f]) + '\n'
1937 output += self._gitcommand(cmd + ['--', f]) + '\n'
1933
1938
1934 if output.strip():
1939 if output.strip():
1935 ui.write(output)
1940 ui.write(output)
1936
1941
1937 @annotatesubrepoerror
1942 @annotatesubrepoerror
1938 def revert(self, substate, *pats, **opts):
1943 def revert(self, substate, *pats, **opts):
1939 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1944 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1940 if not opts.get('no_backup'):
1945 if not opts.get('no_backup'):
1941 status = self.status(None)
1946 status = self.status(None)
1942 names = status.modified
1947 names = status.modified
1943 for name in names:
1948 for name in names:
1944 bakname = scmutil.origpath(self.ui, self._subparent, name)
1949 bakname = scmutil.origpath(self.ui, self._subparent, name)
1945 self.ui.note(_('saving current version of %s as %s\n') %
1950 self.ui.note(_('saving current version of %s as %s\n') %
1946 (name, bakname))
1951 (name, bakname))
1947 self.wvfs.rename(name, bakname)
1952 self.wvfs.rename(name, bakname)
1948
1953
1949 if not opts.get('dry_run'):
1954 if not opts.get('dry_run'):
1950 self.get(substate, overwrite=True)
1955 self.get(substate, overwrite=True)
1951 return []
1956 return []
1952
1957
1953 def shortid(self, revid):
1958 def shortid(self, revid):
1954 return revid[:7]
1959 return revid[:7]
1955
1960
1956 types = {
1961 types = {
1957 'hg': hgsubrepo,
1962 'hg': hgsubrepo,
1958 'svn': svnsubrepo,
1963 'svn': svnsubrepo,
1959 'git': gitsubrepo,
1964 'git': gitsubrepo,
1960 }
1965 }
@@ -1,1778 +1,1778 b''
1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
2
2
3 $ echo "[ui]" >> $HGRCPATH
3 $ echo "[ui]" >> $HGRCPATH
4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
5
5
6 $ hg init t
6 $ hg init t
7 $ cd t
7 $ cd t
8
8
9 first revision, no sub
9 first revision, no sub
10
10
11 $ echo a > a
11 $ echo a > a
12 $ hg ci -Am0
12 $ hg ci -Am0
13 adding a
13 adding a
14
14
15 add first sub
15 add first sub
16
16
17 $ echo s = s > .hgsub
17 $ echo s = s > .hgsub
18 $ hg add .hgsub
18 $ hg add .hgsub
19 $ hg init s
19 $ hg init s
20 $ echo a > s/a
20 $ echo a > s/a
21
21
22 Issue2232: committing a subrepo without .hgsub
22 Issue2232: committing a subrepo without .hgsub
23
23
24 $ hg ci -mbad s
24 $ hg ci -mbad s
25 abort: can't commit subrepos without .hgsub
25 abort: can't commit subrepos without .hgsub
26 [255]
26 [255]
27
27
28 $ hg -R s add s/a
28 $ hg -R s add s/a
29 $ hg files -S
29 $ hg files -S
30 .hgsub
30 .hgsub
31 a
31 a
32 s/a (glob)
32 s/a (glob)
33
33
34 $ hg -R s ci -Ams0
34 $ hg -R s ci -Ams0
35 $ hg sum
35 $ hg sum
36 parent: 0:f7b1eb17ad24 tip
36 parent: 0:f7b1eb17ad24 tip
37 0
37 0
38 branch: default
38 branch: default
39 commit: 1 added, 1 subrepos
39 commit: 1 added, 1 subrepos
40 update: (current)
40 update: (current)
41 phases: 1 draft
41 phases: 1 draft
42 $ hg ci -m1
42 $ hg ci -m1
43
43
44 test handling .hgsubstate "added" explicitly.
44 test handling .hgsubstate "added" explicitly.
45
45
46 $ hg parents --template '{node}\n{files}\n'
46 $ hg parents --template '{node}\n{files}\n'
47 7cf8cfea66e410e8e3336508dfeec07b3192de51
47 7cf8cfea66e410e8e3336508dfeec07b3192de51
48 .hgsub .hgsubstate
48 .hgsub .hgsubstate
49 $ hg rollback -q
49 $ hg rollback -q
50 $ hg add .hgsubstate
50 $ hg add .hgsubstate
51 $ hg ci -m1
51 $ hg ci -m1
52 $ hg parents --template '{node}\n{files}\n'
52 $ hg parents --template '{node}\n{files}\n'
53 7cf8cfea66e410e8e3336508dfeec07b3192de51
53 7cf8cfea66e410e8e3336508dfeec07b3192de51
54 .hgsub .hgsubstate
54 .hgsub .hgsubstate
55
55
56 Subrepopath which overlaps with filepath, does not change warnings in remove()
56 Subrepopath which overlaps with filepath, does not change warnings in remove()
57
57
58 $ mkdir snot
58 $ mkdir snot
59 $ touch snot/file
59 $ touch snot/file
60 $ hg remove -S snot/file
60 $ hg remove -S snot/file
61 not removing snot/file: file is untracked (glob)
61 not removing snot/file: file is untracked (glob)
62 [1]
62 [1]
63 $ hg cat snot/filenot
63 $ hg cat snot/filenot
64 snot/filenot: no such file in rev 7cf8cfea66e4 (glob)
64 snot/filenot: no such file in rev 7cf8cfea66e4 (glob)
65 [1]
65 [1]
66 $ rm -r snot
66 $ rm -r snot
67
67
68 Revert subrepo and test subrepo fileset keyword:
68 Revert subrepo and test subrepo fileset keyword:
69
69
70 $ echo b > s/a
70 $ echo b > s/a
71 $ hg revert --dry-run "set:subrepo('glob:s*')"
71 $ hg revert --dry-run "set:subrepo('glob:s*')"
72 reverting subrepo s
72 reverting subrepo s
73 reverting s/a (glob)
73 reverting s/a (glob)
74 $ cat s/a
74 $ cat s/a
75 b
75 b
76 $ hg revert "set:subrepo('glob:s*')"
76 $ hg revert "set:subrepo('glob:s*')"
77 reverting subrepo s
77 reverting subrepo s
78 reverting s/a (glob)
78 reverting s/a (glob)
79 $ cat s/a
79 $ cat s/a
80 a
80 a
81 $ rm s/a.orig
81 $ rm s/a.orig
82
82
83 Revert subrepo with no backup. The "reverting s/a" line is gone since
83 Revert subrepo with no backup. The "reverting s/a" line is gone since
84 we're really running 'hg update' in the subrepo:
84 we're really running 'hg update' in the subrepo:
85
85
86 $ echo b > s/a
86 $ echo b > s/a
87 $ hg revert --no-backup s
87 $ hg revert --no-backup s
88 reverting subrepo s
88 reverting subrepo s
89
89
90 Issue2022: update -C
90 Issue2022: update -C
91
91
92 $ echo b > s/a
92 $ echo b > s/a
93 $ hg sum
93 $ hg sum
94 parent: 1:7cf8cfea66e4 tip
94 parent: 1:7cf8cfea66e4 tip
95 1
95 1
96 branch: default
96 branch: default
97 commit: 1 subrepos
97 commit: 1 subrepos
98 update: (current)
98 update: (current)
99 phases: 2 draft
99 phases: 2 draft
100 $ hg co -C 1
100 $ hg co -C 1
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 $ hg sum
102 $ hg sum
103 parent: 1:7cf8cfea66e4 tip
103 parent: 1:7cf8cfea66e4 tip
104 1
104 1
105 branch: default
105 branch: default
106 commit: (clean)
106 commit: (clean)
107 update: (current)
107 update: (current)
108 phases: 2 draft
108 phases: 2 draft
109
109
110 commands that require a clean repo should respect subrepos
110 commands that require a clean repo should respect subrepos
111
111
112 $ echo b >> s/a
112 $ echo b >> s/a
113 $ hg backout tip
113 $ hg backout tip
114 abort: uncommitted changes in subrepository 's'
114 abort: uncommitted changes in subrepository 's'
115 [255]
115 [255]
116 $ hg revert -C -R s s/a
116 $ hg revert -C -R s s/a
117
117
118 add sub sub
118 add sub sub
119
119
120 $ echo ss = ss > s/.hgsub
120 $ echo ss = ss > s/.hgsub
121 $ hg init s/ss
121 $ hg init s/ss
122 $ echo a > s/ss/a
122 $ echo a > s/ss/a
123 $ hg -R s add s/.hgsub
123 $ hg -R s add s/.hgsub
124 $ hg -R s/ss add s/ss/a
124 $ hg -R s/ss add s/ss/a
125 $ hg sum
125 $ hg sum
126 parent: 1:7cf8cfea66e4 tip
126 parent: 1:7cf8cfea66e4 tip
127 1
127 1
128 branch: default
128 branch: default
129 commit: 1 subrepos
129 commit: 1 subrepos
130 update: (current)
130 update: (current)
131 phases: 2 draft
131 phases: 2 draft
132 $ hg ci -m2
132 $ hg ci -m2
133 committing subrepository s
133 committing subrepository s
134 committing subrepository s/ss (glob)
134 committing subrepository s/ss (glob)
135 $ hg sum
135 $ hg sum
136 parent: 2:df30734270ae tip
136 parent: 2:df30734270ae tip
137 2
137 2
138 branch: default
138 branch: default
139 commit: (clean)
139 commit: (clean)
140 update: (current)
140 update: (current)
141 phases: 3 draft
141 phases: 3 draft
142
142
143 test handling .hgsubstate "modified" explicitly.
143 test handling .hgsubstate "modified" explicitly.
144
144
145 $ hg parents --template '{node}\n{files}\n'
145 $ hg parents --template '{node}\n{files}\n'
146 df30734270ae757feb35e643b7018e818e78a9aa
146 df30734270ae757feb35e643b7018e818e78a9aa
147 .hgsubstate
147 .hgsubstate
148 $ hg rollback -q
148 $ hg rollback -q
149 $ hg status -A .hgsubstate
149 $ hg status -A .hgsubstate
150 M .hgsubstate
150 M .hgsubstate
151 $ hg ci -m2
151 $ hg ci -m2
152 $ hg parents --template '{node}\n{files}\n'
152 $ hg parents --template '{node}\n{files}\n'
153 df30734270ae757feb35e643b7018e818e78a9aa
153 df30734270ae757feb35e643b7018e818e78a9aa
154 .hgsubstate
154 .hgsubstate
155
155
156 bump sub rev (and check it is ignored by ui.commitsubrepos)
156 bump sub rev (and check it is ignored by ui.commitsubrepos)
157
157
158 $ echo b > s/a
158 $ echo b > s/a
159 $ hg -R s ci -ms1
159 $ hg -R s ci -ms1
160 $ hg --config ui.commitsubrepos=no ci -m3
160 $ hg --config ui.commitsubrepos=no ci -m3
161
161
162 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
162 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
163
163
164 $ echo c > s/a
164 $ echo c > s/a
165 $ hg --config ui.commitsubrepos=no ci -m4
165 $ hg --config ui.commitsubrepos=no ci -m4
166 abort: uncommitted changes in subrepository 's'
166 abort: uncommitted changes in subrepository 's'
167 (use --subrepos for recursive commit)
167 (use --subrepos for recursive commit)
168 [255]
168 [255]
169 $ hg id
169 $ hg id
170 f6affe3fbfaa+ tip
170 f6affe3fbfaa+ tip
171 $ hg -R s ci -mc
171 $ hg -R s ci -mc
172 $ hg id
172 $ hg id
173 f6affe3fbfaa+ tip
173 f6affe3fbfaa+ tip
174 $ echo d > s/a
174 $ echo d > s/a
175 $ hg ci -m4
175 $ hg ci -m4
176 committing subrepository s
176 committing subrepository s
177 $ hg tip -R s
177 $ hg tip -R s
178 changeset: 4:02dcf1d70411
178 changeset: 4:02dcf1d70411
179 tag: tip
179 tag: tip
180 user: test
180 user: test
181 date: Thu Jan 01 00:00:00 1970 +0000
181 date: Thu Jan 01 00:00:00 1970 +0000
182 summary: 4
182 summary: 4
183
183
184
184
185 check caching
185 check caching
186
186
187 $ hg co 0
187 $ hg co 0
188 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
188 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
189 $ hg debugsub
189 $ hg debugsub
190
190
191 restore
191 restore
192
192
193 $ hg co
193 $ hg co
194 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 $ hg debugsub
195 $ hg debugsub
196 path s
196 path s
197 source s
197 source s
198 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
198 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
199
199
200 new branch for merge tests
200 new branch for merge tests
201
201
202 $ hg co 1
202 $ hg co 1
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 $ echo t = t >> .hgsub
204 $ echo t = t >> .hgsub
205 $ hg init t
205 $ hg init t
206 $ echo t > t/t
206 $ echo t > t/t
207 $ hg -R t add t
207 $ hg -R t add t
208 adding t/t (glob)
208 adding t/t (glob)
209
209
210 5
210 5
211
211
212 $ hg ci -m5 # add sub
212 $ hg ci -m5 # add sub
213 committing subrepository t
213 committing subrepository t
214 created new head
214 created new head
215 $ echo t2 > t/t
215 $ echo t2 > t/t
216
216
217 6
217 6
218
218
219 $ hg st -R s
219 $ hg st -R s
220 $ hg ci -m6 # change sub
220 $ hg ci -m6 # change sub
221 committing subrepository t
221 committing subrepository t
222 $ hg debugsub
222 $ hg debugsub
223 path s
223 path s
224 source s
224 source s
225 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
225 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
226 path t
226 path t
227 source t
227 source t
228 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
228 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
229 $ echo t3 > t/t
229 $ echo t3 > t/t
230
230
231 7
231 7
232
232
233 $ hg ci -m7 # change sub again for conflict test
233 $ hg ci -m7 # change sub again for conflict test
234 committing subrepository t
234 committing subrepository t
235 $ hg rm .hgsub
235 $ hg rm .hgsub
236
236
237 8
237 8
238
238
239 $ hg ci -m8 # remove sub
239 $ hg ci -m8 # remove sub
240
240
241 test handling .hgsubstate "removed" explicitly.
241 test handling .hgsubstate "removed" explicitly.
242
242
243 $ hg parents --template '{node}\n{files}\n'
243 $ hg parents --template '{node}\n{files}\n'
244 96615c1dad2dc8e3796d7332c77ce69156f7b78e
244 96615c1dad2dc8e3796d7332c77ce69156f7b78e
245 .hgsub .hgsubstate
245 .hgsub .hgsubstate
246 $ hg rollback -q
246 $ hg rollback -q
247 $ hg remove .hgsubstate
247 $ hg remove .hgsubstate
248 $ hg ci -m8
248 $ hg ci -m8
249 $ hg parents --template '{node}\n{files}\n'
249 $ hg parents --template '{node}\n{files}\n'
250 96615c1dad2dc8e3796d7332c77ce69156f7b78e
250 96615c1dad2dc8e3796d7332c77ce69156f7b78e
251 .hgsub .hgsubstate
251 .hgsub .hgsubstate
252
252
253 merge tests
253 merge tests
254
254
255 $ hg co -C 3
255 $ hg co -C 3
256 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 $ hg merge 5 # test adding
257 $ hg merge 5 # test adding
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 (branch merge, don't forget to commit)
259 (branch merge, don't forget to commit)
260 $ hg debugsub
260 $ hg debugsub
261 path s
261 path s
262 source s
262 source s
263 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
263 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
264 path t
264 path t
265 source t
265 source t
266 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
266 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
267 $ hg ci -m9
267 $ hg ci -m9
268 created new head
268 created new head
269 $ hg merge 6 --debug # test change
269 $ hg merge 6 --debug # test change
270 searching for copies back to rev 2
270 searching for copies back to rev 2
271 resolving manifests
271 resolving manifests
272 branchmerge: True, force: False, partial: False
272 branchmerge: True, force: False, partial: False
273 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
273 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
274 starting 4 threads for background file closing (?)
274 starting 4 threads for background file closing (?)
275 .hgsubstate: versions differ -> m (premerge)
275 .hgsubstate: versions differ -> m (premerge)
276 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
276 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
277 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
277 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
278 getting subrepo t
278 getting subrepo t
279 resolving manifests
279 resolving manifests
280 branchmerge: False, force: False, partial: False
280 branchmerge: False, force: False, partial: False
281 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
281 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
282 t: remote is newer -> g
282 t: remote is newer -> g
283 getting t
283 getting t
284 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 (branch merge, don't forget to commit)
285 (branch merge, don't forget to commit)
286 $ hg debugsub
286 $ hg debugsub
287 path s
287 path s
288 source s
288 source s
289 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
289 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
290 path t
290 path t
291 source t
291 source t
292 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
292 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
293 $ echo conflict > t/t
293 $ echo conflict > t/t
294 $ hg ci -m10
294 $ hg ci -m10
295 committing subrepository t
295 committing subrepository t
296 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
296 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
297 searching for copies back to rev 2
297 searching for copies back to rev 2
298 resolving manifests
298 resolving manifests
299 branchmerge: True, force: False, partial: False
299 branchmerge: True, force: False, partial: False
300 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
300 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
301 starting 4 threads for background file closing (?)
301 starting 4 threads for background file closing (?)
302 .hgsubstate: versions differ -> m (premerge)
302 .hgsubstate: versions differ -> m (premerge)
303 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
303 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
304 subrepo t: both sides changed
304 subrepo t: both sides changed
305 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
305 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
306 starting 4 threads for background file closing (?)
306 starting 4 threads for background file closing (?)
307 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
307 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
308 merging subrepo t
308 merging subrepo t
309 searching for copies back to rev 2
309 searching for copies back to rev 2
310 resolving manifests
310 resolving manifests
311 branchmerge: True, force: False, partial: False
311 branchmerge: True, force: False, partial: False
312 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
312 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
313 preserving t for resolve of t
313 preserving t for resolve of t
314 t: versions differ -> m (premerge)
314 t: versions differ -> m (premerge)
315 picked tool ':merge' for t (binary False symlink False changedelete False)
315 picked tool ':merge' for t (binary False symlink False changedelete False)
316 merging t
316 merging t
317 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
317 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
318 t: versions differ -> m (merge)
318 t: versions differ -> m (merge)
319 picked tool ':merge' for t (binary False symlink False changedelete False)
319 picked tool ':merge' for t (binary False symlink False changedelete False)
320 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
320 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
321 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
321 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
322 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
322 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
323 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
323 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
324 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
324 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
325 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
325 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
326 (branch merge, don't forget to commit)
326 (branch merge, don't forget to commit)
327
327
328 should conflict
328 should conflict
329
329
330 $ cat t/t
330 $ cat t/t
331 <<<<<<< local: 20a0db6fbf6c - test: 10
331 <<<<<<< local: 20a0db6fbf6c - test: 10
332 conflict
332 conflict
333 =======
333 =======
334 t3
334 t3
335 >>>>>>> other: 7af322bc1198 - test: 7
335 >>>>>>> other: 7af322bc1198 - test: 7
336
336
337 11: remove subrepo t
337 11: remove subrepo t
338
338
339 $ hg co -C 5
339 $ hg co -C 5
340 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 $ hg revert -r 4 .hgsub # remove t
341 $ hg revert -r 4 .hgsub # remove t
342 $ hg ci -m11
342 $ hg ci -m11
343 created new head
343 created new head
344 $ hg debugsub
344 $ hg debugsub
345 path s
345 path s
346 source s
346 source s
347 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
347 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
348
348
349 local removed, remote changed, keep changed
349 local removed, remote changed, keep changed
350
350
351 $ hg merge 6
351 $ hg merge 6
352 remote [merge rev] changed subrepository s which local [working copy] removed
352 remote [merge rev] changed subrepository t which local [working copy] removed
353 use (c)hanged version or (d)elete? c
353 use (c)hanged version or (d)elete? c
354 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 (branch merge, don't forget to commit)
355 (branch merge, don't forget to commit)
356 BROKEN: should include subrepo t
356 BROKEN: should include subrepo t
357 $ hg debugsub
357 $ hg debugsub
358 path s
358 path s
359 source s
359 source s
360 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
360 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
361 $ cat .hgsubstate
361 $ cat .hgsubstate
362 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
362 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
363 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
363 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
364 $ hg ci -m 'local removed, remote changed, keep changed'
364 $ hg ci -m 'local removed, remote changed, keep changed'
365 BROKEN: should include subrepo t
365 BROKEN: should include subrepo t
366 $ hg debugsub
366 $ hg debugsub
367 path s
367 path s
368 source s
368 source s
369 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
369 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
370 BROKEN: should include subrepo t
370 BROKEN: should include subrepo t
371 $ cat .hgsubstate
371 $ cat .hgsubstate
372 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
372 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
373 $ cat t/t
373 $ cat t/t
374 t2
374 t2
375
375
376 local removed, remote changed, keep removed
376 local removed, remote changed, keep removed
377
377
378 $ hg co -C 11
378 $ hg co -C 11
379 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
380 $ hg merge --config ui.interactive=true 6 <<EOF
380 $ hg merge --config ui.interactive=true 6 <<EOF
381 > d
381 > d
382 > EOF
382 > EOF
383 remote [merge rev] changed subrepository s which local [working copy] removed
383 remote [merge rev] changed subrepository t which local [working copy] removed
384 use (c)hanged version or (d)elete? d
384 use (c)hanged version or (d)elete? d
385 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 (branch merge, don't forget to commit)
386 (branch merge, don't forget to commit)
387 $ hg debugsub
387 $ hg debugsub
388 path s
388 path s
389 source s
389 source s
390 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
390 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
391 $ cat .hgsubstate
391 $ cat .hgsubstate
392 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
392 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
393 $ hg ci -m 'local removed, remote changed, keep removed'
393 $ hg ci -m 'local removed, remote changed, keep removed'
394 created new head
394 created new head
395 $ hg debugsub
395 $ hg debugsub
396 path s
396 path s
397 source s
397 source s
398 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
398 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
399 $ cat .hgsubstate
399 $ cat .hgsubstate
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
400 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
401
401
402 local changed, remote removed, keep changed
402 local changed, remote removed, keep changed
403
403
404 $ hg co -C 6
404 $ hg co -C 6
405 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 $ hg merge 11
406 $ hg merge 11
407 local [working copy] changed subrepository s which remote [merge rev] removed
407 local [working copy] changed subrepository t which remote [merge rev] removed
408 use (c)hanged version or (d)elete? c
408 use (c)hanged version or (d)elete? c
409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 (branch merge, don't forget to commit)
410 (branch merge, don't forget to commit)
411 BROKEN: should include subrepo t
411 BROKEN: should include subrepo t
412 $ hg debugsub
412 $ hg debugsub
413 path s
413 path s
414 source s
414 source s
415 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
415 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
416 BROKEN: should include subrepo t
416 BROKEN: should include subrepo t
417 $ cat .hgsubstate
417 $ cat .hgsubstate
418 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
418 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
419 $ hg ci -m 'local changed, remote removed, keep changed'
419 $ hg ci -m 'local changed, remote removed, keep changed'
420 created new head
420 created new head
421 BROKEN: should include subrepo t
421 BROKEN: should include subrepo t
422 $ hg debugsub
422 $ hg debugsub
423 path s
423 path s
424 source s
424 source s
425 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
425 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
426 BROKEN: should include subrepo t
426 BROKEN: should include subrepo t
427 $ cat .hgsubstate
427 $ cat .hgsubstate
428 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
428 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
429 $ cat t/t
429 $ cat t/t
430 t2
430 t2
431
431
432 local changed, remote removed, keep removed
432 local changed, remote removed, keep removed
433
433
434 $ hg co -C 6
434 $ hg co -C 6
435 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ hg merge --config ui.interactive=true 11 <<EOF
436 $ hg merge --config ui.interactive=true 11 <<EOF
437 > d
437 > d
438 > EOF
438 > EOF
439 local [working copy] changed subrepository s which remote [merge rev] removed
439 local [working copy] changed subrepository t which remote [merge rev] removed
440 use (c)hanged version or (d)elete? d
440 use (c)hanged version or (d)elete? d
441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 (branch merge, don't forget to commit)
442 (branch merge, don't forget to commit)
443 $ hg debugsub
443 $ hg debugsub
444 path s
444 path s
445 source s
445 source s
446 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
446 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
447 $ cat .hgsubstate
447 $ cat .hgsubstate
448 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
448 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
449 $ hg ci -m 'local changed, remote removed, keep removed'
449 $ hg ci -m 'local changed, remote removed, keep removed'
450 created new head
450 created new head
451 $ hg debugsub
451 $ hg debugsub
452 path s
452 path s
453 source s
453 source s
454 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
454 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
455 $ cat .hgsubstate
455 $ cat .hgsubstate
456 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
456 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
457
457
458 clean up to avoid having to fix up the tests below
458 clean up to avoid having to fix up the tests below
459
459
460 $ hg co -C 10
460 $ hg co -C 10
461 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
461 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 $ cat >> $HGRCPATH <<EOF
462 $ cat >> $HGRCPATH <<EOF
463 > [extensions]
463 > [extensions]
464 > strip=
464 > strip=
465 > EOF
465 > EOF
466 $ hg strip -r 11:15
466 $ hg strip -r 11:15
467 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
467 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
468
468
469 clone
469 clone
470
470
471 $ cd ..
471 $ cd ..
472 $ hg clone t tc
472 $ hg clone t tc
473 updating to branch default
473 updating to branch default
474 cloning subrepo s from $TESTTMP/t/s
474 cloning subrepo s from $TESTTMP/t/s
475 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
475 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
476 cloning subrepo t from $TESTTMP/t/t
476 cloning subrepo t from $TESTTMP/t/t
477 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
477 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 $ cd tc
478 $ cd tc
479 $ hg debugsub
479 $ hg debugsub
480 path s
480 path s
481 source s
481 source s
482 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
482 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
483 path t
483 path t
484 source t
484 source t
485 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
485 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
486
486
487 push
487 push
488
488
489 $ echo bah > t/t
489 $ echo bah > t/t
490 $ hg ci -m11
490 $ hg ci -m11
491 committing subrepository t
491 committing subrepository t
492 $ hg push
492 $ hg push
493 pushing to $TESTTMP/t (glob)
493 pushing to $TESTTMP/t (glob)
494 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
494 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
495 no changes made to subrepo s since last push to $TESTTMP/t/s
495 no changes made to subrepo s since last push to $TESTTMP/t/s
496 pushing subrepo t to $TESTTMP/t/t
496 pushing subrepo t to $TESTTMP/t/t
497 searching for changes
497 searching for changes
498 adding changesets
498 adding changesets
499 adding manifests
499 adding manifests
500 adding file changes
500 adding file changes
501 added 1 changesets with 1 changes to 1 files
501 added 1 changesets with 1 changes to 1 files
502 searching for changes
502 searching for changes
503 adding changesets
503 adding changesets
504 adding manifests
504 adding manifests
505 adding file changes
505 adding file changes
506 added 1 changesets with 1 changes to 1 files
506 added 1 changesets with 1 changes to 1 files
507
507
508 push -f
508 push -f
509
509
510 $ echo bah > s/a
510 $ echo bah > s/a
511 $ hg ci -m12
511 $ hg ci -m12
512 committing subrepository s
512 committing subrepository s
513 $ hg push
513 $ hg push
514 pushing to $TESTTMP/t (glob)
514 pushing to $TESTTMP/t (glob)
515 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
515 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
516 pushing subrepo s to $TESTTMP/t/s
516 pushing subrepo s to $TESTTMP/t/s
517 searching for changes
517 searching for changes
518 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
518 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
519 (merge or see 'hg help push' for details about pushing new heads)
519 (merge or see 'hg help push' for details about pushing new heads)
520 [255]
520 [255]
521 $ hg push -f
521 $ hg push -f
522 pushing to $TESTTMP/t (glob)
522 pushing to $TESTTMP/t (glob)
523 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
523 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
524 searching for changes
524 searching for changes
525 no changes found
525 no changes found
526 pushing subrepo s to $TESTTMP/t/s
526 pushing subrepo s to $TESTTMP/t/s
527 searching for changes
527 searching for changes
528 adding changesets
528 adding changesets
529 adding manifests
529 adding manifests
530 adding file changes
530 adding file changes
531 added 1 changesets with 1 changes to 1 files (+1 heads)
531 added 1 changesets with 1 changes to 1 files (+1 heads)
532 pushing subrepo t to $TESTTMP/t/t
532 pushing subrepo t to $TESTTMP/t/t
533 searching for changes
533 searching for changes
534 no changes found
534 no changes found
535 searching for changes
535 searching for changes
536 adding changesets
536 adding changesets
537 adding manifests
537 adding manifests
538 adding file changes
538 adding file changes
539 added 1 changesets with 1 changes to 1 files
539 added 1 changesets with 1 changes to 1 files
540
540
541 check that unmodified subrepos are not pushed
541 check that unmodified subrepos are not pushed
542
542
543 $ hg clone . ../tcc
543 $ hg clone . ../tcc
544 updating to branch default
544 updating to branch default
545 cloning subrepo s from $TESTTMP/tc/s
545 cloning subrepo s from $TESTTMP/tc/s
546 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
546 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
547 cloning subrepo t from $TESTTMP/tc/t
547 cloning subrepo t from $TESTTMP/tc/t
548 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
548 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
549
549
550 the subrepos on the new clone have nothing to push to its source
550 the subrepos on the new clone have nothing to push to its source
551
551
552 $ hg push -R ../tcc .
552 $ hg push -R ../tcc .
553 pushing to .
553 pushing to .
554 no changes made to subrepo s/ss since last push to s/ss (glob)
554 no changes made to subrepo s/ss since last push to s/ss (glob)
555 no changes made to subrepo s since last push to s
555 no changes made to subrepo s since last push to s
556 no changes made to subrepo t since last push to t
556 no changes made to subrepo t since last push to t
557 searching for changes
557 searching for changes
558 no changes found
558 no changes found
559 [1]
559 [1]
560
560
561 the subrepos on the source do not have a clean store versus the clone target
561 the subrepos on the source do not have a clean store versus the clone target
562 because they were never explicitly pushed to the source
562 because they were never explicitly pushed to the source
563
563
564 $ hg push ../tcc
564 $ hg push ../tcc
565 pushing to ../tcc
565 pushing to ../tcc
566 pushing subrepo s/ss to ../tcc/s/ss (glob)
566 pushing subrepo s/ss to ../tcc/s/ss (glob)
567 searching for changes
567 searching for changes
568 no changes found
568 no changes found
569 pushing subrepo s to ../tcc/s
569 pushing subrepo s to ../tcc/s
570 searching for changes
570 searching for changes
571 no changes found
571 no changes found
572 pushing subrepo t to ../tcc/t
572 pushing subrepo t to ../tcc/t
573 searching for changes
573 searching for changes
574 no changes found
574 no changes found
575 searching for changes
575 searching for changes
576 no changes found
576 no changes found
577 [1]
577 [1]
578
578
579 after push their stores become clean
579 after push their stores become clean
580
580
581 $ hg push ../tcc
581 $ hg push ../tcc
582 pushing to ../tcc
582 pushing to ../tcc
583 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
583 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
584 no changes made to subrepo s since last push to ../tcc/s
584 no changes made to subrepo s since last push to ../tcc/s
585 no changes made to subrepo t since last push to ../tcc/t
585 no changes made to subrepo t since last push to ../tcc/t
586 searching for changes
586 searching for changes
587 no changes found
587 no changes found
588 [1]
588 [1]
589
589
590 updating a subrepo to a different revision or changing
590 updating a subrepo to a different revision or changing
591 its working directory does not make its store dirty
591 its working directory does not make its store dirty
592
592
593 $ hg -R s update '.^'
593 $ hg -R s update '.^'
594 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
594 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 $ hg push
595 $ hg push
596 pushing to $TESTTMP/t (glob)
596 pushing to $TESTTMP/t (glob)
597 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
597 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
598 no changes made to subrepo s since last push to $TESTTMP/t/s
598 no changes made to subrepo s since last push to $TESTTMP/t/s
599 no changes made to subrepo t since last push to $TESTTMP/t/t
599 no changes made to subrepo t since last push to $TESTTMP/t/t
600 searching for changes
600 searching for changes
601 no changes found
601 no changes found
602 [1]
602 [1]
603 $ echo foo >> s/a
603 $ echo foo >> s/a
604 $ hg push
604 $ hg push
605 pushing to $TESTTMP/t (glob)
605 pushing to $TESTTMP/t (glob)
606 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
606 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
607 no changes made to subrepo s since last push to $TESTTMP/t/s
607 no changes made to subrepo s since last push to $TESTTMP/t/s
608 no changes made to subrepo t since last push to $TESTTMP/t/t
608 no changes made to subrepo t since last push to $TESTTMP/t/t
609 searching for changes
609 searching for changes
610 no changes found
610 no changes found
611 [1]
611 [1]
612 $ hg -R s update -C tip
612 $ hg -R s update -C tip
613 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
613 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
614
614
615 committing into a subrepo makes its store (but not its parent's store) dirty
615 committing into a subrepo makes its store (but not its parent's store) dirty
616
616
617 $ echo foo >> s/ss/a
617 $ echo foo >> s/ss/a
618 $ hg -R s/ss commit -m 'test dirty store detection'
618 $ hg -R s/ss commit -m 'test dirty store detection'
619
619
620 $ hg out -S -r `hg log -r tip -T "{node|short}"`
620 $ hg out -S -r `hg log -r tip -T "{node|short}"`
621 comparing with $TESTTMP/t (glob)
621 comparing with $TESTTMP/t (glob)
622 searching for changes
622 searching for changes
623 no changes found
623 no changes found
624 comparing with $TESTTMP/t/s
624 comparing with $TESTTMP/t/s
625 searching for changes
625 searching for changes
626 no changes found
626 no changes found
627 comparing with $TESTTMP/t/s/ss
627 comparing with $TESTTMP/t/s/ss
628 searching for changes
628 searching for changes
629 changeset: 1:79ea5566a333
629 changeset: 1:79ea5566a333
630 tag: tip
630 tag: tip
631 user: test
631 user: test
632 date: Thu Jan 01 00:00:00 1970 +0000
632 date: Thu Jan 01 00:00:00 1970 +0000
633 summary: test dirty store detection
633 summary: test dirty store detection
634
634
635 comparing with $TESTTMP/t/t
635 comparing with $TESTTMP/t/t
636 searching for changes
636 searching for changes
637 no changes found
637 no changes found
638
638
639 $ hg push
639 $ hg push
640 pushing to $TESTTMP/t (glob)
640 pushing to $TESTTMP/t (glob)
641 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
641 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
642 searching for changes
642 searching for changes
643 adding changesets
643 adding changesets
644 adding manifests
644 adding manifests
645 adding file changes
645 adding file changes
646 added 1 changesets with 1 changes to 1 files
646 added 1 changesets with 1 changes to 1 files
647 no changes made to subrepo s since last push to $TESTTMP/t/s
647 no changes made to subrepo s since last push to $TESTTMP/t/s
648 no changes made to subrepo t since last push to $TESTTMP/t/t
648 no changes made to subrepo t since last push to $TESTTMP/t/t
649 searching for changes
649 searching for changes
650 no changes found
650 no changes found
651 [1]
651 [1]
652
652
653 a subrepo store may be clean versus one repo but not versus another
653 a subrepo store may be clean versus one repo but not versus another
654
654
655 $ hg push
655 $ hg push
656 pushing to $TESTTMP/t (glob)
656 pushing to $TESTTMP/t (glob)
657 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
657 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
658 no changes made to subrepo s since last push to $TESTTMP/t/s
658 no changes made to subrepo s since last push to $TESTTMP/t/s
659 no changes made to subrepo t since last push to $TESTTMP/t/t
659 no changes made to subrepo t since last push to $TESTTMP/t/t
660 searching for changes
660 searching for changes
661 no changes found
661 no changes found
662 [1]
662 [1]
663 $ hg push ../tcc
663 $ hg push ../tcc
664 pushing to ../tcc
664 pushing to ../tcc
665 pushing subrepo s/ss to ../tcc/s/ss (glob)
665 pushing subrepo s/ss to ../tcc/s/ss (glob)
666 searching for changes
666 searching for changes
667 adding changesets
667 adding changesets
668 adding manifests
668 adding manifests
669 adding file changes
669 adding file changes
670 added 1 changesets with 1 changes to 1 files
670 added 1 changesets with 1 changes to 1 files
671 no changes made to subrepo s since last push to ../tcc/s
671 no changes made to subrepo s since last push to ../tcc/s
672 no changes made to subrepo t since last push to ../tcc/t
672 no changes made to subrepo t since last push to ../tcc/t
673 searching for changes
673 searching for changes
674 no changes found
674 no changes found
675 [1]
675 [1]
676
676
677 update
677 update
678
678
679 $ cd ../t
679 $ cd ../t
680 $ hg up -C # discard our earlier merge
680 $ hg up -C # discard our earlier merge
681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
682 2 other heads for branch "default"
682 2 other heads for branch "default"
683 $ echo blah > t/t
683 $ echo blah > t/t
684 $ hg ci -m13
684 $ hg ci -m13
685 committing subrepository t
685 committing subrepository t
686
686
687 backout calls revert internally with minimal opts, which should not raise
687 backout calls revert internally with minimal opts, which should not raise
688 KeyError
688 KeyError
689
689
690 $ hg backout ".^" --no-commit
690 $ hg backout ".^" --no-commit
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
692 changeset c373c8102e68 backed out, don't forget to commit.
692 changeset c373c8102e68 backed out, don't forget to commit.
693
693
694 $ hg up -C # discard changes
694 $ hg up -C # discard changes
695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 2 other heads for branch "default"
696 2 other heads for branch "default"
697
697
698 pull
698 pull
699
699
700 $ cd ../tc
700 $ cd ../tc
701 $ hg pull
701 $ hg pull
702 pulling from $TESTTMP/t (glob)
702 pulling from $TESTTMP/t (glob)
703 searching for changes
703 searching for changes
704 adding changesets
704 adding changesets
705 adding manifests
705 adding manifests
706 adding file changes
706 adding file changes
707 added 1 changesets with 1 changes to 1 files
707 added 1 changesets with 1 changes to 1 files
708 (run 'hg update' to get a working copy)
708 (run 'hg update' to get a working copy)
709
709
710 should pull t
710 should pull t
711
711
712 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
712 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
713 comparing with $TESTTMP/t (glob)
713 comparing with $TESTTMP/t (glob)
714 no changes found
714 no changes found
715 comparing with $TESTTMP/t/s
715 comparing with $TESTTMP/t/s
716 searching for changes
716 searching for changes
717 no changes found
717 no changes found
718 comparing with $TESTTMP/t/s/ss
718 comparing with $TESTTMP/t/s/ss
719 searching for changes
719 searching for changes
720 no changes found
720 no changes found
721 comparing with $TESTTMP/t/t
721 comparing with $TESTTMP/t/t
722 searching for changes
722 searching for changes
723 changeset: 5:52c0adc0515a
723 changeset: 5:52c0adc0515a
724 tag: tip
724 tag: tip
725 user: test
725 user: test
726 date: Thu Jan 01 00:00:00 1970 +0000
726 date: Thu Jan 01 00:00:00 1970 +0000
727 summary: 13
727 summary: 13
728
728
729
729
730 $ hg up
730 $ hg up
731 pulling subrepo t from $TESTTMP/t/t
731 pulling subrepo t from $TESTTMP/t/t
732 searching for changes
732 searching for changes
733 adding changesets
733 adding changesets
734 adding manifests
734 adding manifests
735 adding file changes
735 adding file changes
736 added 1 changesets with 1 changes to 1 files
736 added 1 changesets with 1 changes to 1 files
737 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
737 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
738 2 other heads for branch "default"
738 2 other heads for branch "default"
739 $ cat t/t
739 $ cat t/t
740 blah
740 blah
741
741
742 bogus subrepo path aborts
742 bogus subrepo path aborts
743
743
744 $ echo 'bogus=[boguspath' >> .hgsub
744 $ echo 'bogus=[boguspath' >> .hgsub
745 $ hg ci -m 'bogus subrepo path'
745 $ hg ci -m 'bogus subrepo path'
746 abort: missing ] in subrepo source
746 abort: missing ] in subrepo source
747 [255]
747 [255]
748
748
749 Issue1986: merge aborts when trying to merge a subrepo that
749 Issue1986: merge aborts when trying to merge a subrepo that
750 shouldn't need merging
750 shouldn't need merging
751
751
752 # subrepo layout
752 # subrepo layout
753 #
753 #
754 # o 5 br
754 # o 5 br
755 # /|
755 # /|
756 # o | 4 default
756 # o | 4 default
757 # | |
757 # | |
758 # | o 3 br
758 # | o 3 br
759 # |/|
759 # |/|
760 # o | 2 default
760 # o | 2 default
761 # | |
761 # | |
762 # | o 1 br
762 # | o 1 br
763 # |/
763 # |/
764 # o 0 default
764 # o 0 default
765
765
766 $ cd ..
766 $ cd ..
767 $ rm -rf sub
767 $ rm -rf sub
768 $ hg init main
768 $ hg init main
769 $ cd main
769 $ cd main
770 $ hg init s
770 $ hg init s
771 $ cd s
771 $ cd s
772 $ echo a > a
772 $ echo a > a
773 $ hg ci -Am1
773 $ hg ci -Am1
774 adding a
774 adding a
775 $ hg branch br
775 $ hg branch br
776 marked working directory as branch br
776 marked working directory as branch br
777 (branches are permanent and global, did you want a bookmark?)
777 (branches are permanent and global, did you want a bookmark?)
778 $ echo a >> a
778 $ echo a >> a
779 $ hg ci -m1
779 $ hg ci -m1
780 $ hg up default
780 $ hg up default
781 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
781 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 $ echo b > b
782 $ echo b > b
783 $ hg ci -Am1
783 $ hg ci -Am1
784 adding b
784 adding b
785 $ hg up br
785 $ hg up br
786 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
786 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
787 $ hg merge tip
787 $ hg merge tip
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
789 (branch merge, don't forget to commit)
789 (branch merge, don't forget to commit)
790 $ hg ci -m1
790 $ hg ci -m1
791 $ hg up 2
791 $ hg up 2
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
793 $ echo c > c
793 $ echo c > c
794 $ hg ci -Am1
794 $ hg ci -Am1
795 adding c
795 adding c
796 $ hg up 3
796 $ hg up 3
797 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
797 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
798 $ hg merge 4
798 $ hg merge 4
799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
800 (branch merge, don't forget to commit)
800 (branch merge, don't forget to commit)
801 $ hg ci -m1
801 $ hg ci -m1
802
802
803 # main repo layout:
803 # main repo layout:
804 #
804 #
805 # * <-- try to merge default into br again
805 # * <-- try to merge default into br again
806 # .`|
806 # .`|
807 # . o 5 br --> substate = 5
807 # . o 5 br --> substate = 5
808 # . |
808 # . |
809 # o | 4 default --> substate = 4
809 # o | 4 default --> substate = 4
810 # | |
810 # | |
811 # | o 3 br --> substate = 2
811 # | o 3 br --> substate = 2
812 # |/|
812 # |/|
813 # o | 2 default --> substate = 2
813 # o | 2 default --> substate = 2
814 # | |
814 # | |
815 # | o 1 br --> substate = 3
815 # | o 1 br --> substate = 3
816 # |/
816 # |/
817 # o 0 default --> substate = 2
817 # o 0 default --> substate = 2
818
818
819 $ cd ..
819 $ cd ..
820 $ echo 's = s' > .hgsub
820 $ echo 's = s' > .hgsub
821 $ hg -R s up 2
821 $ hg -R s up 2
822 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
822 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
823 $ hg ci -Am1
823 $ hg ci -Am1
824 adding .hgsub
824 adding .hgsub
825 $ hg branch br
825 $ hg branch br
826 marked working directory as branch br
826 marked working directory as branch br
827 (branches are permanent and global, did you want a bookmark?)
827 (branches are permanent and global, did you want a bookmark?)
828 $ echo b > b
828 $ echo b > b
829 $ hg -R s up 3
829 $ hg -R s up 3
830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 $ hg ci -Am1
831 $ hg ci -Am1
832 adding b
832 adding b
833 $ hg up default
833 $ hg up default
834 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
834 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
835 $ echo c > c
835 $ echo c > c
836 $ hg ci -Am1
836 $ hg ci -Am1
837 adding c
837 adding c
838 $ hg up 1
838 $ hg up 1
839 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
839 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
840 $ hg merge 2
840 $ hg merge 2
841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
842 (branch merge, don't forget to commit)
842 (branch merge, don't forget to commit)
843 $ hg ci -m1
843 $ hg ci -m1
844 $ hg up 2
844 $ hg up 2
845 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
845 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
846 $ hg -R s up 4
846 $ hg -R s up 4
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 $ echo d > d
848 $ echo d > d
849 $ hg ci -Am1
849 $ hg ci -Am1
850 adding d
850 adding d
851 $ hg up 3
851 $ hg up 3
852 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
852 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
853 $ hg -R s up 5
853 $ hg -R s up 5
854 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
854 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
855 $ echo e > e
855 $ echo e > e
856 $ hg ci -Am1
856 $ hg ci -Am1
857 adding e
857 adding e
858
858
859 $ hg up 5
859 $ hg up 5
860 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
860 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
861 $ hg merge 4 # try to merge default into br again
861 $ hg merge 4 # try to merge default into br again
862 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
862 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
863 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
863 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
865 (branch merge, don't forget to commit)
865 (branch merge, don't forget to commit)
866 $ cd ..
866 $ cd ..
867
867
868 test subrepo delete from .hgsubstate
868 test subrepo delete from .hgsubstate
869
869
870 $ hg init testdelete
870 $ hg init testdelete
871 $ mkdir testdelete/nested testdelete/nested2
871 $ mkdir testdelete/nested testdelete/nested2
872 $ hg init testdelete/nested
872 $ hg init testdelete/nested
873 $ hg init testdelete/nested2
873 $ hg init testdelete/nested2
874 $ echo test > testdelete/nested/foo
874 $ echo test > testdelete/nested/foo
875 $ echo test > testdelete/nested2/foo
875 $ echo test > testdelete/nested2/foo
876 $ hg -R testdelete/nested add
876 $ hg -R testdelete/nested add
877 adding testdelete/nested/foo (glob)
877 adding testdelete/nested/foo (glob)
878 $ hg -R testdelete/nested2 add
878 $ hg -R testdelete/nested2 add
879 adding testdelete/nested2/foo (glob)
879 adding testdelete/nested2/foo (glob)
880 $ hg -R testdelete/nested ci -m test
880 $ hg -R testdelete/nested ci -m test
881 $ hg -R testdelete/nested2 ci -m test
881 $ hg -R testdelete/nested2 ci -m test
882 $ echo nested = nested > testdelete/.hgsub
882 $ echo nested = nested > testdelete/.hgsub
883 $ echo nested2 = nested2 >> testdelete/.hgsub
883 $ echo nested2 = nested2 >> testdelete/.hgsub
884 $ hg -R testdelete add
884 $ hg -R testdelete add
885 adding testdelete/.hgsub (glob)
885 adding testdelete/.hgsub (glob)
886 $ hg -R testdelete ci -m "nested 1 & 2 added"
886 $ hg -R testdelete ci -m "nested 1 & 2 added"
887 $ echo nested = nested > testdelete/.hgsub
887 $ echo nested = nested > testdelete/.hgsub
888 $ hg -R testdelete ci -m "nested 2 deleted"
888 $ hg -R testdelete ci -m "nested 2 deleted"
889 $ cat testdelete/.hgsubstate
889 $ cat testdelete/.hgsubstate
890 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
890 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
891 $ hg -R testdelete remove testdelete/.hgsub
891 $ hg -R testdelete remove testdelete/.hgsub
892 $ hg -R testdelete ci -m ".hgsub deleted"
892 $ hg -R testdelete ci -m ".hgsub deleted"
893 $ cat testdelete/.hgsubstate
893 $ cat testdelete/.hgsubstate
894 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
894 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
895
895
896 test repository cloning
896 test repository cloning
897
897
898 $ mkdir mercurial mercurial2
898 $ mkdir mercurial mercurial2
899 $ hg init nested_absolute
899 $ hg init nested_absolute
900 $ echo test > nested_absolute/foo
900 $ echo test > nested_absolute/foo
901 $ hg -R nested_absolute add
901 $ hg -R nested_absolute add
902 adding nested_absolute/foo (glob)
902 adding nested_absolute/foo (glob)
903 $ hg -R nested_absolute ci -mtest
903 $ hg -R nested_absolute ci -mtest
904 $ cd mercurial
904 $ cd mercurial
905 $ hg init nested_relative
905 $ hg init nested_relative
906 $ echo test2 > nested_relative/foo2
906 $ echo test2 > nested_relative/foo2
907 $ hg -R nested_relative add
907 $ hg -R nested_relative add
908 adding nested_relative/foo2 (glob)
908 adding nested_relative/foo2 (glob)
909 $ hg -R nested_relative ci -mtest2
909 $ hg -R nested_relative ci -mtest2
910 $ hg init main
910 $ hg init main
911 $ echo "nested_relative = ../nested_relative" > main/.hgsub
911 $ echo "nested_relative = ../nested_relative" > main/.hgsub
912 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
912 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
913 $ hg -R main add
913 $ hg -R main add
914 adding main/.hgsub (glob)
914 adding main/.hgsub (glob)
915 $ hg -R main ci -m "add subrepos"
915 $ hg -R main ci -m "add subrepos"
916 $ cd ..
916 $ cd ..
917 $ hg clone mercurial/main mercurial2/main
917 $ hg clone mercurial/main mercurial2/main
918 updating to branch default
918 updating to branch default
919 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
919 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
920 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
921 > mercurial2/main/nested_relative/.hg/hgrc
921 > mercurial2/main/nested_relative/.hg/hgrc
922 [paths]
922 [paths]
923 default = $TESTTMP/mercurial/nested_absolute
923 default = $TESTTMP/mercurial/nested_absolute
924 [paths]
924 [paths]
925 default = $TESTTMP/mercurial/nested_relative
925 default = $TESTTMP/mercurial/nested_relative
926 $ rm -rf mercurial mercurial2
926 $ rm -rf mercurial mercurial2
927
927
928 Issue1977: multirepo push should fail if subrepo push fails
928 Issue1977: multirepo push should fail if subrepo push fails
929
929
930 $ hg init repo
930 $ hg init repo
931 $ hg init repo/s
931 $ hg init repo/s
932 $ echo a > repo/s/a
932 $ echo a > repo/s/a
933 $ hg -R repo/s ci -Am0
933 $ hg -R repo/s ci -Am0
934 adding a
934 adding a
935 $ echo s = s > repo/.hgsub
935 $ echo s = s > repo/.hgsub
936 $ hg -R repo ci -Am1
936 $ hg -R repo ci -Am1
937 adding .hgsub
937 adding .hgsub
938 $ hg clone repo repo2
938 $ hg clone repo repo2
939 updating to branch default
939 updating to branch default
940 cloning subrepo s from $TESTTMP/repo/s
940 cloning subrepo s from $TESTTMP/repo/s
941 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
941 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
942 $ hg -q -R repo2 pull -u
942 $ hg -q -R repo2 pull -u
943 $ echo 1 > repo2/s/a
943 $ echo 1 > repo2/s/a
944 $ hg -R repo2/s ci -m2
944 $ hg -R repo2/s ci -m2
945 $ hg -q -R repo2/s push
945 $ hg -q -R repo2/s push
946 $ hg -R repo2/s up -C 0
946 $ hg -R repo2/s up -C 0
947 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
947 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
948 $ echo 2 > repo2/s/b
948 $ echo 2 > repo2/s/b
949 $ hg -R repo2/s ci -m3 -A
949 $ hg -R repo2/s ci -m3 -A
950 adding b
950 adding b
951 created new head
951 created new head
952 $ hg -R repo2 ci -m3
952 $ hg -R repo2 ci -m3
953 $ hg -q -R repo2 push
953 $ hg -q -R repo2 push
954 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
954 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
955 (merge or see 'hg help push' for details about pushing new heads)
955 (merge or see 'hg help push' for details about pushing new heads)
956 [255]
956 [255]
957 $ hg -R repo update
957 $ hg -R repo update
958 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
958 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
959
959
960 test if untracked file is not overwritten
960 test if untracked file is not overwritten
961
961
962 (this also tests that updated .hgsubstate is treated as "modified",
962 (this also tests that updated .hgsubstate is treated as "modified",
963 when 'merge.update()' is aborted before 'merge.recordupdates()', even
963 when 'merge.update()' is aborted before 'merge.recordupdates()', even
964 if none of mode, size and timestamp of it isn't changed on the
964 if none of mode, size and timestamp of it isn't changed on the
965 filesystem (see also issue4583))
965 filesystem (see also issue4583))
966
966
967 $ echo issue3276_ok > repo/s/b
967 $ echo issue3276_ok > repo/s/b
968 $ hg -R repo2 push -f -q
968 $ hg -R repo2 push -f -q
969 $ touch -t 200001010000 repo/.hgsubstate
969 $ touch -t 200001010000 repo/.hgsubstate
970
970
971 $ cat >> repo/.hg/hgrc <<EOF
971 $ cat >> repo/.hg/hgrc <<EOF
972 > [fakedirstatewritetime]
972 > [fakedirstatewritetime]
973 > # emulate invoking dirstate.write() via repo.status()
973 > # emulate invoking dirstate.write() via repo.status()
974 > # at 2000-01-01 00:00
974 > # at 2000-01-01 00:00
975 > fakenow = 200001010000
975 > fakenow = 200001010000
976 >
976 >
977 > [extensions]
977 > [extensions]
978 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
978 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
979 > EOF
979 > EOF
980 $ hg -R repo update
980 $ hg -R repo update
981 b: untracked file differs
981 b: untracked file differs
982 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
982 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
983 [255]
983 [255]
984 $ cat >> repo/.hg/hgrc <<EOF
984 $ cat >> repo/.hg/hgrc <<EOF
985 > [extensions]
985 > [extensions]
986 > fakedirstatewritetime = !
986 > fakedirstatewritetime = !
987 > EOF
987 > EOF
988
988
989 $ cat repo/s/b
989 $ cat repo/s/b
990 issue3276_ok
990 issue3276_ok
991 $ rm repo/s/b
991 $ rm repo/s/b
992 $ touch -t 200001010000 repo/.hgsubstate
992 $ touch -t 200001010000 repo/.hgsubstate
993 $ hg -R repo revert --all
993 $ hg -R repo revert --all
994 reverting repo/.hgsubstate (glob)
994 reverting repo/.hgsubstate (glob)
995 reverting subrepo s
995 reverting subrepo s
996 $ hg -R repo update
996 $ hg -R repo update
997 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
997 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
998 $ cat repo/s/b
998 $ cat repo/s/b
999 2
999 2
1000 $ rm -rf repo2 repo
1000 $ rm -rf repo2 repo
1001
1001
1002
1002
1003 Issue1852 subrepos with relative paths always push/pull relative to default
1003 Issue1852 subrepos with relative paths always push/pull relative to default
1004
1004
1005 Prepare a repo with subrepo
1005 Prepare a repo with subrepo
1006
1006
1007 $ hg init issue1852a
1007 $ hg init issue1852a
1008 $ cd issue1852a
1008 $ cd issue1852a
1009 $ hg init sub/repo
1009 $ hg init sub/repo
1010 $ echo test > sub/repo/foo
1010 $ echo test > sub/repo/foo
1011 $ hg -R sub/repo add sub/repo/foo
1011 $ hg -R sub/repo add sub/repo/foo
1012 $ echo sub/repo = sub/repo > .hgsub
1012 $ echo sub/repo = sub/repo > .hgsub
1013 $ hg add .hgsub
1013 $ hg add .hgsub
1014 $ hg ci -mtest
1014 $ hg ci -mtest
1015 committing subrepository sub/repo (glob)
1015 committing subrepository sub/repo (glob)
1016 $ echo test >> sub/repo/foo
1016 $ echo test >> sub/repo/foo
1017 $ hg ci -mtest
1017 $ hg ci -mtest
1018 committing subrepository sub/repo (glob)
1018 committing subrepository sub/repo (glob)
1019 $ hg cat sub/repo/foo
1019 $ hg cat sub/repo/foo
1020 test
1020 test
1021 test
1021 test
1022 $ mkdir -p tmp/sub/repo
1022 $ mkdir -p tmp/sub/repo
1023 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1023 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1024 $ cat tmp/sub/repo/foo_p
1024 $ cat tmp/sub/repo/foo_p
1025 test
1025 test
1026 $ mv sub/repo sub_
1026 $ mv sub/repo sub_
1027 $ hg cat sub/repo/baz
1027 $ hg cat sub/repo/baz
1028 skipping missing subrepository: sub/repo
1028 skipping missing subrepository: sub/repo
1029 [1]
1029 [1]
1030 $ rm -rf sub/repo
1030 $ rm -rf sub/repo
1031 $ mv sub_ sub/repo
1031 $ mv sub_ sub/repo
1032 $ cd ..
1032 $ cd ..
1033
1033
1034 Create repo without default path, pull top repo, and see what happens on update
1034 Create repo without default path, pull top repo, and see what happens on update
1035
1035
1036 $ hg init issue1852b
1036 $ hg init issue1852b
1037 $ hg -R issue1852b pull issue1852a
1037 $ hg -R issue1852b pull issue1852a
1038 pulling from issue1852a
1038 pulling from issue1852a
1039 requesting all changes
1039 requesting all changes
1040 adding changesets
1040 adding changesets
1041 adding manifests
1041 adding manifests
1042 adding file changes
1042 adding file changes
1043 added 2 changesets with 3 changes to 2 files
1043 added 2 changesets with 3 changes to 2 files
1044 (run 'hg update' to get a working copy)
1044 (run 'hg update' to get a working copy)
1045 $ hg -R issue1852b update
1045 $ hg -R issue1852b update
1046 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
1046 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
1047 [255]
1047 [255]
1048
1048
1049 Ensure a full traceback, not just the SubrepoAbort part
1049 Ensure a full traceback, not just the SubrepoAbort part
1050
1050
1051 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1051 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1052 raise error.Abort(_("default path for subrepository not found"))
1052 raise error.Abort(_("default path for subrepository not found"))
1053
1053
1054 Pull -u now doesn't help
1054 Pull -u now doesn't help
1055
1055
1056 $ hg -R issue1852b pull -u issue1852a
1056 $ hg -R issue1852b pull -u issue1852a
1057 pulling from issue1852a
1057 pulling from issue1852a
1058 searching for changes
1058 searching for changes
1059 no changes found
1059 no changes found
1060
1060
1061 Try the same, but with pull -u
1061 Try the same, but with pull -u
1062
1062
1063 $ hg init issue1852c
1063 $ hg init issue1852c
1064 $ hg -R issue1852c pull -r0 -u issue1852a
1064 $ hg -R issue1852c pull -r0 -u issue1852a
1065 pulling from issue1852a
1065 pulling from issue1852a
1066 adding changesets
1066 adding changesets
1067 adding manifests
1067 adding manifests
1068 adding file changes
1068 adding file changes
1069 added 1 changesets with 2 changes to 2 files
1069 added 1 changesets with 2 changes to 2 files
1070 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
1070 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
1071 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1071 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1072
1072
1073 Try to push from the other side
1073 Try to push from the other side
1074
1074
1075 $ hg -R issue1852a push `pwd`/issue1852c
1075 $ hg -R issue1852a push `pwd`/issue1852c
1076 pushing to $TESTTMP/issue1852c (glob)
1076 pushing to $TESTTMP/issue1852c (glob)
1077 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1077 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1078 searching for changes
1078 searching for changes
1079 no changes found
1079 no changes found
1080 searching for changes
1080 searching for changes
1081 adding changesets
1081 adding changesets
1082 adding manifests
1082 adding manifests
1083 adding file changes
1083 adding file changes
1084 added 1 changesets with 1 changes to 1 files
1084 added 1 changesets with 1 changes to 1 files
1085
1085
1086 Incoming and outgoing should not use the default path:
1086 Incoming and outgoing should not use the default path:
1087
1087
1088 $ hg clone -q issue1852a issue1852d
1088 $ hg clone -q issue1852a issue1852d
1089 $ hg -R issue1852d outgoing --subrepos issue1852c
1089 $ hg -R issue1852d outgoing --subrepos issue1852c
1090 comparing with issue1852c
1090 comparing with issue1852c
1091 searching for changes
1091 searching for changes
1092 no changes found
1092 no changes found
1093 comparing with issue1852c/sub/repo
1093 comparing with issue1852c/sub/repo
1094 searching for changes
1094 searching for changes
1095 no changes found
1095 no changes found
1096 [1]
1096 [1]
1097 $ hg -R issue1852d incoming --subrepos issue1852c
1097 $ hg -R issue1852d incoming --subrepos issue1852c
1098 comparing with issue1852c
1098 comparing with issue1852c
1099 searching for changes
1099 searching for changes
1100 no changes found
1100 no changes found
1101 comparing with issue1852c/sub/repo
1101 comparing with issue1852c/sub/repo
1102 searching for changes
1102 searching for changes
1103 no changes found
1103 no changes found
1104 [1]
1104 [1]
1105
1105
1106 Check that merge of a new subrepo doesn't write the uncommitted state to
1106 Check that merge of a new subrepo doesn't write the uncommitted state to
1107 .hgsubstate (issue4622)
1107 .hgsubstate (issue4622)
1108
1108
1109 $ hg init issue1852a/addedsub
1109 $ hg init issue1852a/addedsub
1110 $ echo zzz > issue1852a/addedsub/zz.txt
1110 $ echo zzz > issue1852a/addedsub/zz.txt
1111 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1111 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1112
1112
1113 $ hg clone issue1852a/addedsub issue1852d/addedsub
1113 $ hg clone issue1852a/addedsub issue1852d/addedsub
1114 updating to branch default
1114 updating to branch default
1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1116
1116
1117 $ echo def > issue1852a/sub/repo/foo
1117 $ echo def > issue1852a/sub/repo/foo
1118 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1118 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1119 adding tmp/sub/repo/foo_p
1119 adding tmp/sub/repo/foo_p
1120 committing subrepository sub/repo (glob)
1120 committing subrepository sub/repo (glob)
1121
1121
1122 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1122 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1123 $ echo xyz > issue1852d/sub/repo/foo
1123 $ echo xyz > issue1852d/sub/repo/foo
1124 $ hg -R issue1852d pull -u
1124 $ hg -R issue1852d pull -u
1125 pulling from $TESTTMP/issue1852a (glob)
1125 pulling from $TESTTMP/issue1852a (glob)
1126 searching for changes
1126 searching for changes
1127 adding changesets
1127 adding changesets
1128 adding manifests
1128 adding manifests
1129 adding file changes
1129 adding file changes
1130 added 1 changesets with 2 changes to 2 files
1130 added 1 changesets with 2 changes to 2 files
1131 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1131 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1132 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1132 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1133 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob)
1133 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob)
1134 searching for changes
1134 searching for changes
1135 adding changesets
1135 adding changesets
1136 adding manifests
1136 adding manifests
1137 adding file changes
1137 adding file changes
1138 added 1 changesets with 1 changes to 1 files
1138 added 1 changesets with 1 changes to 1 files
1139 subrepository sources for sub/repo differ (glob)
1139 subrepository sources for sub/repo differ (glob)
1140 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1140 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1142 $ cat issue1852d/.hgsubstate
1142 $ cat issue1852d/.hgsubstate
1143 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1143 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1144
1144
1145 Check status of files when none of them belong to the first
1145 Check status of files when none of them belong to the first
1146 subrepository:
1146 subrepository:
1147
1147
1148 $ hg init subrepo-status
1148 $ hg init subrepo-status
1149 $ cd subrepo-status
1149 $ cd subrepo-status
1150 $ hg init subrepo-1
1150 $ hg init subrepo-1
1151 $ hg init subrepo-2
1151 $ hg init subrepo-2
1152 $ cd subrepo-2
1152 $ cd subrepo-2
1153 $ touch file
1153 $ touch file
1154 $ hg add file
1154 $ hg add file
1155 $ cd ..
1155 $ cd ..
1156 $ echo subrepo-1 = subrepo-1 > .hgsub
1156 $ echo subrepo-1 = subrepo-1 > .hgsub
1157 $ echo subrepo-2 = subrepo-2 >> .hgsub
1157 $ echo subrepo-2 = subrepo-2 >> .hgsub
1158 $ hg add .hgsub
1158 $ hg add .hgsub
1159 $ hg ci -m 'Added subrepos'
1159 $ hg ci -m 'Added subrepos'
1160 committing subrepository subrepo-2
1160 committing subrepository subrepo-2
1161 $ hg st subrepo-2/file
1161 $ hg st subrepo-2/file
1162
1162
1163 Check that share works with subrepo
1163 Check that share works with subrepo
1164 $ hg --config extensions.share= share . ../shared
1164 $ hg --config extensions.share= share . ../shared
1165 updating working directory
1165 updating working directory
1166 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1166 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1167 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1167 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1168 $ test -f ../shared/subrepo-1/.hg/sharedpath
1168 $ test -f ../shared/subrepo-1/.hg/sharedpath
1169 [1]
1169 [1]
1170 $ hg -R ../shared in
1170 $ hg -R ../shared in
1171 abort: repository default not found!
1171 abort: repository default not found!
1172 [255]
1172 [255]
1173 $ hg -R ../shared/subrepo-2 showconfig paths
1173 $ hg -R ../shared/subrepo-2 showconfig paths
1174 paths.default=$TESTTMP/subrepo-status/subrepo-2
1174 paths.default=$TESTTMP/subrepo-status/subrepo-2
1175 $ hg -R ../shared/subrepo-1 sum --remote
1175 $ hg -R ../shared/subrepo-1 sum --remote
1176 parent: -1:000000000000 tip (empty repository)
1176 parent: -1:000000000000 tip (empty repository)
1177 branch: default
1177 branch: default
1178 commit: (clean)
1178 commit: (clean)
1179 update: (current)
1179 update: (current)
1180 remote: (synced)
1180 remote: (synced)
1181
1181
1182 Check hg update --clean
1182 Check hg update --clean
1183 $ cd $TESTTMP/t
1183 $ cd $TESTTMP/t
1184 $ rm -r t/t.orig
1184 $ rm -r t/t.orig
1185 $ hg status -S --all
1185 $ hg status -S --all
1186 C .hgsub
1186 C .hgsub
1187 C .hgsubstate
1187 C .hgsubstate
1188 C a
1188 C a
1189 C s/.hgsub
1189 C s/.hgsub
1190 C s/.hgsubstate
1190 C s/.hgsubstate
1191 C s/a
1191 C s/a
1192 C s/ss/a
1192 C s/ss/a
1193 C t/t
1193 C t/t
1194 $ echo c1 > s/a
1194 $ echo c1 > s/a
1195 $ cd s
1195 $ cd s
1196 $ echo c1 > b
1196 $ echo c1 > b
1197 $ echo c1 > c
1197 $ echo c1 > c
1198 $ hg add b
1198 $ hg add b
1199 $ cd ..
1199 $ cd ..
1200 $ hg status -S
1200 $ hg status -S
1201 M s/a
1201 M s/a
1202 A s/b
1202 A s/b
1203 ? s/c
1203 ? s/c
1204 $ hg update -C
1204 $ hg update -C
1205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1206 2 other heads for branch "default"
1206 2 other heads for branch "default"
1207 $ hg status -S
1207 $ hg status -S
1208 ? s/b
1208 ? s/b
1209 ? s/c
1209 ? s/c
1210
1210
1211 Sticky subrepositories, no changes
1211 Sticky subrepositories, no changes
1212 $ cd $TESTTMP/t
1212 $ cd $TESTTMP/t
1213 $ hg id
1213 $ hg id
1214 925c17564ef8 tip
1214 925c17564ef8 tip
1215 $ hg -R s id
1215 $ hg -R s id
1216 12a213df6fa9 tip
1216 12a213df6fa9 tip
1217 $ hg -R t id
1217 $ hg -R t id
1218 52c0adc0515a tip
1218 52c0adc0515a tip
1219 $ hg update 11
1219 $ hg update 11
1220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1221 $ hg id
1221 $ hg id
1222 365661e5936a
1222 365661e5936a
1223 $ hg -R s id
1223 $ hg -R s id
1224 fc627a69481f
1224 fc627a69481f
1225 $ hg -R t id
1225 $ hg -R t id
1226 e95bcfa18a35
1226 e95bcfa18a35
1227
1227
1228 Sticky subrepositories, file changes
1228 Sticky subrepositories, file changes
1229 $ touch s/f1
1229 $ touch s/f1
1230 $ touch t/f1
1230 $ touch t/f1
1231 $ hg add -S s/f1
1231 $ hg add -S s/f1
1232 $ hg add -S t/f1
1232 $ hg add -S t/f1
1233 $ hg id
1233 $ hg id
1234 365661e5936a+
1234 365661e5936a+
1235 $ hg -R s id
1235 $ hg -R s id
1236 fc627a69481f+
1236 fc627a69481f+
1237 $ hg -R t id
1237 $ hg -R t id
1238 e95bcfa18a35+
1238 e95bcfa18a35+
1239 $ hg update tip
1239 $ hg update tip
1240 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1240 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1241 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1241 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1242 subrepository sources for s differ
1242 subrepository sources for s differ
1243 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1243 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1244 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1244 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1245 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1245 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1246 subrepository sources for t differ
1246 subrepository sources for t differ
1247 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1247 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1248 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1248 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1249 $ hg id
1249 $ hg id
1250 925c17564ef8+ tip
1250 925c17564ef8+ tip
1251 $ hg -R s id
1251 $ hg -R s id
1252 fc627a69481f+
1252 fc627a69481f+
1253 $ hg -R t id
1253 $ hg -R t id
1254 e95bcfa18a35+
1254 e95bcfa18a35+
1255 $ hg update --clean tip
1255 $ hg update --clean tip
1256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1257
1257
1258 Sticky subrepository, revision updates
1258 Sticky subrepository, revision updates
1259 $ hg id
1259 $ hg id
1260 925c17564ef8 tip
1260 925c17564ef8 tip
1261 $ hg -R s id
1261 $ hg -R s id
1262 12a213df6fa9 tip
1262 12a213df6fa9 tip
1263 $ hg -R t id
1263 $ hg -R t id
1264 52c0adc0515a tip
1264 52c0adc0515a tip
1265 $ cd s
1265 $ cd s
1266 $ hg update -r -2
1266 $ hg update -r -2
1267 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1267 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1268 $ cd ../t
1268 $ cd ../t
1269 $ hg update -r 2
1269 $ hg update -r 2
1270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1271 $ cd ..
1271 $ cd ..
1272 $ hg update 10
1272 $ hg update 10
1273 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1273 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1274 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1274 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1275 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1275 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1276 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1276 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1277 subrepository sources for t differ (in checked out version)
1277 subrepository sources for t differ (in checked out version)
1278 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1278 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1279 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1279 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1280 $ hg id
1280 $ hg id
1281 e45c8b14af55+
1281 e45c8b14af55+
1282 $ hg -R s id
1282 $ hg -R s id
1283 02dcf1d70411
1283 02dcf1d70411
1284 $ hg -R t id
1284 $ hg -R t id
1285 7af322bc1198
1285 7af322bc1198
1286
1286
1287 Sticky subrepository, file changes and revision updates
1287 Sticky subrepository, file changes and revision updates
1288 $ touch s/f1
1288 $ touch s/f1
1289 $ touch t/f1
1289 $ touch t/f1
1290 $ hg add -S s/f1
1290 $ hg add -S s/f1
1291 $ hg add -S t/f1
1291 $ hg add -S t/f1
1292 $ hg id
1292 $ hg id
1293 e45c8b14af55+
1293 e45c8b14af55+
1294 $ hg -R s id
1294 $ hg -R s id
1295 02dcf1d70411+
1295 02dcf1d70411+
1296 $ hg -R t id
1296 $ hg -R t id
1297 7af322bc1198+
1297 7af322bc1198+
1298 $ hg update tip
1298 $ hg update tip
1299 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1299 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1300 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1300 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1301 subrepository sources for s differ
1301 subrepository sources for s differ
1302 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1302 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1303 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1303 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1304 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1304 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1305 subrepository sources for t differ
1305 subrepository sources for t differ
1306 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1306 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1307 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1308 $ hg id
1308 $ hg id
1309 925c17564ef8+ tip
1309 925c17564ef8+ tip
1310 $ hg -R s id
1310 $ hg -R s id
1311 02dcf1d70411+
1311 02dcf1d70411+
1312 $ hg -R t id
1312 $ hg -R t id
1313 7af322bc1198+
1313 7af322bc1198+
1314
1314
1315 Sticky repository, update --clean
1315 Sticky repository, update --clean
1316 $ hg update --clean tip
1316 $ hg update --clean tip
1317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1318 $ hg id
1318 $ hg id
1319 925c17564ef8 tip
1319 925c17564ef8 tip
1320 $ hg -R s id
1320 $ hg -R s id
1321 12a213df6fa9 tip
1321 12a213df6fa9 tip
1322 $ hg -R t id
1322 $ hg -R t id
1323 52c0adc0515a tip
1323 52c0adc0515a tip
1324
1324
1325 Test subrepo already at intended revision:
1325 Test subrepo already at intended revision:
1326 $ cd s
1326 $ cd s
1327 $ hg update fc627a69481f
1327 $ hg update fc627a69481f
1328 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1328 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1329 $ cd ..
1329 $ cd ..
1330 $ hg update 11
1330 $ hg update 11
1331 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1331 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1332 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1332 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1333 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1333 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1335 $ hg id -n
1335 $ hg id -n
1336 11+
1336 11+
1337 $ hg -R s id
1337 $ hg -R s id
1338 fc627a69481f
1338 fc627a69481f
1339 $ hg -R t id
1339 $ hg -R t id
1340 e95bcfa18a35
1340 e95bcfa18a35
1341
1341
1342 Test that removing .hgsubstate doesn't break anything:
1342 Test that removing .hgsubstate doesn't break anything:
1343
1343
1344 $ hg rm -f .hgsubstate
1344 $ hg rm -f .hgsubstate
1345 $ hg ci -mrm
1345 $ hg ci -mrm
1346 nothing changed
1346 nothing changed
1347 [1]
1347 [1]
1348 $ hg log -vr tip
1348 $ hg log -vr tip
1349 changeset: 13:925c17564ef8
1349 changeset: 13:925c17564ef8
1350 tag: tip
1350 tag: tip
1351 user: test
1351 user: test
1352 date: Thu Jan 01 00:00:00 1970 +0000
1352 date: Thu Jan 01 00:00:00 1970 +0000
1353 files: .hgsubstate
1353 files: .hgsubstate
1354 description:
1354 description:
1355 13
1355 13
1356
1356
1357
1357
1358
1358
1359 Test that removing .hgsub removes .hgsubstate:
1359 Test that removing .hgsub removes .hgsubstate:
1360
1360
1361 $ hg rm .hgsub
1361 $ hg rm .hgsub
1362 $ hg ci -mrm2
1362 $ hg ci -mrm2
1363 created new head
1363 created new head
1364 $ hg log -vr tip
1364 $ hg log -vr tip
1365 changeset: 14:2400bccd50af
1365 changeset: 14:2400bccd50af
1366 tag: tip
1366 tag: tip
1367 parent: 11:365661e5936a
1367 parent: 11:365661e5936a
1368 user: test
1368 user: test
1369 date: Thu Jan 01 00:00:00 1970 +0000
1369 date: Thu Jan 01 00:00:00 1970 +0000
1370 files: .hgsub .hgsubstate
1370 files: .hgsub .hgsubstate
1371 description:
1371 description:
1372 rm2
1372 rm2
1373
1373
1374
1374
1375 Test issue3153: diff -S with deleted subrepos
1375 Test issue3153: diff -S with deleted subrepos
1376
1376
1377 $ hg diff --nodates -S -c .
1377 $ hg diff --nodates -S -c .
1378 diff -r 365661e5936a -r 2400bccd50af .hgsub
1378 diff -r 365661e5936a -r 2400bccd50af .hgsub
1379 --- a/.hgsub
1379 --- a/.hgsub
1380 +++ /dev/null
1380 +++ /dev/null
1381 @@ -1,2 +0,0 @@
1381 @@ -1,2 +0,0 @@
1382 -s = s
1382 -s = s
1383 -t = t
1383 -t = t
1384 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1384 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1385 --- a/.hgsubstate
1385 --- a/.hgsubstate
1386 +++ /dev/null
1386 +++ /dev/null
1387 @@ -1,2 +0,0 @@
1387 @@ -1,2 +0,0 @@
1388 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1388 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1389 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1389 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1390
1390
1391 Test behavior of add for explicit path in subrepo:
1391 Test behavior of add for explicit path in subrepo:
1392 $ cd ..
1392 $ cd ..
1393 $ hg init explicit
1393 $ hg init explicit
1394 $ cd explicit
1394 $ cd explicit
1395 $ echo s = s > .hgsub
1395 $ echo s = s > .hgsub
1396 $ hg add .hgsub
1396 $ hg add .hgsub
1397 $ hg init s
1397 $ hg init s
1398 $ hg ci -m0
1398 $ hg ci -m0
1399 Adding with an explicit path in a subrepo adds the file
1399 Adding with an explicit path in a subrepo adds the file
1400 $ echo c1 > f1
1400 $ echo c1 > f1
1401 $ echo c2 > s/f2
1401 $ echo c2 > s/f2
1402 $ hg st -S
1402 $ hg st -S
1403 ? f1
1403 ? f1
1404 ? s/f2
1404 ? s/f2
1405 $ hg add s/f2
1405 $ hg add s/f2
1406 $ hg st -S
1406 $ hg st -S
1407 A s/f2
1407 A s/f2
1408 ? f1
1408 ? f1
1409 $ hg ci -R s -m0
1409 $ hg ci -R s -m0
1410 $ hg ci -Am1
1410 $ hg ci -Am1
1411 adding f1
1411 adding f1
1412 Adding with an explicit path in a subrepo with -S has the same behavior
1412 Adding with an explicit path in a subrepo with -S has the same behavior
1413 $ echo c3 > f3
1413 $ echo c3 > f3
1414 $ echo c4 > s/f4
1414 $ echo c4 > s/f4
1415 $ hg st -S
1415 $ hg st -S
1416 ? f3
1416 ? f3
1417 ? s/f4
1417 ? s/f4
1418 $ hg add -S s/f4
1418 $ hg add -S s/f4
1419 $ hg st -S
1419 $ hg st -S
1420 A s/f4
1420 A s/f4
1421 ? f3
1421 ? f3
1422 $ hg ci -R s -m1
1422 $ hg ci -R s -m1
1423 $ hg ci -Ama2
1423 $ hg ci -Ama2
1424 adding f3
1424 adding f3
1425 Adding without a path or pattern silently ignores subrepos
1425 Adding without a path or pattern silently ignores subrepos
1426 $ echo c5 > f5
1426 $ echo c5 > f5
1427 $ echo c6 > s/f6
1427 $ echo c6 > s/f6
1428 $ echo c7 > s/f7
1428 $ echo c7 > s/f7
1429 $ hg st -S
1429 $ hg st -S
1430 ? f5
1430 ? f5
1431 ? s/f6
1431 ? s/f6
1432 ? s/f7
1432 ? s/f7
1433 $ hg add
1433 $ hg add
1434 adding f5
1434 adding f5
1435 $ hg st -S
1435 $ hg st -S
1436 A f5
1436 A f5
1437 ? s/f6
1437 ? s/f6
1438 ? s/f7
1438 ? s/f7
1439 $ hg ci -R s -Am2
1439 $ hg ci -R s -Am2
1440 adding f6
1440 adding f6
1441 adding f7
1441 adding f7
1442 $ hg ci -m3
1442 $ hg ci -m3
1443 Adding without a path or pattern with -S also adds files in subrepos
1443 Adding without a path or pattern with -S also adds files in subrepos
1444 $ echo c8 > f8
1444 $ echo c8 > f8
1445 $ echo c9 > s/f9
1445 $ echo c9 > s/f9
1446 $ echo c10 > s/f10
1446 $ echo c10 > s/f10
1447 $ hg st -S
1447 $ hg st -S
1448 ? f8
1448 ? f8
1449 ? s/f10
1449 ? s/f10
1450 ? s/f9
1450 ? s/f9
1451 $ hg add -S
1451 $ hg add -S
1452 adding f8
1452 adding f8
1453 adding s/f10 (glob)
1453 adding s/f10 (glob)
1454 adding s/f9 (glob)
1454 adding s/f9 (glob)
1455 $ hg st -S
1455 $ hg st -S
1456 A f8
1456 A f8
1457 A s/f10
1457 A s/f10
1458 A s/f9
1458 A s/f9
1459 $ hg ci -R s -m3
1459 $ hg ci -R s -m3
1460 $ hg ci -m4
1460 $ hg ci -m4
1461 Adding with a pattern silently ignores subrepos
1461 Adding with a pattern silently ignores subrepos
1462 $ echo c11 > fm11
1462 $ echo c11 > fm11
1463 $ echo c12 > fn12
1463 $ echo c12 > fn12
1464 $ echo c13 > s/fm13
1464 $ echo c13 > s/fm13
1465 $ echo c14 > s/fn14
1465 $ echo c14 > s/fn14
1466 $ hg st -S
1466 $ hg st -S
1467 ? fm11
1467 ? fm11
1468 ? fn12
1468 ? fn12
1469 ? s/fm13
1469 ? s/fm13
1470 ? s/fn14
1470 ? s/fn14
1471 $ hg add 'glob:**fm*'
1471 $ hg add 'glob:**fm*'
1472 adding fm11
1472 adding fm11
1473 $ hg st -S
1473 $ hg st -S
1474 A fm11
1474 A fm11
1475 ? fn12
1475 ? fn12
1476 ? s/fm13
1476 ? s/fm13
1477 ? s/fn14
1477 ? s/fn14
1478 $ hg ci -R s -Am4
1478 $ hg ci -R s -Am4
1479 adding fm13
1479 adding fm13
1480 adding fn14
1480 adding fn14
1481 $ hg ci -Am5
1481 $ hg ci -Am5
1482 adding fn12
1482 adding fn12
1483 Adding with a pattern with -S also adds matches in subrepos
1483 Adding with a pattern with -S also adds matches in subrepos
1484 $ echo c15 > fm15
1484 $ echo c15 > fm15
1485 $ echo c16 > fn16
1485 $ echo c16 > fn16
1486 $ echo c17 > s/fm17
1486 $ echo c17 > s/fm17
1487 $ echo c18 > s/fn18
1487 $ echo c18 > s/fn18
1488 $ hg st -S
1488 $ hg st -S
1489 ? fm15
1489 ? fm15
1490 ? fn16
1490 ? fn16
1491 ? s/fm17
1491 ? s/fm17
1492 ? s/fn18
1492 ? s/fn18
1493 $ hg add -S 'glob:**fm*'
1493 $ hg add -S 'glob:**fm*'
1494 adding fm15
1494 adding fm15
1495 adding s/fm17 (glob)
1495 adding s/fm17 (glob)
1496 $ hg st -S
1496 $ hg st -S
1497 A fm15
1497 A fm15
1498 A s/fm17
1498 A s/fm17
1499 ? fn16
1499 ? fn16
1500 ? s/fn18
1500 ? s/fn18
1501 $ hg ci -R s -Am5
1501 $ hg ci -R s -Am5
1502 adding fn18
1502 adding fn18
1503 $ hg ci -Am6
1503 $ hg ci -Am6
1504 adding fn16
1504 adding fn16
1505
1505
1506 Test behavior of forget for explicit path in subrepo:
1506 Test behavior of forget for explicit path in subrepo:
1507 Forgetting an explicit path in a subrepo untracks the file
1507 Forgetting an explicit path in a subrepo untracks the file
1508 $ echo c19 > s/f19
1508 $ echo c19 > s/f19
1509 $ hg add s/f19
1509 $ hg add s/f19
1510 $ hg st -S
1510 $ hg st -S
1511 A s/f19
1511 A s/f19
1512 $ hg forget s/f19
1512 $ hg forget s/f19
1513 $ hg st -S
1513 $ hg st -S
1514 ? s/f19
1514 ? s/f19
1515 $ rm s/f19
1515 $ rm s/f19
1516 $ cd ..
1516 $ cd ..
1517
1517
1518 Courtesy phases synchronisation to publishing server does not block the push
1518 Courtesy phases synchronisation to publishing server does not block the push
1519 (issue3781)
1519 (issue3781)
1520
1520
1521 $ cp -R main issue3781
1521 $ cp -R main issue3781
1522 $ cp -R main issue3781-dest
1522 $ cp -R main issue3781-dest
1523 $ cd issue3781-dest/s
1523 $ cd issue3781-dest/s
1524 $ hg phase tip # show we have draft changeset
1524 $ hg phase tip # show we have draft changeset
1525 5: draft
1525 5: draft
1526 $ chmod a-w .hg/store/phaseroots # prevent phase push
1526 $ chmod a-w .hg/store/phaseroots # prevent phase push
1527 $ cd ../../issue3781
1527 $ cd ../../issue3781
1528 $ cat >> .hg/hgrc << EOF
1528 $ cat >> .hg/hgrc << EOF
1529 > [paths]
1529 > [paths]
1530 > default=../issue3781-dest/
1530 > default=../issue3781-dest/
1531 > EOF
1531 > EOF
1532 $ hg push --config devel.legacy.exchange=bundle1
1532 $ hg push --config devel.legacy.exchange=bundle1
1533 pushing to $TESTTMP/issue3781-dest (glob)
1533 pushing to $TESTTMP/issue3781-dest (glob)
1534 pushing subrepo s to $TESTTMP/issue3781-dest/s
1534 pushing subrepo s to $TESTTMP/issue3781-dest/s
1535 searching for changes
1535 searching for changes
1536 no changes found
1536 no changes found
1537 searching for changes
1537 searching for changes
1538 no changes found
1538 no changes found
1539 [1]
1539 [1]
1540 # clean the push cache
1540 # clean the push cache
1541 $ rm s/.hg/cache/storehash/*
1541 $ rm s/.hg/cache/storehash/*
1542 $ hg push # bundle2+
1542 $ hg push # bundle2+
1543 pushing to $TESTTMP/issue3781-dest (glob)
1543 pushing to $TESTTMP/issue3781-dest (glob)
1544 pushing subrepo s to $TESTTMP/issue3781-dest/s
1544 pushing subrepo s to $TESTTMP/issue3781-dest/s
1545 searching for changes
1545 searching for changes
1546 no changes found
1546 no changes found
1547 searching for changes
1547 searching for changes
1548 no changes found
1548 no changes found
1549 [1]
1549 [1]
1550 $ cd ..
1550 $ cd ..
1551
1551
1552 Test phase choice for newly created commit with "phases.subrepochecks"
1552 Test phase choice for newly created commit with "phases.subrepochecks"
1553 configuration
1553 configuration
1554
1554
1555 $ cd t
1555 $ cd t
1556 $ hg update -q -r 12
1556 $ hg update -q -r 12
1557
1557
1558 $ cat >> s/ss/.hg/hgrc <<EOF
1558 $ cat >> s/ss/.hg/hgrc <<EOF
1559 > [phases]
1559 > [phases]
1560 > new-commit = secret
1560 > new-commit = secret
1561 > EOF
1561 > EOF
1562 $ cat >> s/.hg/hgrc <<EOF
1562 $ cat >> s/.hg/hgrc <<EOF
1563 > [phases]
1563 > [phases]
1564 > new-commit = draft
1564 > new-commit = draft
1565 > EOF
1565 > EOF
1566 $ echo phasecheck1 >> s/ss/a
1566 $ echo phasecheck1 >> s/ss/a
1567 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1567 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1568 committing subrepository ss
1568 committing subrepository ss
1569 transaction abort!
1569 transaction abort!
1570 rollback completed
1570 rollback completed
1571 abort: can't commit in draft phase conflicting secret from subrepository ss
1571 abort: can't commit in draft phase conflicting secret from subrepository ss
1572 [255]
1572 [255]
1573 $ echo phasecheck2 >> s/ss/a
1573 $ echo phasecheck2 >> s/ss/a
1574 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1574 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1575 committing subrepository ss
1575 committing subrepository ss
1576 $ hg -R s/ss phase tip
1576 $ hg -R s/ss phase tip
1577 3: secret
1577 3: secret
1578 $ hg -R s phase tip
1578 $ hg -R s phase tip
1579 6: draft
1579 6: draft
1580 $ echo phasecheck3 >> s/ss/a
1580 $ echo phasecheck3 >> s/ss/a
1581 $ hg -R s commit -S -m phasecheck3
1581 $ hg -R s commit -S -m phasecheck3
1582 committing subrepository ss
1582 committing subrepository ss
1583 warning: changes are committed in secret phase from subrepository ss
1583 warning: changes are committed in secret phase from subrepository ss
1584 $ hg -R s/ss phase tip
1584 $ hg -R s/ss phase tip
1585 4: secret
1585 4: secret
1586 $ hg -R s phase tip
1586 $ hg -R s phase tip
1587 7: secret
1587 7: secret
1588
1588
1589 $ cat >> t/.hg/hgrc <<EOF
1589 $ cat >> t/.hg/hgrc <<EOF
1590 > [phases]
1590 > [phases]
1591 > new-commit = draft
1591 > new-commit = draft
1592 > EOF
1592 > EOF
1593 $ cat >> .hg/hgrc <<EOF
1593 $ cat >> .hg/hgrc <<EOF
1594 > [phases]
1594 > [phases]
1595 > new-commit = public
1595 > new-commit = public
1596 > EOF
1596 > EOF
1597 $ echo phasecheck4 >> s/ss/a
1597 $ echo phasecheck4 >> s/ss/a
1598 $ echo phasecheck4 >> t/t
1598 $ echo phasecheck4 >> t/t
1599 $ hg commit -S -m phasecheck4
1599 $ hg commit -S -m phasecheck4
1600 committing subrepository s
1600 committing subrepository s
1601 committing subrepository s/ss (glob)
1601 committing subrepository s/ss (glob)
1602 warning: changes are committed in secret phase from subrepository ss
1602 warning: changes are committed in secret phase from subrepository ss
1603 committing subrepository t
1603 committing subrepository t
1604 warning: changes are committed in secret phase from subrepository s
1604 warning: changes are committed in secret phase from subrepository s
1605 created new head
1605 created new head
1606 $ hg -R s/ss phase tip
1606 $ hg -R s/ss phase tip
1607 5: secret
1607 5: secret
1608 $ hg -R s phase tip
1608 $ hg -R s phase tip
1609 8: secret
1609 8: secret
1610 $ hg -R t phase tip
1610 $ hg -R t phase tip
1611 6: draft
1611 6: draft
1612 $ hg phase tip
1612 $ hg phase tip
1613 15: secret
1613 15: secret
1614
1614
1615 $ cd ..
1615 $ cd ..
1616
1616
1617
1617
1618 Test that commit --secret works on both repo and subrepo (issue4182)
1618 Test that commit --secret works on both repo and subrepo (issue4182)
1619
1619
1620 $ cd main
1620 $ cd main
1621 $ echo secret >> b
1621 $ echo secret >> b
1622 $ echo secret >> s/b
1622 $ echo secret >> s/b
1623 $ hg commit --secret --subrepo -m "secret"
1623 $ hg commit --secret --subrepo -m "secret"
1624 committing subrepository s
1624 committing subrepository s
1625 $ hg phase -r .
1625 $ hg phase -r .
1626 6: secret
1626 6: secret
1627 $ cd s
1627 $ cd s
1628 $ hg phase -r .
1628 $ hg phase -r .
1629 6: secret
1629 6: secret
1630 $ cd ../../
1630 $ cd ../../
1631
1631
1632 Test "subrepos" template keyword
1632 Test "subrepos" template keyword
1633
1633
1634 $ cd t
1634 $ cd t
1635 $ hg update -q 15
1635 $ hg update -q 15
1636 $ cat > .hgsub <<EOF
1636 $ cat > .hgsub <<EOF
1637 > s = s
1637 > s = s
1638 > EOF
1638 > EOF
1639 $ hg commit -m "16"
1639 $ hg commit -m "16"
1640 warning: changes are committed in secret phase from subrepository s
1640 warning: changes are committed in secret phase from subrepository s
1641
1641
1642 (addition of ".hgsub" itself)
1642 (addition of ".hgsub" itself)
1643
1643
1644 $ hg diff --nodates -c 1 .hgsubstate
1644 $ hg diff --nodates -c 1 .hgsubstate
1645 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1645 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1646 --- /dev/null
1646 --- /dev/null
1647 +++ b/.hgsubstate
1647 +++ b/.hgsubstate
1648 @@ -0,0 +1,1 @@
1648 @@ -0,0 +1,1 @@
1649 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1649 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1650 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1650 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1651 f7b1eb17ad24 000000000000
1651 f7b1eb17ad24 000000000000
1652 s
1652 s
1653
1653
1654 (modification of existing entry)
1654 (modification of existing entry)
1655
1655
1656 $ hg diff --nodates -c 2 .hgsubstate
1656 $ hg diff --nodates -c 2 .hgsubstate
1657 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1657 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1658 --- a/.hgsubstate
1658 --- a/.hgsubstate
1659 +++ b/.hgsubstate
1659 +++ b/.hgsubstate
1660 @@ -1,1 +1,1 @@
1660 @@ -1,1 +1,1 @@
1661 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1661 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1662 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1662 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1663 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1663 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1664 7cf8cfea66e4 000000000000
1664 7cf8cfea66e4 000000000000
1665 s
1665 s
1666
1666
1667 (addition of entry)
1667 (addition of entry)
1668
1668
1669 $ hg diff --nodates -c 5 .hgsubstate
1669 $ hg diff --nodates -c 5 .hgsubstate
1670 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1670 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1671 --- a/.hgsubstate
1671 --- a/.hgsubstate
1672 +++ b/.hgsubstate
1672 +++ b/.hgsubstate
1673 @@ -1,1 +1,2 @@
1673 @@ -1,1 +1,2 @@
1674 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1674 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1675 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1675 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1676 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1676 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1677 7cf8cfea66e4 000000000000
1677 7cf8cfea66e4 000000000000
1678 t
1678 t
1679
1679
1680 (removal of existing entry)
1680 (removal of existing entry)
1681
1681
1682 $ hg diff --nodates -c 16 .hgsubstate
1682 $ hg diff --nodates -c 16 .hgsubstate
1683 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1683 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1684 --- a/.hgsubstate
1684 --- a/.hgsubstate
1685 +++ b/.hgsubstate
1685 +++ b/.hgsubstate
1686 @@ -1,2 +1,1 @@
1686 @@ -1,2 +1,1 @@
1687 0731af8ca9423976d3743119d0865097c07bdc1b s
1687 0731af8ca9423976d3743119d0865097c07bdc1b s
1688 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1688 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1689 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1689 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1690 8bec38d2bd0b 000000000000
1690 8bec38d2bd0b 000000000000
1691 t
1691 t
1692
1692
1693 (merging)
1693 (merging)
1694
1694
1695 $ hg diff --nodates -c 9 .hgsubstate
1695 $ hg diff --nodates -c 9 .hgsubstate
1696 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1696 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1697 --- a/.hgsubstate
1697 --- a/.hgsubstate
1698 +++ b/.hgsubstate
1698 +++ b/.hgsubstate
1699 @@ -1,1 +1,2 @@
1699 @@ -1,1 +1,2 @@
1700 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1700 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1701 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1701 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1702 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1702 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1703 f6affe3fbfaa 1f14a2e2d3ec
1703 f6affe3fbfaa 1f14a2e2d3ec
1704 t
1704 t
1705
1705
1706 (removal of ".hgsub" itself)
1706 (removal of ".hgsub" itself)
1707
1707
1708 $ hg diff --nodates -c 8 .hgsubstate
1708 $ hg diff --nodates -c 8 .hgsubstate
1709 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1709 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1710 --- a/.hgsubstate
1710 --- a/.hgsubstate
1711 +++ /dev/null
1711 +++ /dev/null
1712 @@ -1,2 +0,0 @@
1712 @@ -1,2 +0,0 @@
1713 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1713 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1714 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1714 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1715 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1715 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1716 f94576341bcf 000000000000
1716 f94576341bcf 000000000000
1717
1717
1718 Test that '[paths]' is configured correctly at subrepo creation
1718 Test that '[paths]' is configured correctly at subrepo creation
1719
1719
1720 $ cd $TESTTMP/tc
1720 $ cd $TESTTMP/tc
1721 $ cat > .hgsub <<EOF
1721 $ cat > .hgsub <<EOF
1722 > # to clear bogus subrepo path 'bogus=[boguspath'
1722 > # to clear bogus subrepo path 'bogus=[boguspath'
1723 > s = s
1723 > s = s
1724 > t = t
1724 > t = t
1725 > EOF
1725 > EOF
1726 $ hg update -q --clean null
1726 $ hg update -q --clean null
1727 $ rm -rf s t
1727 $ rm -rf s t
1728 $ cat >> .hg/hgrc <<EOF
1728 $ cat >> .hg/hgrc <<EOF
1729 > [paths]
1729 > [paths]
1730 > default-push = /foo/bar
1730 > default-push = /foo/bar
1731 > EOF
1731 > EOF
1732 $ hg update -q
1732 $ hg update -q
1733 $ cat s/.hg/hgrc
1733 $ cat s/.hg/hgrc
1734 [paths]
1734 [paths]
1735 default = $TESTTMP/t/s
1735 default = $TESTTMP/t/s
1736 default-push = /foo/bar/s
1736 default-push = /foo/bar/s
1737 $ cat s/ss/.hg/hgrc
1737 $ cat s/ss/.hg/hgrc
1738 [paths]
1738 [paths]
1739 default = $TESTTMP/t/s/ss
1739 default = $TESTTMP/t/s/ss
1740 default-push = /foo/bar/s/ss
1740 default-push = /foo/bar/s/ss
1741 $ cat t/.hg/hgrc
1741 $ cat t/.hg/hgrc
1742 [paths]
1742 [paths]
1743 default = $TESTTMP/t/t
1743 default = $TESTTMP/t/t
1744 default-push = /foo/bar/t
1744 default-push = /foo/bar/t
1745
1745
1746 $ cd $TESTTMP/t
1746 $ cd $TESTTMP/t
1747 $ hg up -qC 0
1747 $ hg up -qC 0
1748 $ echo 'bar' > bar.txt
1748 $ echo 'bar' > bar.txt
1749 $ hg ci -Am 'branch before subrepo add'
1749 $ hg ci -Am 'branch before subrepo add'
1750 adding bar.txt
1750 adding bar.txt
1751 created new head
1751 created new head
1752 $ hg merge -r "first(subrepo('s'))"
1752 $ hg merge -r "first(subrepo('s'))"
1753 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1753 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1754 (branch merge, don't forget to commit)
1754 (branch merge, don't forget to commit)
1755 $ hg status -S -X '.hgsub*'
1755 $ hg status -S -X '.hgsub*'
1756 A s/a
1756 A s/a
1757 ? s/b
1757 ? s/b
1758 ? s/c
1758 ? s/c
1759 ? s/f1
1759 ? s/f1
1760 $ hg status -S --rev 'p2()'
1760 $ hg status -S --rev 'p2()'
1761 A bar.txt
1761 A bar.txt
1762 ? s/b
1762 ? s/b
1763 ? s/c
1763 ? s/c
1764 ? s/f1
1764 ? s/f1
1765 $ hg diff -S -X '.hgsub*' --nodates
1765 $ hg diff -S -X '.hgsub*' --nodates
1766 diff -r 000000000000 s/a
1766 diff -r 000000000000 s/a
1767 --- /dev/null
1767 --- /dev/null
1768 +++ b/s/a
1768 +++ b/s/a
1769 @@ -0,0 +1,1 @@
1769 @@ -0,0 +1,1 @@
1770 +a
1770 +a
1771 $ hg diff -S --rev 'p2()' --nodates
1771 $ hg diff -S --rev 'p2()' --nodates
1772 diff -r 7cf8cfea66e4 bar.txt
1772 diff -r 7cf8cfea66e4 bar.txt
1773 --- /dev/null
1773 --- /dev/null
1774 +++ b/bar.txt
1774 +++ b/bar.txt
1775 @@ -0,0 +1,1 @@
1775 @@ -0,0 +1,1 @@
1776 +bar
1776 +bar
1777
1777
1778 $ cd ..
1778 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now