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