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