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