##// END OF EJS Templates
subrepo: use sharepath if available when locating the source repo...
Matt Harbison -
r18510:f254ab62 stable
parent child Browse files
Show More
@@ -1,1329 +1,1332 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 errno, os, re, xml.dom.minidom, shutil, posixpath
8 import errno, os, re, xml.dom.minidom, shutil, posixpath
9 import stat, subprocess, tarfile
9 import stat, subprocess, tarfile
10 from i18n import _
10 from i18n import _
11 import config, scmutil, util, node, error, cmdutil, bookmarks, match as matchmod
11 import config, scmutil, util, node, error, cmdutil, bookmarks, match as matchmod
12 hg = None
12 hg = None
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 nullstate = ('', '', 'empty')
15 nullstate = ('', '', 'empty')
16
16
17 class SubrepoAbort(error.Abort):
17 class SubrepoAbort(error.Abort):
18 """Exception class used to avoid handling a subrepo error more than once"""
18 """Exception class used to avoid handling a subrepo error more than once"""
19 def __init__(self, *args, **kw):
19 def __init__(self, *args, **kw):
20 error.Abort.__init__(self, *args, **kw)
20 error.Abort.__init__(self, *args, **kw)
21 self.subrepo = kw.get('subrepo')
21 self.subrepo = kw.get('subrepo')
22
22
23 def annotatesubrepoerror(func):
23 def annotatesubrepoerror(func):
24 def decoratedmethod(self, *args, **kargs):
24 def decoratedmethod(self, *args, **kargs):
25 try:
25 try:
26 res = func(self, *args, **kargs)
26 res = func(self, *args, **kargs)
27 except SubrepoAbort, ex:
27 except SubrepoAbort, ex:
28 # This exception has already been handled
28 # This exception has already been handled
29 raise ex
29 raise ex
30 except error.Abort, ex:
30 except error.Abort, ex:
31 subrepo = subrelpath(self)
31 subrepo = subrelpath(self)
32 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
32 errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
33 # avoid handling this exception by raising a SubrepoAbort exception
33 # avoid handling this exception by raising a SubrepoAbort exception
34 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo)
34 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo)
35 return res
35 return res
36 return decoratedmethod
36 return decoratedmethod
37
37
38 def state(ctx, ui):
38 def state(ctx, ui):
39 """return a state dict, mapping subrepo paths configured in .hgsub
39 """return a state dict, mapping subrepo paths configured in .hgsub
40 to tuple: (source from .hgsub, revision from .hgsubstate, kind
40 to tuple: (source from .hgsub, revision from .hgsubstate, kind
41 (key in types dict))
41 (key in types dict))
42 """
42 """
43 p = config.config()
43 p = config.config()
44 def read(f, sections=None, remap=None):
44 def read(f, sections=None, remap=None):
45 if f in ctx:
45 if f in ctx:
46 try:
46 try:
47 data = ctx[f].data()
47 data = ctx[f].data()
48 except IOError, err:
48 except IOError, err:
49 if err.errno != errno.ENOENT:
49 if err.errno != errno.ENOENT:
50 raise
50 raise
51 # handle missing subrepo spec files as removed
51 # handle missing subrepo spec files as removed
52 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
52 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
53 return
53 return
54 p.parse(f, data, sections, remap, read)
54 p.parse(f, data, sections, remap, read)
55 else:
55 else:
56 raise util.Abort(_("subrepo spec file %s not found") % f)
56 raise util.Abort(_("subrepo spec file %s not found") % f)
57
57
58 if '.hgsub' in ctx:
58 if '.hgsub' in ctx:
59 read('.hgsub')
59 read('.hgsub')
60
60
61 for path, src in ui.configitems('subpaths'):
61 for path, src in ui.configitems('subpaths'):
62 p.set('subpaths', path, src, ui.configsource('subpaths', path))
62 p.set('subpaths', path, src, ui.configsource('subpaths', path))
63
63
64 rev = {}
64 rev = {}
65 if '.hgsubstate' in ctx:
65 if '.hgsubstate' in ctx:
66 try:
66 try:
67 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
67 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
68 l = l.lstrip()
68 l = l.lstrip()
69 if not l:
69 if not l:
70 continue
70 continue
71 try:
71 try:
72 revision, path = l.split(" ", 1)
72 revision, path = l.split(" ", 1)
73 except ValueError:
73 except ValueError:
74 raise util.Abort(_("invalid subrepository revision "
74 raise util.Abort(_("invalid subrepository revision "
75 "specifier in .hgsubstate line %d")
75 "specifier in .hgsubstate line %d")
76 % (i + 1))
76 % (i + 1))
77 rev[path] = revision
77 rev[path] = revision
78 except IOError, err:
78 except IOError, err:
79 if err.errno != errno.ENOENT:
79 if err.errno != errno.ENOENT:
80 raise
80 raise
81
81
82 def remap(src):
82 def remap(src):
83 for pattern, repl in p.items('subpaths'):
83 for pattern, repl in p.items('subpaths'):
84 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
84 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
85 # does a string decode.
85 # does a string decode.
86 repl = repl.encode('string-escape')
86 repl = repl.encode('string-escape')
87 # However, we still want to allow back references to go
87 # However, we still want to allow back references to go
88 # through unharmed, so we turn r'\\1' into r'\1'. Again,
88 # through unharmed, so we turn r'\\1' into r'\1'. Again,
89 # extra escapes are needed because re.sub string decodes.
89 # extra escapes are needed because re.sub string decodes.
90 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
90 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
91 try:
91 try:
92 src = re.sub(pattern, repl, src, 1)
92 src = re.sub(pattern, repl, src, 1)
93 except re.error, e:
93 except re.error, e:
94 raise util.Abort(_("bad subrepository pattern in %s: %s")
94 raise util.Abort(_("bad subrepository pattern in %s: %s")
95 % (p.source('subpaths', pattern), e))
95 % (p.source('subpaths', pattern), e))
96 return src
96 return src
97
97
98 state = {}
98 state = {}
99 for path, src in p[''].items():
99 for path, src in p[''].items():
100 kind = 'hg'
100 kind = 'hg'
101 if src.startswith('['):
101 if src.startswith('['):
102 if ']' not in src:
102 if ']' not in src:
103 raise util.Abort(_('missing ] in subrepo source'))
103 raise util.Abort(_('missing ] in subrepo source'))
104 kind, src = src.split(']', 1)
104 kind, src = src.split(']', 1)
105 kind = kind[1:]
105 kind = kind[1:]
106 src = src.lstrip() # strip any extra whitespace after ']'
106 src = src.lstrip() # strip any extra whitespace after ']'
107
107
108 if not util.url(src).isabs():
108 if not util.url(src).isabs():
109 parent = _abssource(ctx._repo, abort=False)
109 parent = _abssource(ctx._repo, abort=False)
110 if parent:
110 if parent:
111 parent = util.url(parent)
111 parent = util.url(parent)
112 parent.path = posixpath.join(parent.path or '', src)
112 parent.path = posixpath.join(parent.path or '', src)
113 parent.path = posixpath.normpath(parent.path)
113 parent.path = posixpath.normpath(parent.path)
114 joined = str(parent)
114 joined = str(parent)
115 # Remap the full joined path and use it if it changes,
115 # Remap the full joined path and use it if it changes,
116 # else remap the original source.
116 # else remap the original source.
117 remapped = remap(joined)
117 remapped = remap(joined)
118 if remapped == joined:
118 if remapped == joined:
119 src = remap(src)
119 src = remap(src)
120 else:
120 else:
121 src = remapped
121 src = remapped
122
122
123 src = remap(src)
123 src = remap(src)
124 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
124 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
125
125
126 return state
126 return state
127
127
128 def writestate(repo, state):
128 def writestate(repo, state):
129 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
129 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
130 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
130 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
131 repo.wwrite('.hgsubstate', ''.join(lines), '')
131 repo.wwrite('.hgsubstate', ''.join(lines), '')
132
132
133 def submerge(repo, wctx, mctx, actx, overwrite):
133 def submerge(repo, wctx, mctx, actx, overwrite):
134 """delegated from merge.applyupdates: merging of .hgsubstate file
134 """delegated from merge.applyupdates: merging of .hgsubstate file
135 in working context, merging context and ancestor context"""
135 in working context, merging context and ancestor context"""
136 if mctx == actx: # backwards?
136 if mctx == actx: # backwards?
137 actx = wctx.p1()
137 actx = wctx.p1()
138 s1 = wctx.substate
138 s1 = wctx.substate
139 s2 = mctx.substate
139 s2 = mctx.substate
140 sa = actx.substate
140 sa = actx.substate
141 sm = {}
141 sm = {}
142
142
143 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
143 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
144
144
145 def debug(s, msg, r=""):
145 def debug(s, msg, r=""):
146 if r:
146 if r:
147 r = "%s:%s:%s" % r
147 r = "%s:%s:%s" % r
148 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
148 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
149
149
150 for s, l in sorted(s1.iteritems()):
150 for s, l in sorted(s1.iteritems()):
151 a = sa.get(s, nullstate)
151 a = sa.get(s, nullstate)
152 ld = l # local state with possible dirty flag for compares
152 ld = l # local state with possible dirty flag for compares
153 if wctx.sub(s).dirty():
153 if wctx.sub(s).dirty():
154 ld = (l[0], l[1] + "+")
154 ld = (l[0], l[1] + "+")
155 if wctx == actx: # overwrite
155 if wctx == actx: # overwrite
156 a = ld
156 a = ld
157
157
158 if s in s2:
158 if s in s2:
159 r = s2[s]
159 r = s2[s]
160 if ld == r or r == a: # no change or local is newer
160 if ld == r or r == a: # no change or local is newer
161 sm[s] = l
161 sm[s] = l
162 continue
162 continue
163 elif ld == a: # other side changed
163 elif ld == a: # other side changed
164 debug(s, "other changed, get", r)
164 debug(s, "other changed, get", r)
165 wctx.sub(s).get(r, overwrite)
165 wctx.sub(s).get(r, overwrite)
166 sm[s] = r
166 sm[s] = r
167 elif ld[0] != r[0]: # sources differ
167 elif ld[0] != r[0]: # sources differ
168 if repo.ui.promptchoice(
168 if repo.ui.promptchoice(
169 _(' subrepository sources for %s differ\n'
169 _(' subrepository sources for %s differ\n'
170 'use (l)ocal source (%s) or (r)emote source (%s)?')
170 'use (l)ocal source (%s) or (r)emote source (%s)?')
171 % (s, l[0], r[0]),
171 % (s, l[0], r[0]),
172 (_('&Local'), _('&Remote')), 0):
172 (_('&Local'), _('&Remote')), 0):
173 debug(s, "prompt changed, get", r)
173 debug(s, "prompt changed, get", r)
174 wctx.sub(s).get(r, overwrite)
174 wctx.sub(s).get(r, overwrite)
175 sm[s] = r
175 sm[s] = r
176 elif ld[1] == a[1]: # local side is unchanged
176 elif ld[1] == a[1]: # local side is unchanged
177 debug(s, "other side changed, get", r)
177 debug(s, "other side changed, get", r)
178 wctx.sub(s).get(r, overwrite)
178 wctx.sub(s).get(r, overwrite)
179 sm[s] = r
179 sm[s] = r
180 else:
180 else:
181 debug(s, "both sides changed, merge with", r)
181 debug(s, "both sides changed, merge with", r)
182 wctx.sub(s).merge(r)
182 wctx.sub(s).merge(r)
183 sm[s] = l
183 sm[s] = l
184 elif ld == a: # remote removed, local unchanged
184 elif ld == a: # remote removed, local unchanged
185 debug(s, "remote removed, remove")
185 debug(s, "remote removed, remove")
186 wctx.sub(s).remove()
186 wctx.sub(s).remove()
187 elif a == nullstate: # not present in remote or ancestor
187 elif a == nullstate: # not present in remote or ancestor
188 debug(s, "local added, keep")
188 debug(s, "local added, keep")
189 sm[s] = l
189 sm[s] = l
190 continue
190 continue
191 else:
191 else:
192 if repo.ui.promptchoice(
192 if repo.ui.promptchoice(
193 _(' local changed subrepository %s which remote removed\n'
193 _(' local changed subrepository %s which remote removed\n'
194 'use (c)hanged version or (d)elete?') % s,
194 'use (c)hanged version or (d)elete?') % s,
195 (_('&Changed'), _('&Delete')), 0):
195 (_('&Changed'), _('&Delete')), 0):
196 debug(s, "prompt remove")
196 debug(s, "prompt remove")
197 wctx.sub(s).remove()
197 wctx.sub(s).remove()
198
198
199 for s, r in sorted(s2.items()):
199 for s, r in sorted(s2.items()):
200 if s in s1:
200 if s in s1:
201 continue
201 continue
202 elif s not in sa:
202 elif s not in sa:
203 debug(s, "remote added, get", r)
203 debug(s, "remote added, get", r)
204 mctx.sub(s).get(r)
204 mctx.sub(s).get(r)
205 sm[s] = r
205 sm[s] = r
206 elif r != sa[s]:
206 elif r != sa[s]:
207 if repo.ui.promptchoice(
207 if repo.ui.promptchoice(
208 _(' remote changed subrepository %s which local removed\n'
208 _(' remote changed subrepository %s which local removed\n'
209 'use (c)hanged version or (d)elete?') % s,
209 'use (c)hanged version or (d)elete?') % s,
210 (_('&Changed'), _('&Delete')), 0) == 0:
210 (_('&Changed'), _('&Delete')), 0) == 0:
211 debug(s, "prompt recreate", r)
211 debug(s, "prompt recreate", r)
212 wctx.sub(s).get(r)
212 wctx.sub(s).get(r)
213 sm[s] = r
213 sm[s] = r
214
214
215 # record merged .hgsubstate
215 # record merged .hgsubstate
216 writestate(repo, sm)
216 writestate(repo, sm)
217
217
218 def _updateprompt(ui, sub, dirty, local, remote):
218 def _updateprompt(ui, sub, dirty, local, remote):
219 if dirty:
219 if dirty:
220 msg = (_(' subrepository sources for %s differ\n'
220 msg = (_(' subrepository sources for %s differ\n'
221 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
221 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
222 % (subrelpath(sub), local, remote))
222 % (subrelpath(sub), local, remote))
223 else:
223 else:
224 msg = (_(' subrepository sources for %s differ (in checked out '
224 msg = (_(' subrepository sources for %s differ (in checked out '
225 'version)\n'
225 'version)\n'
226 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
226 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
227 % (subrelpath(sub), local, remote))
227 % (subrelpath(sub), local, remote))
228 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
228 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
229
229
230 def reporelpath(repo):
230 def reporelpath(repo):
231 """return path to this (sub)repo as seen from outermost repo"""
231 """return path to this (sub)repo as seen from outermost repo"""
232 parent = repo
232 parent = repo
233 while util.safehasattr(parent, '_subparent'):
233 while util.safehasattr(parent, '_subparent'):
234 parent = parent._subparent
234 parent = parent._subparent
235 p = parent.root.rstrip(os.sep)
235 p = parent.root.rstrip(os.sep)
236 return repo.root[len(p) + 1:]
236 return repo.root[len(p) + 1:]
237
237
238 def subrelpath(sub):
238 def subrelpath(sub):
239 """return path to this subrepo as seen from outermost repo"""
239 """return path to this subrepo as seen from outermost repo"""
240 if util.safehasattr(sub, '_relpath'):
240 if util.safehasattr(sub, '_relpath'):
241 return sub._relpath
241 return sub._relpath
242 if not util.safehasattr(sub, '_repo'):
242 if not util.safehasattr(sub, '_repo'):
243 return sub._path
243 return sub._path
244 return reporelpath(sub._repo)
244 return reporelpath(sub._repo)
245
245
246 def _abssource(repo, push=False, abort=True):
246 def _abssource(repo, push=False, abort=True):
247 """return pull/push path of repo - either based on parent repo .hgsub info
247 """return pull/push path of repo - either based on parent repo .hgsub info
248 or on the top repo config. Abort or return None if no source found."""
248 or on the top repo config. Abort or return None if no source found."""
249 if util.safehasattr(repo, '_subparent'):
249 if util.safehasattr(repo, '_subparent'):
250 source = util.url(repo._subsource)
250 source = util.url(repo._subsource)
251 if source.isabs():
251 if source.isabs():
252 return str(source)
252 return str(source)
253 source.path = posixpath.normpath(source.path)
253 source.path = posixpath.normpath(source.path)
254 parent = _abssource(repo._subparent, push, abort=False)
254 parent = _abssource(repo._subparent, push, abort=False)
255 if parent:
255 if parent:
256 parent = util.url(util.pconvert(parent))
256 parent = util.url(util.pconvert(parent))
257 parent.path = posixpath.join(parent.path or '', source.path)
257 parent.path = posixpath.join(parent.path or '', source.path)
258 parent.path = posixpath.normpath(parent.path)
258 parent.path = posixpath.normpath(parent.path)
259 return str(parent)
259 return str(parent)
260 else: # recursion reached top repo
260 else: # recursion reached top repo
261 if util.safehasattr(repo, '_subtoppath'):
261 if util.safehasattr(repo, '_subtoppath'):
262 return repo._subtoppath
262 return repo._subtoppath
263 if push and repo.ui.config('paths', 'default-push'):
263 if push and repo.ui.config('paths', 'default-push'):
264 return repo.ui.config('paths', 'default-push')
264 return repo.ui.config('paths', 'default-push')
265 if repo.ui.config('paths', 'default'):
265 if repo.ui.config('paths', 'default'):
266 return repo.ui.config('paths', 'default')
266 return repo.ui.config('paths', 'default')
267 if repo.sharedpath != repo.path:
268 # chop off the .hg component to get the default path form
269 return os.path.dirname(repo.sharedpath)
267 if abort:
270 if abort:
268 raise util.Abort(_("default path for subrepository not found"))
271 raise util.Abort(_("default path for subrepository not found"))
269
272
270 def itersubrepos(ctx1, ctx2):
273 def itersubrepos(ctx1, ctx2):
271 """find subrepos in ctx1 or ctx2"""
274 """find subrepos in ctx1 or ctx2"""
272 # Create a (subpath, ctx) mapping where we prefer subpaths from
275 # Create a (subpath, ctx) mapping where we prefer subpaths from
273 # ctx1. The subpaths from ctx2 are important when the .hgsub file
276 # ctx1. The subpaths from ctx2 are important when the .hgsub file
274 # has been modified (in ctx2) but not yet committed (in ctx1).
277 # has been modified (in ctx2) but not yet committed (in ctx1).
275 subpaths = dict.fromkeys(ctx2.substate, ctx2)
278 subpaths = dict.fromkeys(ctx2.substate, ctx2)
276 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
279 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
277 for subpath, ctx in sorted(subpaths.iteritems()):
280 for subpath, ctx in sorted(subpaths.iteritems()):
278 yield subpath, ctx.sub(subpath)
281 yield subpath, ctx.sub(subpath)
279
282
280 def subrepo(ctx, path):
283 def subrepo(ctx, path):
281 """return instance of the right subrepo class for subrepo in path"""
284 """return instance of the right subrepo class for subrepo in path"""
282 # subrepo inherently violates our import layering rules
285 # subrepo inherently violates our import layering rules
283 # because it wants to make repo objects from deep inside the stack
286 # because it wants to make repo objects from deep inside the stack
284 # so we manually delay the circular imports to not break
287 # so we manually delay the circular imports to not break
285 # scripts that don't use our demand-loading
288 # scripts that don't use our demand-loading
286 global hg
289 global hg
287 import hg as h
290 import hg as h
288 hg = h
291 hg = h
289
292
290 scmutil.pathauditor(ctx._repo.root)(path)
293 scmutil.pathauditor(ctx._repo.root)(path)
291 state = ctx.substate[path]
294 state = ctx.substate[path]
292 if state[2] not in types:
295 if state[2] not in types:
293 raise util.Abort(_('unknown subrepo type %s') % state[2])
296 raise util.Abort(_('unknown subrepo type %s') % state[2])
294 return types[state[2]](ctx, path, state[:2])
297 return types[state[2]](ctx, path, state[:2])
295
298
296 # subrepo classes need to implement the following abstract class:
299 # subrepo classes need to implement the following abstract class:
297
300
298 class abstractsubrepo(object):
301 class abstractsubrepo(object):
299
302
300 def dirty(self, ignoreupdate=False):
303 def dirty(self, ignoreupdate=False):
301 """returns true if the dirstate of the subrepo is dirty or does not
304 """returns true if the dirstate of the subrepo is dirty or does not
302 match current stored state. If ignoreupdate is true, only check
305 match current stored state. If ignoreupdate is true, only check
303 whether the subrepo has uncommitted changes in its dirstate.
306 whether the subrepo has uncommitted changes in its dirstate.
304 """
307 """
305 raise NotImplementedError
308 raise NotImplementedError
306
309
307 def basestate(self):
310 def basestate(self):
308 """current working directory base state, disregarding .hgsubstate
311 """current working directory base state, disregarding .hgsubstate
309 state and working directory modifications"""
312 state and working directory modifications"""
310 raise NotImplementedError
313 raise NotImplementedError
311
314
312 def checknested(self, path):
315 def checknested(self, path):
313 """check if path is a subrepository within this repository"""
316 """check if path is a subrepository within this repository"""
314 return False
317 return False
315
318
316 def commit(self, text, user, date):
319 def commit(self, text, user, date):
317 """commit the current changes to the subrepo with the given
320 """commit the current changes to the subrepo with the given
318 log message. Use given user and date if possible. Return the
321 log message. Use given user and date if possible. Return the
319 new state of the subrepo.
322 new state of the subrepo.
320 """
323 """
321 raise NotImplementedError
324 raise NotImplementedError
322
325
323 def remove(self):
326 def remove(self):
324 """remove the subrepo
327 """remove the subrepo
325
328
326 (should verify the dirstate is not dirty first)
329 (should verify the dirstate is not dirty first)
327 """
330 """
328 raise NotImplementedError
331 raise NotImplementedError
329
332
330 def get(self, state, overwrite=False):
333 def get(self, state, overwrite=False):
331 """run whatever commands are needed to put the subrepo into
334 """run whatever commands are needed to put the subrepo into
332 this state
335 this state
333 """
336 """
334 raise NotImplementedError
337 raise NotImplementedError
335
338
336 def merge(self, state):
339 def merge(self, state):
337 """merge currently-saved state with the new state."""
340 """merge currently-saved state with the new state."""
338 raise NotImplementedError
341 raise NotImplementedError
339
342
340 def push(self, opts):
343 def push(self, opts):
341 """perform whatever action is analogous to 'hg push'
344 """perform whatever action is analogous to 'hg push'
342
345
343 This may be a no-op on some systems.
346 This may be a no-op on some systems.
344 """
347 """
345 raise NotImplementedError
348 raise NotImplementedError
346
349
347 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
350 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
348 return []
351 return []
349
352
350 def status(self, rev2, **opts):
353 def status(self, rev2, **opts):
351 return [], [], [], [], [], [], []
354 return [], [], [], [], [], [], []
352
355
353 def diff(self, ui, diffopts, node2, match, prefix, **opts):
356 def diff(self, ui, diffopts, node2, match, prefix, **opts):
354 pass
357 pass
355
358
356 def outgoing(self, ui, dest, opts):
359 def outgoing(self, ui, dest, opts):
357 return 1
360 return 1
358
361
359 def incoming(self, ui, source, opts):
362 def incoming(self, ui, source, opts):
360 return 1
363 return 1
361
364
362 def files(self):
365 def files(self):
363 """return filename iterator"""
366 """return filename iterator"""
364 raise NotImplementedError
367 raise NotImplementedError
365
368
366 def filedata(self, name):
369 def filedata(self, name):
367 """return file data"""
370 """return file data"""
368 raise NotImplementedError
371 raise NotImplementedError
369
372
370 def fileflags(self, name):
373 def fileflags(self, name):
371 """return file flags"""
374 """return file flags"""
372 return ''
375 return ''
373
376
374 def archive(self, ui, archiver, prefix, match=None):
377 def archive(self, ui, archiver, prefix, match=None):
375 if match is not None:
378 if match is not None:
376 files = [f for f in self.files() if match(f)]
379 files = [f for f in self.files() if match(f)]
377 else:
380 else:
378 files = self.files()
381 files = self.files()
379 total = len(files)
382 total = len(files)
380 relpath = subrelpath(self)
383 relpath = subrelpath(self)
381 ui.progress(_('archiving (%s)') % relpath, 0,
384 ui.progress(_('archiving (%s)') % relpath, 0,
382 unit=_('files'), total=total)
385 unit=_('files'), total=total)
383 for i, name in enumerate(files):
386 for i, name in enumerate(files):
384 flags = self.fileflags(name)
387 flags = self.fileflags(name)
385 mode = 'x' in flags and 0755 or 0644
388 mode = 'x' in flags and 0755 or 0644
386 symlink = 'l' in flags
389 symlink = 'l' in flags
387 archiver.addfile(os.path.join(prefix, self._path, name),
390 archiver.addfile(os.path.join(prefix, self._path, name),
388 mode, symlink, self.filedata(name))
391 mode, symlink, self.filedata(name))
389 ui.progress(_('archiving (%s)') % relpath, i + 1,
392 ui.progress(_('archiving (%s)') % relpath, i + 1,
390 unit=_('files'), total=total)
393 unit=_('files'), total=total)
391 ui.progress(_('archiving (%s)') % relpath, None)
394 ui.progress(_('archiving (%s)') % relpath, None)
392
395
393 def walk(self, match):
396 def walk(self, match):
394 '''
397 '''
395 walk recursively through the directory tree, finding all files
398 walk recursively through the directory tree, finding all files
396 matched by the match function
399 matched by the match function
397 '''
400 '''
398 pass
401 pass
399
402
400 def forget(self, ui, match, prefix):
403 def forget(self, ui, match, prefix):
401 return ([], [])
404 return ([], [])
402
405
403 def revert(self, ui, substate, *pats, **opts):
406 def revert(self, ui, substate, *pats, **opts):
404 ui.warn('%s: reverting %s subrepos is unsupported\n' \
407 ui.warn('%s: reverting %s subrepos is unsupported\n' \
405 % (substate[0], substate[2]))
408 % (substate[0], substate[2]))
406 return []
409 return []
407
410
408 class hgsubrepo(abstractsubrepo):
411 class hgsubrepo(abstractsubrepo):
409 def __init__(self, ctx, path, state):
412 def __init__(self, ctx, path, state):
410 self._path = path
413 self._path = path
411 self._state = state
414 self._state = state
412 r = ctx._repo
415 r = ctx._repo
413 root = r.wjoin(path)
416 root = r.wjoin(path)
414 create = False
417 create = False
415 if not os.path.exists(os.path.join(root, '.hg')):
418 if not os.path.exists(os.path.join(root, '.hg')):
416 create = True
419 create = True
417 util.makedirs(root)
420 util.makedirs(root)
418 self._repo = hg.repository(r.baseui, root, create=create)
421 self._repo = hg.repository(r.baseui, root, create=create)
419 for s, k in [('ui', 'commitsubrepos')]:
422 for s, k in [('ui', 'commitsubrepos')]:
420 v = r.ui.config(s, k)
423 v = r.ui.config(s, k)
421 if v:
424 if v:
422 self._repo.ui.setconfig(s, k, v)
425 self._repo.ui.setconfig(s, k, v)
423 self._initrepo(r, state[0], create)
426 self._initrepo(r, state[0], create)
424
427
425 @annotatesubrepoerror
428 @annotatesubrepoerror
426 def _initrepo(self, parentrepo, source, create):
429 def _initrepo(self, parentrepo, source, create):
427 self._repo._subparent = parentrepo
430 self._repo._subparent = parentrepo
428 self._repo._subsource = source
431 self._repo._subsource = source
429
432
430 if create:
433 if create:
431 fp = self._repo.opener("hgrc", "w", text=True)
434 fp = self._repo.opener("hgrc", "w", text=True)
432 fp.write('[paths]\n')
435 fp.write('[paths]\n')
433
436
434 def addpathconfig(key, value):
437 def addpathconfig(key, value):
435 if value:
438 if value:
436 fp.write('%s = %s\n' % (key, value))
439 fp.write('%s = %s\n' % (key, value))
437 self._repo.ui.setconfig('paths', key, value)
440 self._repo.ui.setconfig('paths', key, value)
438
441
439 defpath = _abssource(self._repo, abort=False)
442 defpath = _abssource(self._repo, abort=False)
440 defpushpath = _abssource(self._repo, True, abort=False)
443 defpushpath = _abssource(self._repo, True, abort=False)
441 addpathconfig('default', defpath)
444 addpathconfig('default', defpath)
442 if defpath != defpushpath:
445 if defpath != defpushpath:
443 addpathconfig('default-push', defpushpath)
446 addpathconfig('default-push', defpushpath)
444 fp.close()
447 fp.close()
445
448
446 @annotatesubrepoerror
449 @annotatesubrepoerror
447 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
450 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
448 return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
451 return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
449 os.path.join(prefix, self._path), explicitonly)
452 os.path.join(prefix, self._path), explicitonly)
450
453
451 @annotatesubrepoerror
454 @annotatesubrepoerror
452 def status(self, rev2, **opts):
455 def status(self, rev2, **opts):
453 try:
456 try:
454 rev1 = self._state[1]
457 rev1 = self._state[1]
455 ctx1 = self._repo[rev1]
458 ctx1 = self._repo[rev1]
456 ctx2 = self._repo[rev2]
459 ctx2 = self._repo[rev2]
457 return self._repo.status(ctx1, ctx2, **opts)
460 return self._repo.status(ctx1, ctx2, **opts)
458 except error.RepoLookupError, inst:
461 except error.RepoLookupError, inst:
459 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
462 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
460 % (inst, subrelpath(self)))
463 % (inst, subrelpath(self)))
461 return [], [], [], [], [], [], []
464 return [], [], [], [], [], [], []
462
465
463 @annotatesubrepoerror
466 @annotatesubrepoerror
464 def diff(self, ui, diffopts, node2, match, prefix, **opts):
467 def diff(self, ui, diffopts, node2, match, prefix, **opts):
465 try:
468 try:
466 node1 = node.bin(self._state[1])
469 node1 = node.bin(self._state[1])
467 # We currently expect node2 to come from substate and be
470 # We currently expect node2 to come from substate and be
468 # in hex format
471 # in hex format
469 if node2 is not None:
472 if node2 is not None:
470 node2 = node.bin(node2)
473 node2 = node.bin(node2)
471 cmdutil.diffordiffstat(ui, self._repo, diffopts,
474 cmdutil.diffordiffstat(ui, self._repo, diffopts,
472 node1, node2, match,
475 node1, node2, match,
473 prefix=posixpath.join(prefix, self._path),
476 prefix=posixpath.join(prefix, self._path),
474 listsubrepos=True, **opts)
477 listsubrepos=True, **opts)
475 except error.RepoLookupError, inst:
478 except error.RepoLookupError, inst:
476 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
479 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
477 % (inst, subrelpath(self)))
480 % (inst, subrelpath(self)))
478
481
479 @annotatesubrepoerror
482 @annotatesubrepoerror
480 def archive(self, ui, archiver, prefix, match=None):
483 def archive(self, ui, archiver, prefix, match=None):
481 self._get(self._state + ('hg',))
484 self._get(self._state + ('hg',))
482 abstractsubrepo.archive(self, ui, archiver, prefix, match)
485 abstractsubrepo.archive(self, ui, archiver, prefix, match)
483
486
484 rev = self._state[1]
487 rev = self._state[1]
485 ctx = self._repo[rev]
488 ctx = self._repo[rev]
486 for subpath in ctx.substate:
489 for subpath in ctx.substate:
487 s = subrepo(ctx, subpath)
490 s = subrepo(ctx, subpath)
488 submatch = matchmod.narrowmatcher(subpath, match)
491 submatch = matchmod.narrowmatcher(subpath, match)
489 s.archive(ui, archiver, os.path.join(prefix, self._path), submatch)
492 s.archive(ui, archiver, os.path.join(prefix, self._path), submatch)
490
493
491 @annotatesubrepoerror
494 @annotatesubrepoerror
492 def dirty(self, ignoreupdate=False):
495 def dirty(self, ignoreupdate=False):
493 r = self._state[1]
496 r = self._state[1]
494 if r == '' and not ignoreupdate: # no state recorded
497 if r == '' and not ignoreupdate: # no state recorded
495 return True
498 return True
496 w = self._repo[None]
499 w = self._repo[None]
497 if r != w.p1().hex() and not ignoreupdate:
500 if r != w.p1().hex() and not ignoreupdate:
498 # different version checked out
501 # different version checked out
499 return True
502 return True
500 return w.dirty() # working directory changed
503 return w.dirty() # working directory changed
501
504
502 def basestate(self):
505 def basestate(self):
503 return self._repo['.'].hex()
506 return self._repo['.'].hex()
504
507
505 def checknested(self, path):
508 def checknested(self, path):
506 return self._repo._checknested(self._repo.wjoin(path))
509 return self._repo._checknested(self._repo.wjoin(path))
507
510
508 @annotatesubrepoerror
511 @annotatesubrepoerror
509 def commit(self, text, user, date):
512 def commit(self, text, user, date):
510 # don't bother committing in the subrepo if it's only been
513 # don't bother committing in the subrepo if it's only been
511 # updated
514 # updated
512 if not self.dirty(True):
515 if not self.dirty(True):
513 return self._repo['.'].hex()
516 return self._repo['.'].hex()
514 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
517 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
515 n = self._repo.commit(text, user, date)
518 n = self._repo.commit(text, user, date)
516 if not n:
519 if not n:
517 return self._repo['.'].hex() # different version checked out
520 return self._repo['.'].hex() # different version checked out
518 return node.hex(n)
521 return node.hex(n)
519
522
520 @annotatesubrepoerror
523 @annotatesubrepoerror
521 def remove(self):
524 def remove(self):
522 # we can't fully delete the repository as it may contain
525 # we can't fully delete the repository as it may contain
523 # local-only history
526 # local-only history
524 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
527 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
525 hg.clean(self._repo, node.nullid, False)
528 hg.clean(self._repo, node.nullid, False)
526
529
527 def _get(self, state):
530 def _get(self, state):
528 source, revision, kind = state
531 source, revision, kind = state
529 if revision not in self._repo:
532 if revision not in self._repo:
530 self._repo._subsource = source
533 self._repo._subsource = source
531 srcurl = _abssource(self._repo)
534 srcurl = _abssource(self._repo)
532 other = hg.peer(self._repo, {}, srcurl)
535 other = hg.peer(self._repo, {}, srcurl)
533 if len(self._repo) == 0:
536 if len(self._repo) == 0:
534 self._repo.ui.status(_('cloning subrepo %s from %s\n')
537 self._repo.ui.status(_('cloning subrepo %s from %s\n')
535 % (subrelpath(self), srcurl))
538 % (subrelpath(self), srcurl))
536 parentrepo = self._repo._subparent
539 parentrepo = self._repo._subparent
537 shutil.rmtree(self._repo.path)
540 shutil.rmtree(self._repo.path)
538 other, cloned = hg.clone(self._repo._subparent.baseui, {},
541 other, cloned = hg.clone(self._repo._subparent.baseui, {},
539 other, self._repo.root,
542 other, self._repo.root,
540 update=False)
543 update=False)
541 self._repo = cloned.local()
544 self._repo = cloned.local()
542 self._initrepo(parentrepo, source, create=True)
545 self._initrepo(parentrepo, source, create=True)
543 else:
546 else:
544 self._repo.ui.status(_('pulling subrepo %s from %s\n')
547 self._repo.ui.status(_('pulling subrepo %s from %s\n')
545 % (subrelpath(self), srcurl))
548 % (subrelpath(self), srcurl))
546 self._repo.pull(other)
549 self._repo.pull(other)
547 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
550 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
548 srcurl)
551 srcurl)
549
552
550 @annotatesubrepoerror
553 @annotatesubrepoerror
551 def get(self, state, overwrite=False):
554 def get(self, state, overwrite=False):
552 self._get(state)
555 self._get(state)
553 source, revision, kind = state
556 source, revision, kind = state
554 self._repo.ui.debug("getting subrepo %s\n" % self._path)
557 self._repo.ui.debug("getting subrepo %s\n" % self._path)
555 hg.updaterepo(self._repo, revision, overwrite)
558 hg.updaterepo(self._repo, revision, overwrite)
556
559
557 @annotatesubrepoerror
560 @annotatesubrepoerror
558 def merge(self, state):
561 def merge(self, state):
559 self._get(state)
562 self._get(state)
560 cur = self._repo['.']
563 cur = self._repo['.']
561 dst = self._repo[state[1]]
564 dst = self._repo[state[1]]
562 anc = dst.ancestor(cur)
565 anc = dst.ancestor(cur)
563
566
564 def mergefunc():
567 def mergefunc():
565 if anc == cur and dst.branch() == cur.branch():
568 if anc == cur and dst.branch() == cur.branch():
566 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
569 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
567 hg.update(self._repo, state[1])
570 hg.update(self._repo, state[1])
568 elif anc == dst:
571 elif anc == dst:
569 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
572 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
570 else:
573 else:
571 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
574 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
572 hg.merge(self._repo, state[1], remind=False)
575 hg.merge(self._repo, state[1], remind=False)
573
576
574 wctx = self._repo[None]
577 wctx = self._repo[None]
575 if self.dirty():
578 if self.dirty():
576 if anc != dst:
579 if anc != dst:
577 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
580 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
578 mergefunc()
581 mergefunc()
579 else:
582 else:
580 mergefunc()
583 mergefunc()
581 else:
584 else:
582 mergefunc()
585 mergefunc()
583
586
584 @annotatesubrepoerror
587 @annotatesubrepoerror
585 def push(self, opts):
588 def push(self, opts):
586 force = opts.get('force')
589 force = opts.get('force')
587 newbranch = opts.get('new_branch')
590 newbranch = opts.get('new_branch')
588 ssh = opts.get('ssh')
591 ssh = opts.get('ssh')
589
592
590 # push subrepos depth-first for coherent ordering
593 # push subrepos depth-first for coherent ordering
591 c = self._repo['']
594 c = self._repo['']
592 subs = c.substate # only repos that are committed
595 subs = c.substate # only repos that are committed
593 for s in sorted(subs):
596 for s in sorted(subs):
594 if c.sub(s).push(opts) == 0:
597 if c.sub(s).push(opts) == 0:
595 return False
598 return False
596
599
597 dsturl = _abssource(self._repo, True)
600 dsturl = _abssource(self._repo, True)
598 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
601 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
599 (subrelpath(self), dsturl))
602 (subrelpath(self), dsturl))
600 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
603 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
601 return self._repo.push(other, force, newbranch=newbranch)
604 return self._repo.push(other, force, newbranch=newbranch)
602
605
603 @annotatesubrepoerror
606 @annotatesubrepoerror
604 def outgoing(self, ui, dest, opts):
607 def outgoing(self, ui, dest, opts):
605 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
608 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
606
609
607 @annotatesubrepoerror
610 @annotatesubrepoerror
608 def incoming(self, ui, source, opts):
611 def incoming(self, ui, source, opts):
609 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
612 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
610
613
611 @annotatesubrepoerror
614 @annotatesubrepoerror
612 def files(self):
615 def files(self):
613 rev = self._state[1]
616 rev = self._state[1]
614 ctx = self._repo[rev]
617 ctx = self._repo[rev]
615 return ctx.manifest()
618 return ctx.manifest()
616
619
617 def filedata(self, name):
620 def filedata(self, name):
618 rev = self._state[1]
621 rev = self._state[1]
619 return self._repo[rev][name].data()
622 return self._repo[rev][name].data()
620
623
621 def fileflags(self, name):
624 def fileflags(self, name):
622 rev = self._state[1]
625 rev = self._state[1]
623 ctx = self._repo[rev]
626 ctx = self._repo[rev]
624 return ctx.flags(name)
627 return ctx.flags(name)
625
628
626 def walk(self, match):
629 def walk(self, match):
627 ctx = self._repo[None]
630 ctx = self._repo[None]
628 return ctx.walk(match)
631 return ctx.walk(match)
629
632
630 @annotatesubrepoerror
633 @annotatesubrepoerror
631 def forget(self, ui, match, prefix):
634 def forget(self, ui, match, prefix):
632 return cmdutil.forget(ui, self._repo, match,
635 return cmdutil.forget(ui, self._repo, match,
633 os.path.join(prefix, self._path), True)
636 os.path.join(prefix, self._path), True)
634
637
635 @annotatesubrepoerror
638 @annotatesubrepoerror
636 def revert(self, ui, substate, *pats, **opts):
639 def revert(self, ui, substate, *pats, **opts):
637 # reverting a subrepo is a 2 step process:
640 # reverting a subrepo is a 2 step process:
638 # 1. if the no_backup is not set, revert all modified
641 # 1. if the no_backup is not set, revert all modified
639 # files inside the subrepo
642 # files inside the subrepo
640 # 2. update the subrepo to the revision specified in
643 # 2. update the subrepo to the revision specified in
641 # the corresponding substate dictionary
644 # the corresponding substate dictionary
642 ui.status(_('reverting subrepo %s\n') % substate[0])
645 ui.status(_('reverting subrepo %s\n') % substate[0])
643 if not opts.get('no_backup'):
646 if not opts.get('no_backup'):
644 # Revert all files on the subrepo, creating backups
647 # Revert all files on the subrepo, creating backups
645 # Note that this will not recursively revert subrepos
648 # Note that this will not recursively revert subrepos
646 # We could do it if there was a set:subrepos() predicate
649 # We could do it if there was a set:subrepos() predicate
647 opts = opts.copy()
650 opts = opts.copy()
648 opts['date'] = None
651 opts['date'] = None
649 opts['rev'] = substate[1]
652 opts['rev'] = substate[1]
650
653
651 pats = []
654 pats = []
652 if not opts['all']:
655 if not opts['all']:
653 pats = ['set:modified()']
656 pats = ['set:modified()']
654 self.filerevert(ui, *pats, **opts)
657 self.filerevert(ui, *pats, **opts)
655
658
656 # Update the repo to the revision specified in the given substate
659 # Update the repo to the revision specified in the given substate
657 self.get(substate, overwrite=True)
660 self.get(substate, overwrite=True)
658
661
659 def filerevert(self, ui, *pats, **opts):
662 def filerevert(self, ui, *pats, **opts):
660 ctx = self._repo[opts['rev']]
663 ctx = self._repo[opts['rev']]
661 parents = self._repo.dirstate.parents()
664 parents = self._repo.dirstate.parents()
662 if opts['all']:
665 if opts['all']:
663 pats = ['set:modified()']
666 pats = ['set:modified()']
664 else:
667 else:
665 pats = []
668 pats = []
666 cmdutil.revert(ui, self._repo, ctx, parents, *pats, **opts)
669 cmdutil.revert(ui, self._repo, ctx, parents, *pats, **opts)
667
670
668 class svnsubrepo(abstractsubrepo):
671 class svnsubrepo(abstractsubrepo):
669 def __init__(self, ctx, path, state):
672 def __init__(self, ctx, path, state):
670 self._path = path
673 self._path = path
671 self._state = state
674 self._state = state
672 self._ctx = ctx
675 self._ctx = ctx
673 self._ui = ctx._repo.ui
676 self._ui = ctx._repo.ui
674 self._exe = util.findexe('svn')
677 self._exe = util.findexe('svn')
675 if not self._exe:
678 if not self._exe:
676 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
679 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
677 % self._path)
680 % self._path)
678
681
679 def _svncommand(self, commands, filename='', failok=False):
682 def _svncommand(self, commands, filename='', failok=False):
680 cmd = [self._exe]
683 cmd = [self._exe]
681 extrakw = {}
684 extrakw = {}
682 if not self._ui.interactive():
685 if not self._ui.interactive():
683 # Making stdin be a pipe should prevent svn from behaving
686 # Making stdin be a pipe should prevent svn from behaving
684 # interactively even if we can't pass --non-interactive.
687 # interactively even if we can't pass --non-interactive.
685 extrakw['stdin'] = subprocess.PIPE
688 extrakw['stdin'] = subprocess.PIPE
686 # Starting in svn 1.5 --non-interactive is a global flag
689 # Starting in svn 1.5 --non-interactive is a global flag
687 # instead of being per-command, but we need to support 1.4 so
690 # instead of being per-command, but we need to support 1.4 so
688 # we have to be intelligent about what commands take
691 # we have to be intelligent about what commands take
689 # --non-interactive.
692 # --non-interactive.
690 if commands[0] in ('update', 'checkout', 'commit'):
693 if commands[0] in ('update', 'checkout', 'commit'):
691 cmd.append('--non-interactive')
694 cmd.append('--non-interactive')
692 cmd.extend(commands)
695 cmd.extend(commands)
693 if filename is not None:
696 if filename is not None:
694 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
697 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
695 cmd.append(path)
698 cmd.append(path)
696 env = dict(os.environ)
699 env = dict(os.environ)
697 # Avoid localized output, preserve current locale for everything else.
700 # Avoid localized output, preserve current locale for everything else.
698 lc_all = env.get('LC_ALL')
701 lc_all = env.get('LC_ALL')
699 if lc_all:
702 if lc_all:
700 env['LANG'] = lc_all
703 env['LANG'] = lc_all
701 del env['LC_ALL']
704 del env['LC_ALL']
702 env['LC_MESSAGES'] = 'C'
705 env['LC_MESSAGES'] = 'C'
703 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
706 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
704 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
707 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
705 universal_newlines=True, env=env, **extrakw)
708 universal_newlines=True, env=env, **extrakw)
706 stdout, stderr = p.communicate()
709 stdout, stderr = p.communicate()
707 stderr = stderr.strip()
710 stderr = stderr.strip()
708 if not failok:
711 if not failok:
709 if p.returncode:
712 if p.returncode:
710 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
713 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
711 if stderr:
714 if stderr:
712 self._ui.warn(stderr + '\n')
715 self._ui.warn(stderr + '\n')
713 return stdout, stderr
716 return stdout, stderr
714
717
715 @propertycache
718 @propertycache
716 def _svnversion(self):
719 def _svnversion(self):
717 output, err = self._svncommand(['--version', '--quiet'], filename=None)
720 output, err = self._svncommand(['--version', '--quiet'], filename=None)
718 m = re.search(r'^(\d+)\.(\d+)', output)
721 m = re.search(r'^(\d+)\.(\d+)', output)
719 if not m:
722 if not m:
720 raise util.Abort(_('cannot retrieve svn tool version'))
723 raise util.Abort(_('cannot retrieve svn tool version'))
721 return (int(m.group(1)), int(m.group(2)))
724 return (int(m.group(1)), int(m.group(2)))
722
725
723 def _wcrevs(self):
726 def _wcrevs(self):
724 # Get the working directory revision as well as the last
727 # Get the working directory revision as well as the last
725 # commit revision so we can compare the subrepo state with
728 # commit revision so we can compare the subrepo state with
726 # both. We used to store the working directory one.
729 # both. We used to store the working directory one.
727 output, err = self._svncommand(['info', '--xml'])
730 output, err = self._svncommand(['info', '--xml'])
728 doc = xml.dom.minidom.parseString(output)
731 doc = xml.dom.minidom.parseString(output)
729 entries = doc.getElementsByTagName('entry')
732 entries = doc.getElementsByTagName('entry')
730 lastrev, rev = '0', '0'
733 lastrev, rev = '0', '0'
731 if entries:
734 if entries:
732 rev = str(entries[0].getAttribute('revision')) or '0'
735 rev = str(entries[0].getAttribute('revision')) or '0'
733 commits = entries[0].getElementsByTagName('commit')
736 commits = entries[0].getElementsByTagName('commit')
734 if commits:
737 if commits:
735 lastrev = str(commits[0].getAttribute('revision')) or '0'
738 lastrev = str(commits[0].getAttribute('revision')) or '0'
736 return (lastrev, rev)
739 return (lastrev, rev)
737
740
738 def _wcrev(self):
741 def _wcrev(self):
739 return self._wcrevs()[0]
742 return self._wcrevs()[0]
740
743
741 def _wcchanged(self):
744 def _wcchanged(self):
742 """Return (changes, extchanges, missing) where changes is True
745 """Return (changes, extchanges, missing) where changes is True
743 if the working directory was changed, extchanges is
746 if the working directory was changed, extchanges is
744 True if any of these changes concern an external entry and missing
747 True if any of these changes concern an external entry and missing
745 is True if any change is a missing entry.
748 is True if any change is a missing entry.
746 """
749 """
747 output, err = self._svncommand(['status', '--xml'])
750 output, err = self._svncommand(['status', '--xml'])
748 externals, changes, missing = [], [], []
751 externals, changes, missing = [], [], []
749 doc = xml.dom.minidom.parseString(output)
752 doc = xml.dom.minidom.parseString(output)
750 for e in doc.getElementsByTagName('entry'):
753 for e in doc.getElementsByTagName('entry'):
751 s = e.getElementsByTagName('wc-status')
754 s = e.getElementsByTagName('wc-status')
752 if not s:
755 if not s:
753 continue
756 continue
754 item = s[0].getAttribute('item')
757 item = s[0].getAttribute('item')
755 props = s[0].getAttribute('props')
758 props = s[0].getAttribute('props')
756 path = e.getAttribute('path')
759 path = e.getAttribute('path')
757 if item == 'external':
760 if item == 'external':
758 externals.append(path)
761 externals.append(path)
759 elif item == 'missing':
762 elif item == 'missing':
760 missing.append(path)
763 missing.append(path)
761 if (item not in ('', 'normal', 'unversioned', 'external')
764 if (item not in ('', 'normal', 'unversioned', 'external')
762 or props not in ('', 'none', 'normal')):
765 or props not in ('', 'none', 'normal')):
763 changes.append(path)
766 changes.append(path)
764 for path in changes:
767 for path in changes:
765 for ext in externals:
768 for ext in externals:
766 if path == ext or path.startswith(ext + os.sep):
769 if path == ext or path.startswith(ext + os.sep):
767 return True, True, bool(missing)
770 return True, True, bool(missing)
768 return bool(changes), False, bool(missing)
771 return bool(changes), False, bool(missing)
769
772
770 def dirty(self, ignoreupdate=False):
773 def dirty(self, ignoreupdate=False):
771 if not self._wcchanged()[0]:
774 if not self._wcchanged()[0]:
772 if self._state[1] in self._wcrevs() or ignoreupdate:
775 if self._state[1] in self._wcrevs() or ignoreupdate:
773 return False
776 return False
774 return True
777 return True
775
778
776 def basestate(self):
779 def basestate(self):
777 lastrev, rev = self._wcrevs()
780 lastrev, rev = self._wcrevs()
778 if lastrev != rev:
781 if lastrev != rev:
779 # Last committed rev is not the same than rev. We would
782 # Last committed rev is not the same than rev. We would
780 # like to take lastrev but we do not know if the subrepo
783 # like to take lastrev but we do not know if the subrepo
781 # URL exists at lastrev. Test it and fallback to rev it
784 # URL exists at lastrev. Test it and fallback to rev it
782 # is not there.
785 # is not there.
783 try:
786 try:
784 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
787 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
785 return lastrev
788 return lastrev
786 except error.Abort:
789 except error.Abort:
787 pass
790 pass
788 return rev
791 return rev
789
792
790 @annotatesubrepoerror
793 @annotatesubrepoerror
791 def commit(self, text, user, date):
794 def commit(self, text, user, date):
792 # user and date are out of our hands since svn is centralized
795 # user and date are out of our hands since svn is centralized
793 changed, extchanged, missing = self._wcchanged()
796 changed, extchanged, missing = self._wcchanged()
794 if not changed:
797 if not changed:
795 return self.basestate()
798 return self.basestate()
796 if extchanged:
799 if extchanged:
797 # Do not try to commit externals
800 # Do not try to commit externals
798 raise util.Abort(_('cannot commit svn externals'))
801 raise util.Abort(_('cannot commit svn externals'))
799 if missing:
802 if missing:
800 # svn can commit with missing entries but aborting like hg
803 # svn can commit with missing entries but aborting like hg
801 # seems a better approach.
804 # seems a better approach.
802 raise util.Abort(_('cannot commit missing svn entries'))
805 raise util.Abort(_('cannot commit missing svn entries'))
803 commitinfo, err = self._svncommand(['commit', '-m', text])
806 commitinfo, err = self._svncommand(['commit', '-m', text])
804 self._ui.status(commitinfo)
807 self._ui.status(commitinfo)
805 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
808 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
806 if not newrev:
809 if not newrev:
807 if not commitinfo.strip():
810 if not commitinfo.strip():
808 # Sometimes, our definition of "changed" differs from
811 # Sometimes, our definition of "changed" differs from
809 # svn one. For instance, svn ignores missing files
812 # svn one. For instance, svn ignores missing files
810 # when committing. If there are only missing files, no
813 # when committing. If there are only missing files, no
811 # commit is made, no output and no error code.
814 # commit is made, no output and no error code.
812 raise util.Abort(_('failed to commit svn changes'))
815 raise util.Abort(_('failed to commit svn changes'))
813 raise util.Abort(commitinfo.splitlines()[-1])
816 raise util.Abort(commitinfo.splitlines()[-1])
814 newrev = newrev.groups()[0]
817 newrev = newrev.groups()[0]
815 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
818 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
816 return newrev
819 return newrev
817
820
818 @annotatesubrepoerror
821 @annotatesubrepoerror
819 def remove(self):
822 def remove(self):
820 if self.dirty():
823 if self.dirty():
821 self._ui.warn(_('not removing repo %s because '
824 self._ui.warn(_('not removing repo %s because '
822 'it has changes.\n' % self._path))
825 'it has changes.\n' % self._path))
823 return
826 return
824 self._ui.note(_('removing subrepo %s\n') % self._path)
827 self._ui.note(_('removing subrepo %s\n') % self._path)
825
828
826 def onerror(function, path, excinfo):
829 def onerror(function, path, excinfo):
827 if function is not os.remove:
830 if function is not os.remove:
828 raise
831 raise
829 # read-only files cannot be unlinked under Windows
832 # read-only files cannot be unlinked under Windows
830 s = os.stat(path)
833 s = os.stat(path)
831 if (s.st_mode & stat.S_IWRITE) != 0:
834 if (s.st_mode & stat.S_IWRITE) != 0:
832 raise
835 raise
833 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
836 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
834 os.remove(path)
837 os.remove(path)
835
838
836 path = self._ctx._repo.wjoin(self._path)
839 path = self._ctx._repo.wjoin(self._path)
837 shutil.rmtree(path, onerror=onerror)
840 shutil.rmtree(path, onerror=onerror)
838 try:
841 try:
839 os.removedirs(os.path.dirname(path))
842 os.removedirs(os.path.dirname(path))
840 except OSError:
843 except OSError:
841 pass
844 pass
842
845
843 @annotatesubrepoerror
846 @annotatesubrepoerror
844 def get(self, state, overwrite=False):
847 def get(self, state, overwrite=False):
845 if overwrite:
848 if overwrite:
846 self._svncommand(['revert', '--recursive'])
849 self._svncommand(['revert', '--recursive'])
847 args = ['checkout']
850 args = ['checkout']
848 if self._svnversion >= (1, 5):
851 if self._svnversion >= (1, 5):
849 args.append('--force')
852 args.append('--force')
850 # The revision must be specified at the end of the URL to properly
853 # The revision must be specified at the end of the URL to properly
851 # update to a directory which has since been deleted and recreated.
854 # update to a directory which has since been deleted and recreated.
852 args.append('%s@%s' % (state[0], state[1]))
855 args.append('%s@%s' % (state[0], state[1]))
853 status, err = self._svncommand(args, failok=True)
856 status, err = self._svncommand(args, failok=True)
854 if not re.search('Checked out revision [0-9]+.', status):
857 if not re.search('Checked out revision [0-9]+.', status):
855 if ('is already a working copy for a different URL' in err
858 if ('is already a working copy for a different URL' in err
856 and (self._wcchanged()[:2] == (False, False))):
859 and (self._wcchanged()[:2] == (False, False))):
857 # obstructed but clean working copy, so just blow it away.
860 # obstructed but clean working copy, so just blow it away.
858 self.remove()
861 self.remove()
859 self.get(state, overwrite=False)
862 self.get(state, overwrite=False)
860 return
863 return
861 raise util.Abort((status or err).splitlines()[-1])
864 raise util.Abort((status or err).splitlines()[-1])
862 self._ui.status(status)
865 self._ui.status(status)
863
866
864 @annotatesubrepoerror
867 @annotatesubrepoerror
865 def merge(self, state):
868 def merge(self, state):
866 old = self._state[1]
869 old = self._state[1]
867 new = state[1]
870 new = state[1]
868 wcrev = self._wcrev()
871 wcrev = self._wcrev()
869 if new != wcrev:
872 if new != wcrev:
870 dirty = old == wcrev or self._wcchanged()[0]
873 dirty = old == wcrev or self._wcchanged()[0]
871 if _updateprompt(self._ui, self, dirty, wcrev, new):
874 if _updateprompt(self._ui, self, dirty, wcrev, new):
872 self.get(state, False)
875 self.get(state, False)
873
876
874 def push(self, opts):
877 def push(self, opts):
875 # push is a no-op for SVN
878 # push is a no-op for SVN
876 return True
879 return True
877
880
878 @annotatesubrepoerror
881 @annotatesubrepoerror
879 def files(self):
882 def files(self):
880 output = self._svncommand(['list', '--recursive', '--xml'])[0]
883 output = self._svncommand(['list', '--recursive', '--xml'])[0]
881 doc = xml.dom.minidom.parseString(output)
884 doc = xml.dom.minidom.parseString(output)
882 paths = []
885 paths = []
883 for e in doc.getElementsByTagName('entry'):
886 for e in doc.getElementsByTagName('entry'):
884 kind = str(e.getAttribute('kind'))
887 kind = str(e.getAttribute('kind'))
885 if kind != 'file':
888 if kind != 'file':
886 continue
889 continue
887 name = ''.join(c.data for c
890 name = ''.join(c.data for c
888 in e.getElementsByTagName('name')[0].childNodes
891 in e.getElementsByTagName('name')[0].childNodes
889 if c.nodeType == c.TEXT_NODE)
892 if c.nodeType == c.TEXT_NODE)
890 paths.append(name.encode('utf-8'))
893 paths.append(name.encode('utf-8'))
891 return paths
894 return paths
892
895
893 def filedata(self, name):
896 def filedata(self, name):
894 return self._svncommand(['cat'], name)[0]
897 return self._svncommand(['cat'], name)[0]
895
898
896
899
897 class gitsubrepo(abstractsubrepo):
900 class gitsubrepo(abstractsubrepo):
898 def __init__(self, ctx, path, state):
901 def __init__(self, ctx, path, state):
899 self._state = state
902 self._state = state
900 self._ctx = ctx
903 self._ctx = ctx
901 self._path = path
904 self._path = path
902 self._relpath = os.path.join(reporelpath(ctx._repo), path)
905 self._relpath = os.path.join(reporelpath(ctx._repo), path)
903 self._abspath = ctx._repo.wjoin(path)
906 self._abspath = ctx._repo.wjoin(path)
904 self._subparent = ctx._repo
907 self._subparent = ctx._repo
905 self._ui = ctx._repo.ui
908 self._ui = ctx._repo.ui
906 self._ensuregit()
909 self._ensuregit()
907
910
908 def _ensuregit(self):
911 def _ensuregit(self):
909 try:
912 try:
910 self._gitexecutable = 'git'
913 self._gitexecutable = 'git'
911 out, err = self._gitnodir(['--version'])
914 out, err = self._gitnodir(['--version'])
912 except OSError, e:
915 except OSError, e:
913 if e.errno != 2 or os.name != 'nt':
916 if e.errno != 2 or os.name != 'nt':
914 raise
917 raise
915 self._gitexecutable = 'git.cmd'
918 self._gitexecutable = 'git.cmd'
916 out, err = self._gitnodir(['--version'])
919 out, err = self._gitnodir(['--version'])
917 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
920 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
918 if not m:
921 if not m:
919 self._ui.warn(_('cannot retrieve git version'))
922 self._ui.warn(_('cannot retrieve git version'))
920 return
923 return
921 version = (int(m.group(1)), m.group(2), m.group(3))
924 version = (int(m.group(1)), m.group(2), m.group(3))
922 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
925 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
923 # despite the docstring comment. For now, error on 1.4.0, warn on
926 # despite the docstring comment. For now, error on 1.4.0, warn on
924 # 1.5.0 but attempt to continue.
927 # 1.5.0 but attempt to continue.
925 if version < (1, 5, 0):
928 if version < (1, 5, 0):
926 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
929 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
927 elif version < (1, 6, 0):
930 elif version < (1, 6, 0):
928 self._ui.warn(_('git subrepo requires at least 1.6.0 or later'))
931 self._ui.warn(_('git subrepo requires at least 1.6.0 or later'))
929
932
930 def _gitcommand(self, commands, env=None, stream=False):
933 def _gitcommand(self, commands, env=None, stream=False):
931 return self._gitdir(commands, env=env, stream=stream)[0]
934 return self._gitdir(commands, env=env, stream=stream)[0]
932
935
933 def _gitdir(self, commands, env=None, stream=False):
936 def _gitdir(self, commands, env=None, stream=False):
934 return self._gitnodir(commands, env=env, stream=stream,
937 return self._gitnodir(commands, env=env, stream=stream,
935 cwd=self._abspath)
938 cwd=self._abspath)
936
939
937 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
940 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
938 """Calls the git command
941 """Calls the git command
939
942
940 The methods tries to call the git command. versions prior to 1.6.0
943 The methods tries to call the git command. versions prior to 1.6.0
941 are not supported and very probably fail.
944 are not supported and very probably fail.
942 """
945 """
943 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
946 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
944 # unless ui.quiet is set, print git's stderr,
947 # unless ui.quiet is set, print git's stderr,
945 # which is mostly progress and useful info
948 # which is mostly progress and useful info
946 errpipe = None
949 errpipe = None
947 if self._ui.quiet:
950 if self._ui.quiet:
948 errpipe = open(os.devnull, 'w')
951 errpipe = open(os.devnull, 'w')
949 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
952 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
950 cwd=cwd, env=env, close_fds=util.closefds,
953 cwd=cwd, env=env, close_fds=util.closefds,
951 stdout=subprocess.PIPE, stderr=errpipe)
954 stdout=subprocess.PIPE, stderr=errpipe)
952 if stream:
955 if stream:
953 return p.stdout, None
956 return p.stdout, None
954
957
955 retdata = p.stdout.read().strip()
958 retdata = p.stdout.read().strip()
956 # wait for the child to exit to avoid race condition.
959 # wait for the child to exit to avoid race condition.
957 p.wait()
960 p.wait()
958
961
959 if p.returncode != 0 and p.returncode != 1:
962 if p.returncode != 0 and p.returncode != 1:
960 # there are certain error codes that are ok
963 # there are certain error codes that are ok
961 command = commands[0]
964 command = commands[0]
962 if command in ('cat-file', 'symbolic-ref'):
965 if command in ('cat-file', 'symbolic-ref'):
963 return retdata, p.returncode
966 return retdata, p.returncode
964 # for all others, abort
967 # for all others, abort
965 raise util.Abort('git %s error %d in %s' %
968 raise util.Abort('git %s error %d in %s' %
966 (command, p.returncode, self._relpath))
969 (command, p.returncode, self._relpath))
967
970
968 return retdata, p.returncode
971 return retdata, p.returncode
969
972
970 def _gitmissing(self):
973 def _gitmissing(self):
971 return not os.path.exists(os.path.join(self._abspath, '.git'))
974 return not os.path.exists(os.path.join(self._abspath, '.git'))
972
975
973 def _gitstate(self):
976 def _gitstate(self):
974 return self._gitcommand(['rev-parse', 'HEAD'])
977 return self._gitcommand(['rev-parse', 'HEAD'])
975
978
976 def _gitcurrentbranch(self):
979 def _gitcurrentbranch(self):
977 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
980 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
978 if err:
981 if err:
979 current = None
982 current = None
980 return current
983 return current
981
984
982 def _gitremote(self, remote):
985 def _gitremote(self, remote):
983 out = self._gitcommand(['remote', 'show', '-n', remote])
986 out = self._gitcommand(['remote', 'show', '-n', remote])
984 line = out.split('\n')[1]
987 line = out.split('\n')[1]
985 i = line.index('URL: ') + len('URL: ')
988 i = line.index('URL: ') + len('URL: ')
986 return line[i:]
989 return line[i:]
987
990
988 def _githavelocally(self, revision):
991 def _githavelocally(self, revision):
989 out, code = self._gitdir(['cat-file', '-e', revision])
992 out, code = self._gitdir(['cat-file', '-e', revision])
990 return code == 0
993 return code == 0
991
994
992 def _gitisancestor(self, r1, r2):
995 def _gitisancestor(self, r1, r2):
993 base = self._gitcommand(['merge-base', r1, r2])
996 base = self._gitcommand(['merge-base', r1, r2])
994 return base == r1
997 return base == r1
995
998
996 def _gitisbare(self):
999 def _gitisbare(self):
997 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1000 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
998
1001
999 def _gitupdatestat(self):
1002 def _gitupdatestat(self):
1000 """This must be run before git diff-index.
1003 """This must be run before git diff-index.
1001 diff-index only looks at changes to file stat;
1004 diff-index only looks at changes to file stat;
1002 this command looks at file contents and updates the stat."""
1005 this command looks at file contents and updates the stat."""
1003 self._gitcommand(['update-index', '-q', '--refresh'])
1006 self._gitcommand(['update-index', '-q', '--refresh'])
1004
1007
1005 def _gitbranchmap(self):
1008 def _gitbranchmap(self):
1006 '''returns 2 things:
1009 '''returns 2 things:
1007 a map from git branch to revision
1010 a map from git branch to revision
1008 a map from revision to branches'''
1011 a map from revision to branches'''
1009 branch2rev = {}
1012 branch2rev = {}
1010 rev2branch = {}
1013 rev2branch = {}
1011
1014
1012 out = self._gitcommand(['for-each-ref', '--format',
1015 out = self._gitcommand(['for-each-ref', '--format',
1013 '%(objectname) %(refname)'])
1016 '%(objectname) %(refname)'])
1014 for line in out.split('\n'):
1017 for line in out.split('\n'):
1015 revision, ref = line.split(' ')
1018 revision, ref = line.split(' ')
1016 if (not ref.startswith('refs/heads/') and
1019 if (not ref.startswith('refs/heads/') and
1017 not ref.startswith('refs/remotes/')):
1020 not ref.startswith('refs/remotes/')):
1018 continue
1021 continue
1019 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1022 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1020 continue # ignore remote/HEAD redirects
1023 continue # ignore remote/HEAD redirects
1021 branch2rev[ref] = revision
1024 branch2rev[ref] = revision
1022 rev2branch.setdefault(revision, []).append(ref)
1025 rev2branch.setdefault(revision, []).append(ref)
1023 return branch2rev, rev2branch
1026 return branch2rev, rev2branch
1024
1027
1025 def _gittracking(self, branches):
1028 def _gittracking(self, branches):
1026 'return map of remote branch to local tracking branch'
1029 'return map of remote branch to local tracking branch'
1027 # assumes no more than one local tracking branch for each remote
1030 # assumes no more than one local tracking branch for each remote
1028 tracking = {}
1031 tracking = {}
1029 for b in branches:
1032 for b in branches:
1030 if b.startswith('refs/remotes/'):
1033 if b.startswith('refs/remotes/'):
1031 continue
1034 continue
1032 bname = b.split('/', 2)[2]
1035 bname = b.split('/', 2)[2]
1033 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1036 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1034 if remote:
1037 if remote:
1035 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1038 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1036 tracking['refs/remotes/%s/%s' %
1039 tracking['refs/remotes/%s/%s' %
1037 (remote, ref.split('/', 2)[2])] = b
1040 (remote, ref.split('/', 2)[2])] = b
1038 return tracking
1041 return tracking
1039
1042
1040 def _abssource(self, source):
1043 def _abssource(self, source):
1041 if '://' not in source:
1044 if '://' not in source:
1042 # recognize the scp syntax as an absolute source
1045 # recognize the scp syntax as an absolute source
1043 colon = source.find(':')
1046 colon = source.find(':')
1044 if colon != -1 and '/' not in source[:colon]:
1047 if colon != -1 and '/' not in source[:colon]:
1045 return source
1048 return source
1046 self._subsource = source
1049 self._subsource = source
1047 return _abssource(self)
1050 return _abssource(self)
1048
1051
1049 def _fetch(self, source, revision):
1052 def _fetch(self, source, revision):
1050 if self._gitmissing():
1053 if self._gitmissing():
1051 source = self._abssource(source)
1054 source = self._abssource(source)
1052 self._ui.status(_('cloning subrepo %s from %s\n') %
1055 self._ui.status(_('cloning subrepo %s from %s\n') %
1053 (self._relpath, source))
1056 (self._relpath, source))
1054 self._gitnodir(['clone', source, self._abspath])
1057 self._gitnodir(['clone', source, self._abspath])
1055 if self._githavelocally(revision):
1058 if self._githavelocally(revision):
1056 return
1059 return
1057 self._ui.status(_('pulling subrepo %s from %s\n') %
1060 self._ui.status(_('pulling subrepo %s from %s\n') %
1058 (self._relpath, self._gitremote('origin')))
1061 (self._relpath, self._gitremote('origin')))
1059 # try only origin: the originally cloned repo
1062 # try only origin: the originally cloned repo
1060 self._gitcommand(['fetch'])
1063 self._gitcommand(['fetch'])
1061 if not self._githavelocally(revision):
1064 if not self._githavelocally(revision):
1062 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1065 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1063 (revision, self._relpath))
1066 (revision, self._relpath))
1064
1067
1065 @annotatesubrepoerror
1068 @annotatesubrepoerror
1066 def dirty(self, ignoreupdate=False):
1069 def dirty(self, ignoreupdate=False):
1067 if self._gitmissing():
1070 if self._gitmissing():
1068 return self._state[1] != ''
1071 return self._state[1] != ''
1069 if self._gitisbare():
1072 if self._gitisbare():
1070 return True
1073 return True
1071 if not ignoreupdate and self._state[1] != self._gitstate():
1074 if not ignoreupdate and self._state[1] != self._gitstate():
1072 # different version checked out
1075 # different version checked out
1073 return True
1076 return True
1074 # check for staged changes or modified files; ignore untracked files
1077 # check for staged changes or modified files; ignore untracked files
1075 self._gitupdatestat()
1078 self._gitupdatestat()
1076 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1079 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1077 return code == 1
1080 return code == 1
1078
1081
1079 def basestate(self):
1082 def basestate(self):
1080 return self._gitstate()
1083 return self._gitstate()
1081
1084
1082 @annotatesubrepoerror
1085 @annotatesubrepoerror
1083 def get(self, state, overwrite=False):
1086 def get(self, state, overwrite=False):
1084 source, revision, kind = state
1087 source, revision, kind = state
1085 if not revision:
1088 if not revision:
1086 self.remove()
1089 self.remove()
1087 return
1090 return
1088 self._fetch(source, revision)
1091 self._fetch(source, revision)
1089 # if the repo was set to be bare, unbare it
1092 # if the repo was set to be bare, unbare it
1090 if self._gitisbare():
1093 if self._gitisbare():
1091 self._gitcommand(['config', 'core.bare', 'false'])
1094 self._gitcommand(['config', 'core.bare', 'false'])
1092 if self._gitstate() == revision:
1095 if self._gitstate() == revision:
1093 self._gitcommand(['reset', '--hard', 'HEAD'])
1096 self._gitcommand(['reset', '--hard', 'HEAD'])
1094 return
1097 return
1095 elif self._gitstate() == revision:
1098 elif self._gitstate() == revision:
1096 if overwrite:
1099 if overwrite:
1097 # first reset the index to unmark new files for commit, because
1100 # first reset the index to unmark new files for commit, because
1098 # reset --hard will otherwise throw away files added for commit,
1101 # reset --hard will otherwise throw away files added for commit,
1099 # not just unmark them.
1102 # not just unmark them.
1100 self._gitcommand(['reset', 'HEAD'])
1103 self._gitcommand(['reset', 'HEAD'])
1101 self._gitcommand(['reset', '--hard', 'HEAD'])
1104 self._gitcommand(['reset', '--hard', 'HEAD'])
1102 return
1105 return
1103 branch2rev, rev2branch = self._gitbranchmap()
1106 branch2rev, rev2branch = self._gitbranchmap()
1104
1107
1105 def checkout(args):
1108 def checkout(args):
1106 cmd = ['checkout']
1109 cmd = ['checkout']
1107 if overwrite:
1110 if overwrite:
1108 # first reset the index to unmark new files for commit, because
1111 # first reset the index to unmark new files for commit, because
1109 # the -f option will otherwise throw away files added for
1112 # the -f option will otherwise throw away files added for
1110 # commit, not just unmark them.
1113 # commit, not just unmark them.
1111 self._gitcommand(['reset', 'HEAD'])
1114 self._gitcommand(['reset', 'HEAD'])
1112 cmd.append('-f')
1115 cmd.append('-f')
1113 self._gitcommand(cmd + args)
1116 self._gitcommand(cmd + args)
1114
1117
1115 def rawcheckout():
1118 def rawcheckout():
1116 # no branch to checkout, check it out with no branch
1119 # no branch to checkout, check it out with no branch
1117 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1120 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1118 self._relpath)
1121 self._relpath)
1119 self._ui.warn(_('check out a git branch if you intend '
1122 self._ui.warn(_('check out a git branch if you intend '
1120 'to make changes\n'))
1123 'to make changes\n'))
1121 checkout(['-q', revision])
1124 checkout(['-q', revision])
1122
1125
1123 if revision not in rev2branch:
1126 if revision not in rev2branch:
1124 rawcheckout()
1127 rawcheckout()
1125 return
1128 return
1126 branches = rev2branch[revision]
1129 branches = rev2branch[revision]
1127 firstlocalbranch = None
1130 firstlocalbranch = None
1128 for b in branches:
1131 for b in branches:
1129 if b == 'refs/heads/master':
1132 if b == 'refs/heads/master':
1130 # master trumps all other branches
1133 # master trumps all other branches
1131 checkout(['refs/heads/master'])
1134 checkout(['refs/heads/master'])
1132 return
1135 return
1133 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1136 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1134 firstlocalbranch = b
1137 firstlocalbranch = b
1135 if firstlocalbranch:
1138 if firstlocalbranch:
1136 checkout([firstlocalbranch])
1139 checkout([firstlocalbranch])
1137 return
1140 return
1138
1141
1139 tracking = self._gittracking(branch2rev.keys())
1142 tracking = self._gittracking(branch2rev.keys())
1140 # choose a remote branch already tracked if possible
1143 # choose a remote branch already tracked if possible
1141 remote = branches[0]
1144 remote = branches[0]
1142 if remote not in tracking:
1145 if remote not in tracking:
1143 for b in branches:
1146 for b in branches:
1144 if b in tracking:
1147 if b in tracking:
1145 remote = b
1148 remote = b
1146 break
1149 break
1147
1150
1148 if remote not in tracking:
1151 if remote not in tracking:
1149 # create a new local tracking branch
1152 # create a new local tracking branch
1150 local = remote.split('/', 2)[2]
1153 local = remote.split('/', 2)[2]
1151 checkout(['-b', local, remote])
1154 checkout(['-b', local, remote])
1152 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1155 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1153 # When updating to a tracked remote branch,
1156 # When updating to a tracked remote branch,
1154 # if the local tracking branch is downstream of it,
1157 # if the local tracking branch is downstream of it,
1155 # a normal `git pull` would have performed a "fast-forward merge"
1158 # a normal `git pull` would have performed a "fast-forward merge"
1156 # which is equivalent to updating the local branch to the remote.
1159 # which is equivalent to updating the local branch to the remote.
1157 # Since we are only looking at branching at update, we need to
1160 # Since we are only looking at branching at update, we need to
1158 # detect this situation and perform this action lazily.
1161 # detect this situation and perform this action lazily.
1159 if tracking[remote] != self._gitcurrentbranch():
1162 if tracking[remote] != self._gitcurrentbranch():
1160 checkout([tracking[remote]])
1163 checkout([tracking[remote]])
1161 self._gitcommand(['merge', '--ff', remote])
1164 self._gitcommand(['merge', '--ff', remote])
1162 else:
1165 else:
1163 # a real merge would be required, just checkout the revision
1166 # a real merge would be required, just checkout the revision
1164 rawcheckout()
1167 rawcheckout()
1165
1168
1166 @annotatesubrepoerror
1169 @annotatesubrepoerror
1167 def commit(self, text, user, date):
1170 def commit(self, text, user, date):
1168 if self._gitmissing():
1171 if self._gitmissing():
1169 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1172 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1170 cmd = ['commit', '-a', '-m', text]
1173 cmd = ['commit', '-a', '-m', text]
1171 env = os.environ.copy()
1174 env = os.environ.copy()
1172 if user:
1175 if user:
1173 cmd += ['--author', user]
1176 cmd += ['--author', user]
1174 if date:
1177 if date:
1175 # git's date parser silently ignores when seconds < 1e9
1178 # git's date parser silently ignores when seconds < 1e9
1176 # convert to ISO8601
1179 # convert to ISO8601
1177 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1180 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1178 '%Y-%m-%dT%H:%M:%S %1%2')
1181 '%Y-%m-%dT%H:%M:%S %1%2')
1179 self._gitcommand(cmd, env=env)
1182 self._gitcommand(cmd, env=env)
1180 # make sure commit works otherwise HEAD might not exist under certain
1183 # make sure commit works otherwise HEAD might not exist under certain
1181 # circumstances
1184 # circumstances
1182 return self._gitstate()
1185 return self._gitstate()
1183
1186
1184 @annotatesubrepoerror
1187 @annotatesubrepoerror
1185 def merge(self, state):
1188 def merge(self, state):
1186 source, revision, kind = state
1189 source, revision, kind = state
1187 self._fetch(source, revision)
1190 self._fetch(source, revision)
1188 base = self._gitcommand(['merge-base', revision, self._state[1]])
1191 base = self._gitcommand(['merge-base', revision, self._state[1]])
1189 self._gitupdatestat()
1192 self._gitupdatestat()
1190 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1193 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1191
1194
1192 def mergefunc():
1195 def mergefunc():
1193 if base == revision:
1196 if base == revision:
1194 self.get(state) # fast forward merge
1197 self.get(state) # fast forward merge
1195 elif base != self._state[1]:
1198 elif base != self._state[1]:
1196 self._gitcommand(['merge', '--no-commit', revision])
1199 self._gitcommand(['merge', '--no-commit', revision])
1197
1200
1198 if self.dirty():
1201 if self.dirty():
1199 if self._gitstate() != revision:
1202 if self._gitstate() != revision:
1200 dirty = self._gitstate() == self._state[1] or code != 0
1203 dirty = self._gitstate() == self._state[1] or code != 0
1201 if _updateprompt(self._ui, self, dirty,
1204 if _updateprompt(self._ui, self, dirty,
1202 self._state[1][:7], revision[:7]):
1205 self._state[1][:7], revision[:7]):
1203 mergefunc()
1206 mergefunc()
1204 else:
1207 else:
1205 mergefunc()
1208 mergefunc()
1206
1209
1207 @annotatesubrepoerror
1210 @annotatesubrepoerror
1208 def push(self, opts):
1211 def push(self, opts):
1209 force = opts.get('force')
1212 force = opts.get('force')
1210
1213
1211 if not self._state[1]:
1214 if not self._state[1]:
1212 return True
1215 return True
1213 if self._gitmissing():
1216 if self._gitmissing():
1214 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1217 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1215 # if a branch in origin contains the revision, nothing to do
1218 # if a branch in origin contains the revision, nothing to do
1216 branch2rev, rev2branch = self._gitbranchmap()
1219 branch2rev, rev2branch = self._gitbranchmap()
1217 if self._state[1] in rev2branch:
1220 if self._state[1] in rev2branch:
1218 for b in rev2branch[self._state[1]]:
1221 for b in rev2branch[self._state[1]]:
1219 if b.startswith('refs/remotes/origin/'):
1222 if b.startswith('refs/remotes/origin/'):
1220 return True
1223 return True
1221 for b, revision in branch2rev.iteritems():
1224 for b, revision in branch2rev.iteritems():
1222 if b.startswith('refs/remotes/origin/'):
1225 if b.startswith('refs/remotes/origin/'):
1223 if self._gitisancestor(self._state[1], revision):
1226 if self._gitisancestor(self._state[1], revision):
1224 return True
1227 return True
1225 # otherwise, try to push the currently checked out branch
1228 # otherwise, try to push the currently checked out branch
1226 cmd = ['push']
1229 cmd = ['push']
1227 if force:
1230 if force:
1228 cmd.append('--force')
1231 cmd.append('--force')
1229
1232
1230 current = self._gitcurrentbranch()
1233 current = self._gitcurrentbranch()
1231 if current:
1234 if current:
1232 # determine if the current branch is even useful
1235 # determine if the current branch is even useful
1233 if not self._gitisancestor(self._state[1], current):
1236 if not self._gitisancestor(self._state[1], current):
1234 self._ui.warn(_('unrelated git branch checked out '
1237 self._ui.warn(_('unrelated git branch checked out '
1235 'in subrepo %s\n') % self._relpath)
1238 'in subrepo %s\n') % self._relpath)
1236 return False
1239 return False
1237 self._ui.status(_('pushing branch %s of subrepo %s\n') %
1240 self._ui.status(_('pushing branch %s of subrepo %s\n') %
1238 (current.split('/', 2)[2], self._relpath))
1241 (current.split('/', 2)[2], self._relpath))
1239 self._gitcommand(cmd + ['origin', current])
1242 self._gitcommand(cmd + ['origin', current])
1240 return True
1243 return True
1241 else:
1244 else:
1242 self._ui.warn(_('no branch checked out in subrepo %s\n'
1245 self._ui.warn(_('no branch checked out in subrepo %s\n'
1243 'cannot push revision %s\n') %
1246 'cannot push revision %s\n') %
1244 (self._relpath, self._state[1]))
1247 (self._relpath, self._state[1]))
1245 return False
1248 return False
1246
1249
1247 @annotatesubrepoerror
1250 @annotatesubrepoerror
1248 def remove(self):
1251 def remove(self):
1249 if self._gitmissing():
1252 if self._gitmissing():
1250 return
1253 return
1251 if self.dirty():
1254 if self.dirty():
1252 self._ui.warn(_('not removing repo %s because '
1255 self._ui.warn(_('not removing repo %s because '
1253 'it has changes.\n') % self._relpath)
1256 'it has changes.\n') % self._relpath)
1254 return
1257 return
1255 # we can't fully delete the repository as it may contain
1258 # we can't fully delete the repository as it may contain
1256 # local-only history
1259 # local-only history
1257 self._ui.note(_('removing subrepo %s\n') % self._relpath)
1260 self._ui.note(_('removing subrepo %s\n') % self._relpath)
1258 self._gitcommand(['config', 'core.bare', 'true'])
1261 self._gitcommand(['config', 'core.bare', 'true'])
1259 for f in os.listdir(self._abspath):
1262 for f in os.listdir(self._abspath):
1260 if f == '.git':
1263 if f == '.git':
1261 continue
1264 continue
1262 path = os.path.join(self._abspath, f)
1265 path = os.path.join(self._abspath, f)
1263 if os.path.isdir(path) and not os.path.islink(path):
1266 if os.path.isdir(path) and not os.path.islink(path):
1264 shutil.rmtree(path)
1267 shutil.rmtree(path)
1265 else:
1268 else:
1266 os.remove(path)
1269 os.remove(path)
1267
1270
1268 def archive(self, ui, archiver, prefix, match=None):
1271 def archive(self, ui, archiver, prefix, match=None):
1269 source, revision = self._state
1272 source, revision = self._state
1270 if not revision:
1273 if not revision:
1271 return
1274 return
1272 self._fetch(source, revision)
1275 self._fetch(source, revision)
1273
1276
1274 # Parse git's native archive command.
1277 # Parse git's native archive command.
1275 # This should be much faster than manually traversing the trees
1278 # This should be much faster than manually traversing the trees
1276 # and objects with many subprocess calls.
1279 # and objects with many subprocess calls.
1277 tarstream = self._gitcommand(['archive', revision], stream=True)
1280 tarstream = self._gitcommand(['archive', revision], stream=True)
1278 tar = tarfile.open(fileobj=tarstream, mode='r|')
1281 tar = tarfile.open(fileobj=tarstream, mode='r|')
1279 relpath = subrelpath(self)
1282 relpath = subrelpath(self)
1280 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1283 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1281 for i, info in enumerate(tar):
1284 for i, info in enumerate(tar):
1282 if info.isdir():
1285 if info.isdir():
1283 continue
1286 continue
1284 if match and not match(info.name):
1287 if match and not match(info.name):
1285 continue
1288 continue
1286 if info.issym():
1289 if info.issym():
1287 data = info.linkname
1290 data = info.linkname
1288 else:
1291 else:
1289 data = tar.extractfile(info).read()
1292 data = tar.extractfile(info).read()
1290 archiver.addfile(os.path.join(prefix, self._path, info.name),
1293 archiver.addfile(os.path.join(prefix, self._path, info.name),
1291 info.mode, info.issym(), data)
1294 info.mode, info.issym(), data)
1292 ui.progress(_('archiving (%s)') % relpath, i + 1,
1295 ui.progress(_('archiving (%s)') % relpath, i + 1,
1293 unit=_('files'))
1296 unit=_('files'))
1294 ui.progress(_('archiving (%s)') % relpath, None)
1297 ui.progress(_('archiving (%s)') % relpath, None)
1295
1298
1296
1299
1297 @annotatesubrepoerror
1300 @annotatesubrepoerror
1298 def status(self, rev2, **opts):
1301 def status(self, rev2, **opts):
1299 rev1 = self._state[1]
1302 rev1 = self._state[1]
1300 if self._gitmissing() or not rev1:
1303 if self._gitmissing() or not rev1:
1301 # if the repo is missing, return no results
1304 # if the repo is missing, return no results
1302 return [], [], [], [], [], [], []
1305 return [], [], [], [], [], [], []
1303 modified, added, removed = [], [], []
1306 modified, added, removed = [], [], []
1304 self._gitupdatestat()
1307 self._gitupdatestat()
1305 if rev2:
1308 if rev2:
1306 command = ['diff-tree', rev1, rev2]
1309 command = ['diff-tree', rev1, rev2]
1307 else:
1310 else:
1308 command = ['diff-index', rev1]
1311 command = ['diff-index', rev1]
1309 out = self._gitcommand(command)
1312 out = self._gitcommand(command)
1310 for line in out.split('\n'):
1313 for line in out.split('\n'):
1311 tab = line.find('\t')
1314 tab = line.find('\t')
1312 if tab == -1:
1315 if tab == -1:
1313 continue
1316 continue
1314 status, f = line[tab - 1], line[tab + 1:]
1317 status, f = line[tab - 1], line[tab + 1:]
1315 if status == 'M':
1318 if status == 'M':
1316 modified.append(f)
1319 modified.append(f)
1317 elif status == 'A':
1320 elif status == 'A':
1318 added.append(f)
1321 added.append(f)
1319 elif status == 'D':
1322 elif status == 'D':
1320 removed.append(f)
1323 removed.append(f)
1321
1324
1322 deleted = unknown = ignored = clean = []
1325 deleted = unknown = ignored = clean = []
1323 return modified, added, removed, deleted, unknown, ignored, clean
1326 return modified, added, removed, deleted, unknown, ignored, clean
1324
1327
1325 types = {
1328 types = {
1326 'hg': hgsubrepo,
1329 'hg': hgsubrepo,
1327 'svn': svnsubrepo,
1330 'svn': svnsubrepo,
1328 'git': gitsubrepo,
1331 'git': gitsubrepo,
1329 }
1332 }
@@ -1,1053 +1,1066 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 Revert subrepo and test subrepo fileset keyword:
38 Revert subrepo and test subrepo fileset keyword:
39
39
40 $ echo b > s/a
40 $ echo b > s/a
41 $ hg revert "set:subrepo('glob:s*')"
41 $ hg revert "set:subrepo('glob:s*')"
42 reverting subrepo s
42 reverting subrepo s
43 reverting s/a (glob)
43 reverting s/a (glob)
44 $ rm s/a.orig
44 $ rm s/a.orig
45
45
46 Revert subrepo with no backup. The "reverting s/a" line is gone since
46 Revert subrepo with no backup. The "reverting s/a" line is gone since
47 we're really running 'hg update' in the subrepo:
47 we're really running 'hg update' in the subrepo:
48
48
49 $ echo b > s/a
49 $ echo b > s/a
50 $ hg revert --no-backup s
50 $ hg revert --no-backup s
51 reverting subrepo s
51 reverting subrepo s
52
52
53 Issue2022: update -C
53 Issue2022: update -C
54
54
55 $ echo b > s/a
55 $ echo b > s/a
56 $ hg sum
56 $ hg sum
57 parent: 1:7cf8cfea66e4 tip
57 parent: 1:7cf8cfea66e4 tip
58 1
58 1
59 branch: default
59 branch: default
60 commit: 1 subrepos
60 commit: 1 subrepos
61 update: (current)
61 update: (current)
62 $ hg co -C 1
62 $ hg co -C 1
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 $ hg sum
64 $ hg sum
65 parent: 1:7cf8cfea66e4 tip
65 parent: 1:7cf8cfea66e4 tip
66 1
66 1
67 branch: default
67 branch: default
68 commit: (clean)
68 commit: (clean)
69 update: (current)
69 update: (current)
70
70
71 commands that require a clean repo should respect subrepos
71 commands that require a clean repo should respect subrepos
72
72
73 $ echo b >> s/a
73 $ echo b >> s/a
74 $ hg backout tip
74 $ hg backout tip
75 abort: uncommitted changes in subrepo s
75 abort: uncommitted changes in subrepo s
76 [255]
76 [255]
77 $ hg revert -C -R s s/a
77 $ hg revert -C -R s s/a
78
78
79 add sub sub
79 add sub sub
80
80
81 $ echo ss = ss > s/.hgsub
81 $ echo ss = ss > s/.hgsub
82 $ hg init s/ss
82 $ hg init s/ss
83 $ echo a > s/ss/a
83 $ echo a > s/ss/a
84 $ hg -R s add s/.hgsub
84 $ hg -R s add s/.hgsub
85 $ hg -R s/ss add s/ss/a
85 $ hg -R s/ss add s/ss/a
86 $ hg sum
86 $ hg sum
87 parent: 1:7cf8cfea66e4 tip
87 parent: 1:7cf8cfea66e4 tip
88 1
88 1
89 branch: default
89 branch: default
90 commit: 1 subrepos
90 commit: 1 subrepos
91 update: (current)
91 update: (current)
92 $ hg ci -m2
92 $ hg ci -m2
93 committing subrepository s
93 committing subrepository s
94 committing subrepository s/ss (glob)
94 committing subrepository s/ss (glob)
95 $ hg sum
95 $ hg sum
96 parent: 2:df30734270ae tip
96 parent: 2:df30734270ae tip
97 2
97 2
98 branch: default
98 branch: default
99 commit: (clean)
99 commit: (clean)
100 update: (current)
100 update: (current)
101
101
102 bump sub rev (and check it is ignored by ui.commitsubrepos)
102 bump sub rev (and check it is ignored by ui.commitsubrepos)
103
103
104 $ echo b > s/a
104 $ echo b > s/a
105 $ hg -R s ci -ms1
105 $ hg -R s ci -ms1
106 $ hg --config ui.commitsubrepos=no ci -m3
106 $ hg --config ui.commitsubrepos=no ci -m3
107
107
108 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
108 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
109
109
110 $ echo c > s/a
110 $ echo c > s/a
111 $ hg --config ui.commitsubrepos=no ci -m4
111 $ hg --config ui.commitsubrepos=no ci -m4
112 abort: uncommitted changes in subrepo s
112 abort: uncommitted changes in subrepo s
113 (use --subrepos for recursive commit)
113 (use --subrepos for recursive commit)
114 [255]
114 [255]
115 $ hg id
115 $ hg id
116 f6affe3fbfaa+ tip
116 f6affe3fbfaa+ tip
117 $ hg -R s ci -mc
117 $ hg -R s ci -mc
118 $ hg id
118 $ hg id
119 f6affe3fbfaa+ tip
119 f6affe3fbfaa+ tip
120 $ echo d > s/a
120 $ echo d > s/a
121 $ hg ci -m4
121 $ hg ci -m4
122 committing subrepository s
122 committing subrepository s
123 $ hg tip -R s
123 $ hg tip -R s
124 changeset: 4:02dcf1d70411
124 changeset: 4:02dcf1d70411
125 tag: tip
125 tag: tip
126 user: test
126 user: test
127 date: Thu Jan 01 00:00:00 1970 +0000
127 date: Thu Jan 01 00:00:00 1970 +0000
128 summary: 4
128 summary: 4
129
129
130
130
131 check caching
131 check caching
132
132
133 $ hg co 0
133 $ hg co 0
134 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
134 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
135 $ hg debugsub
135 $ hg debugsub
136
136
137 restore
137 restore
138
138
139 $ hg co
139 $ hg co
140 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 $ hg debugsub
141 $ hg debugsub
142 path s
142 path s
143 source s
143 source s
144 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
144 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
145
145
146 new branch for merge tests
146 new branch for merge tests
147
147
148 $ hg co 1
148 $ hg co 1
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 $ echo t = t >> .hgsub
150 $ echo t = t >> .hgsub
151 $ hg init t
151 $ hg init t
152 $ echo t > t/t
152 $ echo t > t/t
153 $ hg -R t add t
153 $ hg -R t add t
154 adding t/t (glob)
154 adding t/t (glob)
155
155
156 5
156 5
157
157
158 $ hg ci -m5 # add sub
158 $ hg ci -m5 # add sub
159 committing subrepository t
159 committing subrepository t
160 created new head
160 created new head
161 $ echo t2 > t/t
161 $ echo t2 > t/t
162
162
163 6
163 6
164
164
165 $ hg st -R s
165 $ hg st -R s
166 $ hg ci -m6 # change sub
166 $ hg ci -m6 # change sub
167 committing subrepository t
167 committing subrepository t
168 $ hg debugsub
168 $ hg debugsub
169 path s
169 path s
170 source s
170 source s
171 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
171 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
172 path t
172 path t
173 source t
173 source t
174 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
174 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
175 $ echo t3 > t/t
175 $ echo t3 > t/t
176
176
177 7
177 7
178
178
179 $ hg ci -m7 # change sub again for conflict test
179 $ hg ci -m7 # change sub again for conflict test
180 committing subrepository t
180 committing subrepository t
181 $ hg rm .hgsub
181 $ hg rm .hgsub
182
182
183 8
183 8
184
184
185 $ hg ci -m8 # remove sub
185 $ hg ci -m8 # remove sub
186
186
187 merge tests
187 merge tests
188
188
189 $ hg co -C 3
189 $ hg co -C 3
190 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 $ hg merge 5 # test adding
191 $ hg merge 5 # test adding
192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 (branch merge, don't forget to commit)
193 (branch merge, don't forget to commit)
194 $ hg debugsub
194 $ hg debugsub
195 path s
195 path s
196 source s
196 source s
197 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
197 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
198 path t
198 path t
199 source t
199 source t
200 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
200 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
201 $ hg ci -m9
201 $ hg ci -m9
202 created new head
202 created new head
203 $ hg merge 6 --debug # test change
203 $ hg merge 6 --debug # test change
204 searching for copies back to rev 2
204 searching for copies back to rev 2
205 resolving manifests
205 resolving manifests
206 overwrite: False, partial: False
206 overwrite: False, partial: False
207 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
207 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
208 .hgsubstate: versions differ -> m
208 .hgsubstate: versions differ -> m
209 updating: .hgsubstate 1/1 files (100.00%)
209 updating: .hgsubstate 1/1 files (100.00%)
210 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
210 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
211 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
211 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
212 getting subrepo t
212 getting subrepo t
213 searching for copies back to rev 1
213 searching for copies back to rev 1
214 resolving manifests
214 resolving manifests
215 overwrite: False, partial: False
215 overwrite: False, partial: False
216 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
216 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
217 t: remote is newer -> g
217 t: remote is newer -> g
218 updating: t 1/1 files (100.00%)
218 updating: t 1/1 files (100.00%)
219 getting t
219 getting t
220 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 (branch merge, don't forget to commit)
221 (branch merge, don't forget to commit)
222 $ hg debugsub
222 $ hg debugsub
223 path s
223 path s
224 source s
224 source s
225 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
225 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
226 path t
226 path t
227 source t
227 source t
228 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
228 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
229 $ echo conflict > t/t
229 $ echo conflict > t/t
230 $ hg ci -m10
230 $ hg ci -m10
231 committing subrepository t
231 committing subrepository t
232 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
232 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
233 searching for copies back to rev 2
233 searching for copies back to rev 2
234 resolving manifests
234 resolving manifests
235 overwrite: False, partial: False
235 overwrite: False, partial: False
236 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
236 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
237 .hgsubstate: versions differ -> m
237 .hgsubstate: versions differ -> m
238 updating: .hgsubstate 1/1 files (100.00%)
238 updating: .hgsubstate 1/1 files (100.00%)
239 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
239 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
240 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
240 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
241 merging subrepo t
241 merging subrepo t
242 searching for copies back to rev 2
242 searching for copies back to rev 2
243 resolving manifests
243 resolving manifests
244 overwrite: False, partial: False
244 overwrite: False, partial: False
245 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
245 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
246 t: versions differ -> m
246 t: versions differ -> m
247 preserving t for resolve of t
247 preserving t for resolve of t
248 updating: t 1/1 files (100.00%)
248 updating: t 1/1 files (100.00%)
249 picked tool 'internal:merge' for t (binary False symlink False)
249 picked tool 'internal:merge' for t (binary False symlink False)
250 merging t
250 merging t
251 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
251 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
252 warning: conflicts during merge.
252 warning: conflicts during merge.
253 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
253 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
254 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
254 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
255 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
255 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 (branch merge, don't forget to commit)
257 (branch merge, don't forget to commit)
258
258
259 should conflict
259 should conflict
260
260
261 $ cat t/t
261 $ cat t/t
262 <<<<<<< local
262 <<<<<<< local
263 conflict
263 conflict
264 =======
264 =======
265 t3
265 t3
266 >>>>>>> other
266 >>>>>>> other
267
267
268 clone
268 clone
269
269
270 $ cd ..
270 $ cd ..
271 $ hg clone t tc
271 $ hg clone t tc
272 updating to branch default
272 updating to branch default
273 cloning subrepo s from $TESTTMP/t/s (glob)
273 cloning subrepo s from $TESTTMP/t/s (glob)
274 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
274 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
275 cloning subrepo t from $TESTTMP/t/t (glob)
275 cloning subrepo t from $TESTTMP/t/t (glob)
276 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 $ cd tc
277 $ cd tc
278 $ hg debugsub
278 $ hg debugsub
279 path s
279 path s
280 source s
280 source s
281 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
281 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
282 path t
282 path t
283 source t
283 source t
284 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
284 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
285
285
286 push
286 push
287
287
288 $ echo bah > t/t
288 $ echo bah > t/t
289 $ hg ci -m11
289 $ hg ci -m11
290 committing subrepository t
290 committing subrepository t
291 $ hg push
291 $ hg push
292 pushing to $TESTTMP/t (glob)
292 pushing to $TESTTMP/t (glob)
293 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
293 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
294 searching for changes
294 searching for changes
295 no changes found
295 no changes found
296 pushing subrepo s to $TESTTMP/t/s (glob)
296 pushing subrepo s to $TESTTMP/t/s (glob)
297 searching for changes
297 searching for changes
298 no changes found
298 no changes found
299 pushing subrepo t to $TESTTMP/t/t (glob)
299 pushing subrepo t to $TESTTMP/t/t (glob)
300 searching for changes
300 searching for changes
301 adding changesets
301 adding changesets
302 adding manifests
302 adding manifests
303 adding file changes
303 adding file changes
304 added 1 changesets with 1 changes to 1 files
304 added 1 changesets with 1 changes to 1 files
305 searching for changes
305 searching for changes
306 adding changesets
306 adding changesets
307 adding manifests
307 adding manifests
308 adding file changes
308 adding file changes
309 added 1 changesets with 1 changes to 1 files
309 added 1 changesets with 1 changes to 1 files
310
310
311 push -f
311 push -f
312
312
313 $ echo bah > s/a
313 $ echo bah > s/a
314 $ hg ci -m12
314 $ hg ci -m12
315 committing subrepository s
315 committing subrepository s
316 $ hg push
316 $ hg push
317 pushing to $TESTTMP/t (glob)
317 pushing to $TESTTMP/t (glob)
318 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
318 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
319 searching for changes
319 searching for changes
320 no changes found
320 no changes found
321 pushing subrepo s to $TESTTMP/t/s (glob)
321 pushing subrepo s to $TESTTMP/t/s (glob)
322 searching for changes
322 searching for changes
323 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
323 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
324 (did you forget to merge? use push -f to force)
324 (did you forget to merge? use push -f to force)
325 [255]
325 [255]
326 $ hg push -f
326 $ hg push -f
327 pushing to $TESTTMP/t (glob)
327 pushing to $TESTTMP/t (glob)
328 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
328 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
329 searching for changes
329 searching for changes
330 no changes found
330 no changes found
331 pushing subrepo s to $TESTTMP/t/s (glob)
331 pushing subrepo s to $TESTTMP/t/s (glob)
332 searching for changes
332 searching for changes
333 adding changesets
333 adding changesets
334 adding manifests
334 adding manifests
335 adding file changes
335 adding file changes
336 added 1 changesets with 1 changes to 1 files (+1 heads)
336 added 1 changesets with 1 changes to 1 files (+1 heads)
337 pushing subrepo t to $TESTTMP/t/t (glob)
337 pushing subrepo t to $TESTTMP/t/t (glob)
338 searching for changes
338 searching for changes
339 no changes found
339 no changes found
340 searching for changes
340 searching for changes
341 adding changesets
341 adding changesets
342 adding manifests
342 adding manifests
343 adding file changes
343 adding file changes
344 added 1 changesets with 1 changes to 1 files
344 added 1 changesets with 1 changes to 1 files
345
345
346 update
346 update
347
347
348 $ cd ../t
348 $ cd ../t
349 $ hg up -C # discard our earlier merge
349 $ hg up -C # discard our earlier merge
350 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 $ echo blah > t/t
351 $ echo blah > t/t
352 $ hg ci -m13
352 $ hg ci -m13
353 committing subrepository t
353 committing subrepository t
354
354
355 pull
355 pull
356
356
357 $ cd ../tc
357 $ cd ../tc
358 $ hg pull
358 $ hg pull
359 pulling from $TESTTMP/t (glob)
359 pulling from $TESTTMP/t (glob)
360 searching for changes
360 searching for changes
361 adding changesets
361 adding changesets
362 adding manifests
362 adding manifests
363 adding file changes
363 adding file changes
364 added 1 changesets with 1 changes to 1 files
364 added 1 changesets with 1 changes to 1 files
365 (run 'hg update' to get a working copy)
365 (run 'hg update' to get a working copy)
366
366
367 should pull t
367 should pull t
368
368
369 $ hg up
369 $ hg up
370 pulling subrepo t from $TESTTMP/t/t (glob)
370 pulling subrepo t from $TESTTMP/t/t (glob)
371 searching for changes
371 searching for changes
372 adding changesets
372 adding changesets
373 adding manifests
373 adding manifests
374 adding file changes
374 adding file changes
375 added 1 changesets with 1 changes to 1 files
375 added 1 changesets with 1 changes to 1 files
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 $ cat t/t
377 $ cat t/t
378 blah
378 blah
379
379
380 bogus subrepo path aborts
380 bogus subrepo path aborts
381
381
382 $ echo 'bogus=[boguspath' >> .hgsub
382 $ echo 'bogus=[boguspath' >> .hgsub
383 $ hg ci -m 'bogus subrepo path'
383 $ hg ci -m 'bogus subrepo path'
384 abort: missing ] in subrepo source
384 abort: missing ] in subrepo source
385 [255]
385 [255]
386
386
387 Issue1986: merge aborts when trying to merge a subrepo that
387 Issue1986: merge aborts when trying to merge a subrepo that
388 shouldn't need merging
388 shouldn't need merging
389
389
390 # subrepo layout
390 # subrepo layout
391 #
391 #
392 # o 5 br
392 # o 5 br
393 # /|
393 # /|
394 # o | 4 default
394 # o | 4 default
395 # | |
395 # | |
396 # | o 3 br
396 # | o 3 br
397 # |/|
397 # |/|
398 # o | 2 default
398 # o | 2 default
399 # | |
399 # | |
400 # | o 1 br
400 # | o 1 br
401 # |/
401 # |/
402 # o 0 default
402 # o 0 default
403
403
404 $ cd ..
404 $ cd ..
405 $ rm -rf sub
405 $ rm -rf sub
406 $ hg init main
406 $ hg init main
407 $ cd main
407 $ cd main
408 $ hg init s
408 $ hg init s
409 $ cd s
409 $ cd s
410 $ echo a > a
410 $ echo a > a
411 $ hg ci -Am1
411 $ hg ci -Am1
412 adding a
412 adding a
413 $ hg branch br
413 $ hg branch br
414 marked working directory as branch br
414 marked working directory as branch br
415 (branches are permanent and global, did you want a bookmark?)
415 (branches are permanent and global, did you want a bookmark?)
416 $ echo a >> a
416 $ echo a >> a
417 $ hg ci -m1
417 $ hg ci -m1
418 $ hg up default
418 $ hg up default
419 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
419 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
420 $ echo b > b
420 $ echo b > b
421 $ hg ci -Am1
421 $ hg ci -Am1
422 adding b
422 adding b
423 $ hg up br
423 $ hg up br
424 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
424 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
425 $ hg merge tip
425 $ hg merge tip
426 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
427 (branch merge, don't forget to commit)
427 (branch merge, don't forget to commit)
428 $ hg ci -m1
428 $ hg ci -m1
429 $ hg up 2
429 $ hg up 2
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 $ echo c > c
431 $ echo c > c
432 $ hg ci -Am1
432 $ hg ci -Am1
433 adding c
433 adding c
434 $ hg up 3
434 $ hg up 3
435 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
435 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 $ hg merge 4
436 $ hg merge 4
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 (branch merge, don't forget to commit)
438 (branch merge, don't forget to commit)
439 $ hg ci -m1
439 $ hg ci -m1
440
440
441 # main repo layout:
441 # main repo layout:
442 #
442 #
443 # * <-- try to merge default into br again
443 # * <-- try to merge default into br again
444 # .`|
444 # .`|
445 # . o 5 br --> substate = 5
445 # . o 5 br --> substate = 5
446 # . |
446 # . |
447 # o | 4 default --> substate = 4
447 # o | 4 default --> substate = 4
448 # | |
448 # | |
449 # | o 3 br --> substate = 2
449 # | o 3 br --> substate = 2
450 # |/|
450 # |/|
451 # o | 2 default --> substate = 2
451 # o | 2 default --> substate = 2
452 # | |
452 # | |
453 # | o 1 br --> substate = 3
453 # | o 1 br --> substate = 3
454 # |/
454 # |/
455 # o 0 default --> substate = 2
455 # o 0 default --> substate = 2
456
456
457 $ cd ..
457 $ cd ..
458 $ echo 's = s' > .hgsub
458 $ echo 's = s' > .hgsub
459 $ hg -R s up 2
459 $ hg -R s up 2
460 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
460 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
461 $ hg ci -Am1
461 $ hg ci -Am1
462 adding .hgsub
462 adding .hgsub
463 $ hg branch br
463 $ hg branch br
464 marked working directory as branch br
464 marked working directory as branch br
465 (branches are permanent and global, did you want a bookmark?)
465 (branches are permanent and global, did you want a bookmark?)
466 $ echo b > b
466 $ echo b > b
467 $ hg -R s up 3
467 $ hg -R s up 3
468 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 $ hg ci -Am1
469 $ hg ci -Am1
470 adding b
470 adding b
471 $ hg up default
471 $ hg up default
472 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
472 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
473 $ echo c > c
473 $ echo c > c
474 $ hg ci -Am1
474 $ hg ci -Am1
475 adding c
475 adding c
476 $ hg up 1
476 $ hg up 1
477 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
477 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
478 $ hg merge 2
478 $ hg merge 2
479 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
479 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 (branch merge, don't forget to commit)
480 (branch merge, don't forget to commit)
481 $ hg ci -m1
481 $ hg ci -m1
482 $ hg up 2
482 $ hg up 2
483 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
483 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
484 $ hg -R s up 4
484 $ hg -R s up 4
485 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 $ echo d > d
486 $ echo d > d
487 $ hg ci -Am1
487 $ hg ci -Am1
488 adding d
488 adding d
489 $ hg up 3
489 $ hg up 3
490 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
490 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
491 $ hg -R s up 5
491 $ hg -R s up 5
492 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
492 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
493 $ echo e > e
493 $ echo e > e
494 $ hg ci -Am1
494 $ hg ci -Am1
495 adding e
495 adding e
496
496
497 $ hg up 5
497 $ hg up 5
498 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
498 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
499 $ hg merge 4 # try to merge default into br again
499 $ hg merge 4 # try to merge default into br again
500 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 (branch merge, don't forget to commit)
501 (branch merge, don't forget to commit)
502 $ cd ..
502 $ cd ..
503
503
504 test subrepo delete from .hgsubstate
504 test subrepo delete from .hgsubstate
505
505
506 $ hg init testdelete
506 $ hg init testdelete
507 $ mkdir testdelete/nested testdelete/nested2
507 $ mkdir testdelete/nested testdelete/nested2
508 $ hg init testdelete/nested
508 $ hg init testdelete/nested
509 $ hg init testdelete/nested2
509 $ hg init testdelete/nested2
510 $ echo test > testdelete/nested/foo
510 $ echo test > testdelete/nested/foo
511 $ echo test > testdelete/nested2/foo
511 $ echo test > testdelete/nested2/foo
512 $ hg -R testdelete/nested add
512 $ hg -R testdelete/nested add
513 adding testdelete/nested/foo (glob)
513 adding testdelete/nested/foo (glob)
514 $ hg -R testdelete/nested2 add
514 $ hg -R testdelete/nested2 add
515 adding testdelete/nested2/foo (glob)
515 adding testdelete/nested2/foo (glob)
516 $ hg -R testdelete/nested ci -m test
516 $ hg -R testdelete/nested ci -m test
517 $ hg -R testdelete/nested2 ci -m test
517 $ hg -R testdelete/nested2 ci -m test
518 $ echo nested = nested > testdelete/.hgsub
518 $ echo nested = nested > testdelete/.hgsub
519 $ echo nested2 = nested2 >> testdelete/.hgsub
519 $ echo nested2 = nested2 >> testdelete/.hgsub
520 $ hg -R testdelete add
520 $ hg -R testdelete add
521 adding testdelete/.hgsub (glob)
521 adding testdelete/.hgsub (glob)
522 $ hg -R testdelete ci -m "nested 1 & 2 added"
522 $ hg -R testdelete ci -m "nested 1 & 2 added"
523 $ echo nested = nested > testdelete/.hgsub
523 $ echo nested = nested > testdelete/.hgsub
524 $ hg -R testdelete ci -m "nested 2 deleted"
524 $ hg -R testdelete ci -m "nested 2 deleted"
525 $ cat testdelete/.hgsubstate
525 $ cat testdelete/.hgsubstate
526 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
526 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
527 $ hg -R testdelete remove testdelete/.hgsub
527 $ hg -R testdelete remove testdelete/.hgsub
528 $ hg -R testdelete ci -m ".hgsub deleted"
528 $ hg -R testdelete ci -m ".hgsub deleted"
529 $ cat testdelete/.hgsubstate
529 $ cat testdelete/.hgsubstate
530 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
530 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
531
531
532 test repository cloning
532 test repository cloning
533
533
534 $ mkdir mercurial mercurial2
534 $ mkdir mercurial mercurial2
535 $ hg init nested_absolute
535 $ hg init nested_absolute
536 $ echo test > nested_absolute/foo
536 $ echo test > nested_absolute/foo
537 $ hg -R nested_absolute add
537 $ hg -R nested_absolute add
538 adding nested_absolute/foo (glob)
538 adding nested_absolute/foo (glob)
539 $ hg -R nested_absolute ci -mtest
539 $ hg -R nested_absolute ci -mtest
540 $ cd mercurial
540 $ cd mercurial
541 $ hg init nested_relative
541 $ hg init nested_relative
542 $ echo test2 > nested_relative/foo2
542 $ echo test2 > nested_relative/foo2
543 $ hg -R nested_relative add
543 $ hg -R nested_relative add
544 adding nested_relative/foo2 (glob)
544 adding nested_relative/foo2 (glob)
545 $ hg -R nested_relative ci -mtest2
545 $ hg -R nested_relative ci -mtest2
546 $ hg init main
546 $ hg init main
547 $ echo "nested_relative = ../nested_relative" > main/.hgsub
547 $ echo "nested_relative = ../nested_relative" > main/.hgsub
548 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
548 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
549 $ hg -R main add
549 $ hg -R main add
550 adding main/.hgsub (glob)
550 adding main/.hgsub (glob)
551 $ hg -R main ci -m "add subrepos"
551 $ hg -R main ci -m "add subrepos"
552 $ cd ..
552 $ cd ..
553 $ hg clone mercurial/main mercurial2/main
553 $ hg clone mercurial/main mercurial2/main
554 updating to branch default
554 updating to branch default
555 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
555 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
556 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
557 > mercurial2/main/nested_relative/.hg/hgrc
557 > mercurial2/main/nested_relative/.hg/hgrc
558 [paths]
558 [paths]
559 default = $TESTTMP/mercurial/nested_absolute
559 default = $TESTTMP/mercurial/nested_absolute
560 [paths]
560 [paths]
561 default = $TESTTMP/mercurial/nested_relative
561 default = $TESTTMP/mercurial/nested_relative
562 $ rm -rf mercurial mercurial2
562 $ rm -rf mercurial mercurial2
563
563
564 Issue1977: multirepo push should fail if subrepo push fails
564 Issue1977: multirepo push should fail if subrepo push fails
565
565
566 $ hg init repo
566 $ hg init repo
567 $ hg init repo/s
567 $ hg init repo/s
568 $ echo a > repo/s/a
568 $ echo a > repo/s/a
569 $ hg -R repo/s ci -Am0
569 $ hg -R repo/s ci -Am0
570 adding a
570 adding a
571 $ echo s = s > repo/.hgsub
571 $ echo s = s > repo/.hgsub
572 $ hg -R repo ci -Am1
572 $ hg -R repo ci -Am1
573 adding .hgsub
573 adding .hgsub
574 $ hg clone repo repo2
574 $ hg clone repo repo2
575 updating to branch default
575 updating to branch default
576 cloning subrepo s from $TESTTMP/repo/s (glob)
576 cloning subrepo s from $TESTTMP/repo/s (glob)
577 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
577 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
578 $ hg -q -R repo2 pull -u
578 $ hg -q -R repo2 pull -u
579 $ echo 1 > repo2/s/a
579 $ echo 1 > repo2/s/a
580 $ hg -R repo2/s ci -m2
580 $ hg -R repo2/s ci -m2
581 $ hg -q -R repo2/s push
581 $ hg -q -R repo2/s push
582 $ hg -R repo2/s up -C 0
582 $ hg -R repo2/s up -C 0
583 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
583 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 $ echo 2 > repo2/s/b
584 $ echo 2 > repo2/s/b
585 $ hg -R repo2/s ci -m3 -A
585 $ hg -R repo2/s ci -m3 -A
586 adding b
586 adding b
587 created new head
587 created new head
588 $ hg -R repo2 ci -m3
588 $ hg -R repo2 ci -m3
589 $ hg -q -R repo2 push
589 $ hg -q -R repo2 push
590 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
590 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
591 (did you forget to merge? use push -f to force)
591 (did you forget to merge? use push -f to force)
592 [255]
592 [255]
593 $ hg -R repo update
593 $ hg -R repo update
594 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
594 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
595
595
596 test if untracked file is not overwritten
596 test if untracked file is not overwritten
597
597
598 $ echo issue3276_ok > repo/s/b
598 $ echo issue3276_ok > repo/s/b
599 $ hg -R repo2 push -f -q
599 $ hg -R repo2 push -f -q
600 $ hg -R repo update
600 $ hg -R repo update
601 b: untracked file differs
601 b: untracked file differs
602 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
602 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
603 [255]
603 [255]
604
604
605 $ cat repo/s/b
605 $ cat repo/s/b
606 issue3276_ok
606 issue3276_ok
607 $ rm repo/s/b
607 $ rm repo/s/b
608 $ hg -R repo revert --all
608 $ hg -R repo revert --all
609 reverting repo/.hgsubstate (glob)
609 reverting repo/.hgsubstate (glob)
610 reverting subrepo s
610 reverting subrepo s
611 $ hg -R repo update
611 $ hg -R repo update
612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
613 $ cat repo/s/b
613 $ cat repo/s/b
614 2
614 2
615 $ rm -rf repo2 repo
615 $ rm -rf repo2 repo
616
616
617
617
618 Issue1852 subrepos with relative paths always push/pull relative to default
618 Issue1852 subrepos with relative paths always push/pull relative to default
619
619
620 Prepare a repo with subrepo
620 Prepare a repo with subrepo
621
621
622 $ hg init issue1852a
622 $ hg init issue1852a
623 $ cd issue1852a
623 $ cd issue1852a
624 $ hg init sub/repo
624 $ hg init sub/repo
625 $ echo test > sub/repo/foo
625 $ echo test > sub/repo/foo
626 $ hg -R sub/repo add sub/repo/foo
626 $ hg -R sub/repo add sub/repo/foo
627 $ echo sub/repo = sub/repo > .hgsub
627 $ echo sub/repo = sub/repo > .hgsub
628 $ hg add .hgsub
628 $ hg add .hgsub
629 $ hg ci -mtest
629 $ hg ci -mtest
630 committing subrepository sub/repo (glob)
630 committing subrepository sub/repo (glob)
631 $ echo test >> sub/repo/foo
631 $ echo test >> sub/repo/foo
632 $ hg ci -mtest
632 $ hg ci -mtest
633 committing subrepository sub/repo (glob)
633 committing subrepository sub/repo (glob)
634 $ cd ..
634 $ cd ..
635
635
636 Create repo without default path, pull top repo, and see what happens on update
636 Create repo without default path, pull top repo, and see what happens on update
637
637
638 $ hg init issue1852b
638 $ hg init issue1852b
639 $ hg -R issue1852b pull issue1852a
639 $ hg -R issue1852b pull issue1852a
640 pulling from issue1852a
640 pulling from issue1852a
641 requesting all changes
641 requesting all changes
642 adding changesets
642 adding changesets
643 adding manifests
643 adding manifests
644 adding file changes
644 adding file changes
645 added 2 changesets with 3 changes to 2 files
645 added 2 changesets with 3 changes to 2 files
646 (run 'hg update' to get a working copy)
646 (run 'hg update' to get a working copy)
647 $ hg -R issue1852b update
647 $ hg -R issue1852b update
648 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
648 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
649 [255]
649 [255]
650
650
651 Pull -u now doesn't help
651 Pull -u now doesn't help
652
652
653 $ hg -R issue1852b pull -u issue1852a
653 $ hg -R issue1852b pull -u issue1852a
654 pulling from issue1852a
654 pulling from issue1852a
655 searching for changes
655 searching for changes
656 no changes found
656 no changes found
657
657
658 Try the same, but with pull -u
658 Try the same, but with pull -u
659
659
660 $ hg init issue1852c
660 $ hg init issue1852c
661 $ hg -R issue1852c pull -r0 -u issue1852a
661 $ hg -R issue1852c pull -r0 -u issue1852a
662 pulling from issue1852a
662 pulling from issue1852a
663 adding changesets
663 adding changesets
664 adding manifests
664 adding manifests
665 adding file changes
665 adding file changes
666 added 1 changesets with 2 changes to 2 files
666 added 1 changesets with 2 changes to 2 files
667 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
667 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
668 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
668 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
669
669
670 Try to push from the other side
670 Try to push from the other side
671
671
672 $ hg -R issue1852a push `pwd`/issue1852c
672 $ hg -R issue1852a push `pwd`/issue1852c
673 pushing to $TESTTMP/issue1852c
673 pushing to $TESTTMP/issue1852c
674 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
674 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
675 searching for changes
675 searching for changes
676 no changes found
676 no changes found
677 searching for changes
677 searching for changes
678 adding changesets
678 adding changesets
679 adding manifests
679 adding manifests
680 adding file changes
680 adding file changes
681 added 1 changesets with 1 changes to 1 files
681 added 1 changesets with 1 changes to 1 files
682
682
683 Incoming and outgoing should not use the default path:
683 Incoming and outgoing should not use the default path:
684
684
685 $ hg clone -q issue1852a issue1852d
685 $ hg clone -q issue1852a issue1852d
686 $ hg -R issue1852d outgoing --subrepos issue1852c
686 $ hg -R issue1852d outgoing --subrepos issue1852c
687 comparing with issue1852c
687 comparing with issue1852c
688 searching for changes
688 searching for changes
689 no changes found
689 no changes found
690 comparing with issue1852c/sub/repo
690 comparing with issue1852c/sub/repo
691 searching for changes
691 searching for changes
692 no changes found
692 no changes found
693 [1]
693 [1]
694 $ hg -R issue1852d incoming --subrepos issue1852c
694 $ hg -R issue1852d incoming --subrepos issue1852c
695 comparing with issue1852c
695 comparing with issue1852c
696 searching for changes
696 searching for changes
697 no changes found
697 no changes found
698 comparing with issue1852c/sub/repo
698 comparing with issue1852c/sub/repo
699 searching for changes
699 searching for changes
700 no changes found
700 no changes found
701 [1]
701 [1]
702
702
703 Check status of files when none of them belong to the first
703 Check status of files when none of them belong to the first
704 subrepository:
704 subrepository:
705
705
706 $ hg init subrepo-status
706 $ hg init subrepo-status
707 $ cd subrepo-status
707 $ cd subrepo-status
708 $ hg init subrepo-1
708 $ hg init subrepo-1
709 $ hg init subrepo-2
709 $ hg init subrepo-2
710 $ cd subrepo-2
710 $ cd subrepo-2
711 $ touch file
711 $ touch file
712 $ hg add file
712 $ hg add file
713 $ cd ..
713 $ cd ..
714 $ echo subrepo-1 = subrepo-1 > .hgsub
714 $ echo subrepo-1 = subrepo-1 > .hgsub
715 $ echo subrepo-2 = subrepo-2 >> .hgsub
715 $ echo subrepo-2 = subrepo-2 >> .hgsub
716 $ hg add .hgsub
716 $ hg add .hgsub
717 $ hg ci -m 'Added subrepos'
717 $ hg ci -m 'Added subrepos'
718 committing subrepository subrepo-2
718 committing subrepository subrepo-2
719 $ hg st subrepo-2/file
719 $ hg st subrepo-2/file
720
720
721 Check that share works with subrepo
721 Check that share works with subrepo
722 $ hg --config extensions.share= share . ../shared
722 $ hg --config extensions.share= share . ../shared
723 updating working directory
723 updating working directory
724 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
724 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
725 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
725 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
726 $ test -f ../shared/subrepo-1/.hg/sharedpath
726 $ test -f ../shared/subrepo-1/.hg/sharedpath
727 [1]
727 [1]
728 $ hg -R ../shared in
729 comparing with $TESTTMP/subrepo-status (glob)
730 searching for changes
731 no changes found
732 [1]
733 $ hg -R ../shared/subrepo-2 showconfig paths
734 paths.default=$TESTTMP/subrepo-status/subrepo-2
735 $ hg -R ../shared/subrepo-1 sum --remote
736 parent: -1:000000000000 tip (empty repository)
737 branch: default
738 commit: (clean)
739 update: (current)
740 remote: (synced)
728
741
729 Check hg update --clean
742 Check hg update --clean
730 $ cd $TESTTMP/t
743 $ cd $TESTTMP/t
731 $ rm -r t/t.orig
744 $ rm -r t/t.orig
732 $ hg status -S --all
745 $ hg status -S --all
733 C .hgsub
746 C .hgsub
734 C .hgsubstate
747 C .hgsubstate
735 C a
748 C a
736 C s/.hgsub
749 C s/.hgsub
737 C s/.hgsubstate
750 C s/.hgsubstate
738 C s/a
751 C s/a
739 C s/ss/a
752 C s/ss/a
740 C t/t
753 C t/t
741 $ echo c1 > s/a
754 $ echo c1 > s/a
742 $ cd s
755 $ cd s
743 $ echo c1 > b
756 $ echo c1 > b
744 $ echo c1 > c
757 $ echo c1 > c
745 $ hg add b
758 $ hg add b
746 $ cd ..
759 $ cd ..
747 $ hg status -S
760 $ hg status -S
748 M s/a
761 M s/a
749 A s/b
762 A s/b
750 ? s/c
763 ? s/c
751 $ hg update -C
764 $ hg update -C
752 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
765 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
753 $ hg status -S
766 $ hg status -S
754 ? s/b
767 ? s/b
755 ? s/c
768 ? s/c
756
769
757 Sticky subrepositories, no changes
770 Sticky subrepositories, no changes
758 $ cd $TESTTMP/t
771 $ cd $TESTTMP/t
759 $ hg id
772 $ hg id
760 925c17564ef8 tip
773 925c17564ef8 tip
761 $ hg -R s id
774 $ hg -R s id
762 12a213df6fa9 tip
775 12a213df6fa9 tip
763 $ hg -R t id
776 $ hg -R t id
764 52c0adc0515a tip
777 52c0adc0515a tip
765 $ hg update 11
778 $ hg update 11
766 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
779 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
767 $ hg id
780 $ hg id
768 365661e5936a
781 365661e5936a
769 $ hg -R s id
782 $ hg -R s id
770 fc627a69481f
783 fc627a69481f
771 $ hg -R t id
784 $ hg -R t id
772 e95bcfa18a35
785 e95bcfa18a35
773
786
774 Sticky subrepositorys, file changes
787 Sticky subrepositorys, file changes
775 $ touch s/f1
788 $ touch s/f1
776 $ touch t/f1
789 $ touch t/f1
777 $ hg add -S s/f1
790 $ hg add -S s/f1
778 $ hg add -S t/f1
791 $ hg add -S t/f1
779 $ hg id
792 $ hg id
780 365661e5936a+
793 365661e5936a+
781 $ hg -R s id
794 $ hg -R s id
782 fc627a69481f+
795 fc627a69481f+
783 $ hg -R t id
796 $ hg -R t id
784 e95bcfa18a35+
797 e95bcfa18a35+
785 $ hg update tip
798 $ hg update tip
786 subrepository sources for s differ
799 subrepository sources for s differ
787 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)?
800 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)?
788 l
801 l
789 subrepository sources for t differ
802 subrepository sources for t differ
790 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)?
803 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)?
791 l
804 l
792 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
805 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
793 $ hg id
806 $ hg id
794 925c17564ef8+ tip
807 925c17564ef8+ tip
795 $ hg -R s id
808 $ hg -R s id
796 fc627a69481f+
809 fc627a69481f+
797 $ hg -R t id
810 $ hg -R t id
798 e95bcfa18a35+
811 e95bcfa18a35+
799 $ hg update --clean tip
812 $ hg update --clean tip
800 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
813 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
801
814
802 Sticky subrepository, revision updates
815 Sticky subrepository, revision updates
803 $ hg id
816 $ hg id
804 925c17564ef8 tip
817 925c17564ef8 tip
805 $ hg -R s id
818 $ hg -R s id
806 12a213df6fa9 tip
819 12a213df6fa9 tip
807 $ hg -R t id
820 $ hg -R t id
808 52c0adc0515a tip
821 52c0adc0515a tip
809 $ cd s
822 $ cd s
810 $ hg update -r -2
823 $ hg update -r -2
811 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
824 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
812 $ cd ../t
825 $ cd ../t
813 $ hg update -r 2
826 $ hg update -r 2
814 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
827 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
815 $ cd ..
828 $ cd ..
816 $ hg update 10
829 $ hg update 10
817 subrepository sources for t differ (in checked out version)
830 subrepository sources for t differ (in checked out version)
818 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)?
831 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)?
819 l
832 l
820 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
833 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
821 $ hg id
834 $ hg id
822 e45c8b14af55+
835 e45c8b14af55+
823 $ hg -R s id
836 $ hg -R s id
824 02dcf1d70411
837 02dcf1d70411
825 $ hg -R t id
838 $ hg -R t id
826 7af322bc1198
839 7af322bc1198
827
840
828 Sticky subrepository, file changes and revision updates
841 Sticky subrepository, file changes and revision updates
829 $ touch s/f1
842 $ touch s/f1
830 $ touch t/f1
843 $ touch t/f1
831 $ hg add -S s/f1
844 $ hg add -S s/f1
832 $ hg add -S t/f1
845 $ hg add -S t/f1
833 $ hg id
846 $ hg id
834 e45c8b14af55+
847 e45c8b14af55+
835 $ hg -R s id
848 $ hg -R s id
836 02dcf1d70411+
849 02dcf1d70411+
837 $ hg -R t id
850 $ hg -R t id
838 7af322bc1198+
851 7af322bc1198+
839 $ hg update tip
852 $ hg update tip
840 subrepository sources for s differ
853 subrepository sources for s differ
841 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)?
854 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)?
842 l
855 l
843 subrepository sources for t differ
856 subrepository sources for t differ
844 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)?
857 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)?
845 l
858 l
846 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
859 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
847 $ hg id
860 $ hg id
848 925c17564ef8+ tip
861 925c17564ef8+ tip
849 $ hg -R s id
862 $ hg -R s id
850 02dcf1d70411+
863 02dcf1d70411+
851 $ hg -R t id
864 $ hg -R t id
852 7af322bc1198+
865 7af322bc1198+
853
866
854 Sticky repository, update --clean
867 Sticky repository, update --clean
855 $ hg update --clean tip
868 $ hg update --clean tip
856 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
869 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
857 $ hg id
870 $ hg id
858 925c17564ef8 tip
871 925c17564ef8 tip
859 $ hg -R s id
872 $ hg -R s id
860 12a213df6fa9 tip
873 12a213df6fa9 tip
861 $ hg -R t id
874 $ hg -R t id
862 52c0adc0515a tip
875 52c0adc0515a tip
863
876
864 Test subrepo already at intended revision:
877 Test subrepo already at intended revision:
865 $ cd s
878 $ cd s
866 $ hg update fc627a69481f
879 $ hg update fc627a69481f
867 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
880 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
868 $ cd ..
881 $ cd ..
869 $ hg update 11
882 $ hg update 11
870 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
871 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
884 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
872 $ hg id -n
885 $ hg id -n
873 11+
886 11+
874 $ hg -R s id
887 $ hg -R s id
875 fc627a69481f
888 fc627a69481f
876 $ hg -R t id
889 $ hg -R t id
877 e95bcfa18a35
890 e95bcfa18a35
878
891
879 Test that removing .hgsubstate doesn't break anything:
892 Test that removing .hgsubstate doesn't break anything:
880
893
881 $ hg rm -f .hgsubstate
894 $ hg rm -f .hgsubstate
882 $ hg ci -mrm
895 $ hg ci -mrm
883 nothing changed
896 nothing changed
884 [1]
897 [1]
885 $ hg log -vr tip
898 $ hg log -vr tip
886 changeset: 13:925c17564ef8
899 changeset: 13:925c17564ef8
887 tag: tip
900 tag: tip
888 user: test
901 user: test
889 date: Thu Jan 01 00:00:00 1970 +0000
902 date: Thu Jan 01 00:00:00 1970 +0000
890 files: .hgsubstate
903 files: .hgsubstate
891 description:
904 description:
892 13
905 13
893
906
894
907
895
908
896 Test that removing .hgsub removes .hgsubstate:
909 Test that removing .hgsub removes .hgsubstate:
897
910
898 $ hg rm .hgsub
911 $ hg rm .hgsub
899 $ hg ci -mrm2
912 $ hg ci -mrm2
900 created new head
913 created new head
901 $ hg log -vr tip
914 $ hg log -vr tip
902 changeset: 14:2400bccd50af
915 changeset: 14:2400bccd50af
903 tag: tip
916 tag: tip
904 parent: 11:365661e5936a
917 parent: 11:365661e5936a
905 user: test
918 user: test
906 date: Thu Jan 01 00:00:00 1970 +0000
919 date: Thu Jan 01 00:00:00 1970 +0000
907 files: .hgsub .hgsubstate
920 files: .hgsub .hgsubstate
908 description:
921 description:
909 rm2
922 rm2
910
923
911
924
912 Test issue3153: diff -S with deleted subrepos
925 Test issue3153: diff -S with deleted subrepos
913
926
914 $ hg diff --nodates -S -c .
927 $ hg diff --nodates -S -c .
915 diff -r 365661e5936a -r 2400bccd50af .hgsub
928 diff -r 365661e5936a -r 2400bccd50af .hgsub
916 --- a/.hgsub
929 --- a/.hgsub
917 +++ /dev/null
930 +++ /dev/null
918 @@ -1,2 +0,0 @@
931 @@ -1,2 +0,0 @@
919 -s = s
932 -s = s
920 -t = t
933 -t = t
921 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
934 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
922 --- a/.hgsubstate
935 --- a/.hgsubstate
923 +++ /dev/null
936 +++ /dev/null
924 @@ -1,2 +0,0 @@
937 @@ -1,2 +0,0 @@
925 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
938 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
926 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
939 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
927
940
928 Test behavior of add for explicit path in subrepo:
941 Test behavior of add for explicit path in subrepo:
929 $ cd ..
942 $ cd ..
930 $ hg init explicit
943 $ hg init explicit
931 $ cd explicit
944 $ cd explicit
932 $ echo s = s > .hgsub
945 $ echo s = s > .hgsub
933 $ hg add .hgsub
946 $ hg add .hgsub
934 $ hg init s
947 $ hg init s
935 $ hg ci -m0
948 $ hg ci -m0
936 Adding with an explicit path in a subrepo adds the file
949 Adding with an explicit path in a subrepo adds the file
937 $ echo c1 > f1
950 $ echo c1 > f1
938 $ echo c2 > s/f2
951 $ echo c2 > s/f2
939 $ hg st -S
952 $ hg st -S
940 ? f1
953 ? f1
941 ? s/f2
954 ? s/f2
942 $ hg add s/f2
955 $ hg add s/f2
943 $ hg st -S
956 $ hg st -S
944 A s/f2
957 A s/f2
945 ? f1
958 ? f1
946 $ hg ci -R s -m0
959 $ hg ci -R s -m0
947 $ hg ci -Am1
960 $ hg ci -Am1
948 adding f1
961 adding f1
949 Adding with an explicit path in a subrepo with -S has the same behavior
962 Adding with an explicit path in a subrepo with -S has the same behavior
950 $ echo c3 > f3
963 $ echo c3 > f3
951 $ echo c4 > s/f4
964 $ echo c4 > s/f4
952 $ hg st -S
965 $ hg st -S
953 ? f3
966 ? f3
954 ? s/f4
967 ? s/f4
955 $ hg add -S s/f4
968 $ hg add -S s/f4
956 $ hg st -S
969 $ hg st -S
957 A s/f4
970 A s/f4
958 ? f3
971 ? f3
959 $ hg ci -R s -m1
972 $ hg ci -R s -m1
960 $ hg ci -Ama2
973 $ hg ci -Ama2
961 adding f3
974 adding f3
962 Adding without a path or pattern silently ignores subrepos
975 Adding without a path or pattern silently ignores subrepos
963 $ echo c5 > f5
976 $ echo c5 > f5
964 $ echo c6 > s/f6
977 $ echo c6 > s/f6
965 $ echo c7 > s/f7
978 $ echo c7 > s/f7
966 $ hg st -S
979 $ hg st -S
967 ? f5
980 ? f5
968 ? s/f6
981 ? s/f6
969 ? s/f7
982 ? s/f7
970 $ hg add
983 $ hg add
971 adding f5
984 adding f5
972 $ hg st -S
985 $ hg st -S
973 A f5
986 A f5
974 ? s/f6
987 ? s/f6
975 ? s/f7
988 ? s/f7
976 $ hg ci -R s -Am2
989 $ hg ci -R s -Am2
977 adding f6
990 adding f6
978 adding f7
991 adding f7
979 $ hg ci -m3
992 $ hg ci -m3
980 Adding without a path or pattern with -S also adds files in subrepos
993 Adding without a path or pattern with -S also adds files in subrepos
981 $ echo c8 > f8
994 $ echo c8 > f8
982 $ echo c9 > s/f9
995 $ echo c9 > s/f9
983 $ echo c10 > s/f10
996 $ echo c10 > s/f10
984 $ hg st -S
997 $ hg st -S
985 ? f8
998 ? f8
986 ? s/f10
999 ? s/f10
987 ? s/f9
1000 ? s/f9
988 $ hg add -S
1001 $ hg add -S
989 adding f8
1002 adding f8
990 adding s/f10 (glob)
1003 adding s/f10 (glob)
991 adding s/f9 (glob)
1004 adding s/f9 (glob)
992 $ hg st -S
1005 $ hg st -S
993 A f8
1006 A f8
994 A s/f10
1007 A s/f10
995 A s/f9
1008 A s/f9
996 $ hg ci -R s -m3
1009 $ hg ci -R s -m3
997 $ hg ci -m4
1010 $ hg ci -m4
998 Adding with a pattern silently ignores subrepos
1011 Adding with a pattern silently ignores subrepos
999 $ echo c11 > fm11
1012 $ echo c11 > fm11
1000 $ echo c12 > fn12
1013 $ echo c12 > fn12
1001 $ echo c13 > s/fm13
1014 $ echo c13 > s/fm13
1002 $ echo c14 > s/fn14
1015 $ echo c14 > s/fn14
1003 $ hg st -S
1016 $ hg st -S
1004 ? fm11
1017 ? fm11
1005 ? fn12
1018 ? fn12
1006 ? s/fm13
1019 ? s/fm13
1007 ? s/fn14
1020 ? s/fn14
1008 $ hg add 'glob:**fm*'
1021 $ hg add 'glob:**fm*'
1009 adding fm11
1022 adding fm11
1010 $ hg st -S
1023 $ hg st -S
1011 A fm11
1024 A fm11
1012 ? fn12
1025 ? fn12
1013 ? s/fm13
1026 ? s/fm13
1014 ? s/fn14
1027 ? s/fn14
1015 $ hg ci -R s -Am4
1028 $ hg ci -R s -Am4
1016 adding fm13
1029 adding fm13
1017 adding fn14
1030 adding fn14
1018 $ hg ci -Am5
1031 $ hg ci -Am5
1019 adding fn12
1032 adding fn12
1020 Adding with a pattern with -S also adds matches in subrepos
1033 Adding with a pattern with -S also adds matches in subrepos
1021 $ echo c15 > fm15
1034 $ echo c15 > fm15
1022 $ echo c16 > fn16
1035 $ echo c16 > fn16
1023 $ echo c17 > s/fm17
1036 $ echo c17 > s/fm17
1024 $ echo c18 > s/fn18
1037 $ echo c18 > s/fn18
1025 $ hg st -S
1038 $ hg st -S
1026 ? fm15
1039 ? fm15
1027 ? fn16
1040 ? fn16
1028 ? s/fm17
1041 ? s/fm17
1029 ? s/fn18
1042 ? s/fn18
1030 $ hg add -S 'glob:**fm*'
1043 $ hg add -S 'glob:**fm*'
1031 adding fm15
1044 adding fm15
1032 adding s/fm17 (glob)
1045 adding s/fm17 (glob)
1033 $ hg st -S
1046 $ hg st -S
1034 A fm15
1047 A fm15
1035 A s/fm17
1048 A s/fm17
1036 ? fn16
1049 ? fn16
1037 ? s/fn18
1050 ? s/fn18
1038 $ hg ci -R s -Am5
1051 $ hg ci -R s -Am5
1039 adding fn18
1052 adding fn18
1040 $ hg ci -Am6
1053 $ hg ci -Am6
1041 adding fn16
1054 adding fn16
1042
1055
1043 Test behavior of forget for explicit path in subrepo:
1056 Test behavior of forget for explicit path in subrepo:
1044 Forgetting an explicit path in a subrepo untracks the file
1057 Forgetting an explicit path in a subrepo untracks the file
1045 $ echo c19 > s/f19
1058 $ echo c19 > s/f19
1046 $ hg add s/f19
1059 $ hg add s/f19
1047 $ hg st -S
1060 $ hg st -S
1048 A s/f19
1061 A s/f19
1049 $ hg forget s/f19
1062 $ hg forget s/f19
1050 $ hg st -S
1063 $ hg st -S
1051 ? s/f19
1064 ? s/f19
1052 $ rm s/f19
1065 $ rm s/f19
1053 $ cd ..
1066 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now