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