##// END OF EJS Templates
subrepoutil: use relative path for looking up config `%include`s...
Martin von Zweigbergk -
r45815:0323972f default
parent child Browse files
Show More
@@ -1,459 +1,459 b''
1 # subrepoutil.py - sub-repository operations and substate handling
1 # subrepoutil.py - sub-repository operations and substate handling
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import posixpath
12 import posixpath
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .pycompat import getattr
16 from .pycompat import getattr
17 from . import (
17 from . import (
18 config,
18 config,
19 error,
19 error,
20 filemerge,
20 filemerge,
21 pathutil,
21 pathutil,
22 phases,
22 phases,
23 pycompat,
23 pycompat,
24 util,
24 util,
25 )
25 )
26 from .utils import stringutil
26 from .utils import stringutil
27
27
28 nullstate = (b'', b'', b'empty')
28 nullstate = (b'', b'', b'empty')
29
29
30
30
31 def state(ctx, ui):
31 def state(ctx, ui):
32 """return a state dict, mapping subrepo paths configured in .hgsub
32 """return a state dict, mapping subrepo paths configured in .hgsub
33 to tuple: (source from .hgsub, revision from .hgsubstate, kind
33 to tuple: (source from .hgsub, revision from .hgsubstate, kind
34 (key in types dict))
34 (key in types dict))
35 """
35 """
36 p = config.config()
36 p = config.config()
37 repo = ctx.repo()
37 repo = ctx.repo()
38
38
39 def read(rel, f, sections=None, remap=None):
39 def read(f, abs, sections=None, remap=None):
40 if f in ctx:
40 if f in ctx:
41 try:
41 try:
42 data = ctx[f].data()
42 data = ctx[f].data()
43 except IOError as err:
43 except IOError as err:
44 if err.errno != errno.ENOENT:
44 if err.errno != errno.ENOENT:
45 raise
45 raise
46 # handle missing subrepo spec files as removed
46 # handle missing subrepo spec files as removed
47 ui.warn(
47 ui.warn(
48 _(b"warning: subrepo spec file \'%s\' not found\n")
48 _(b"warning: subrepo spec file \'%s\' not found\n")
49 % repo.pathto(f)
49 % repo.pathto(f)
50 )
50 )
51 return
51 return
52 p.parse(f, data, sections, remap, read)
52 p.parse(f, data, sections, remap, read)
53 else:
53 else:
54 raise error.Abort(
54 raise error.Abort(
55 _(b"subrepo spec file \'%s\' not found") % repo.pathto(f)
55 _(b"subrepo spec file \'%s\' not found") % repo.pathto(f)
56 )
56 )
57
57
58 if b'.hgsub' in ctx:
58 if b'.hgsub' in ctx:
59 read(b'.hgsub', b'.hgsub')
59 read(b'.hgsub', b'.hgsub')
60
60
61 for path, src in ui.configitems(b'subpaths'):
61 for path, src in ui.configitems(b'subpaths'):
62 p.set(b'subpaths', path, src, ui.configsource(b'subpaths', path))
62 p.set(b'subpaths', path, src, ui.configsource(b'subpaths', path))
63
63
64 rev = {}
64 rev = {}
65 if b'.hgsubstate' in ctx:
65 if b'.hgsubstate' in ctx:
66 try:
66 try:
67 for i, l in enumerate(ctx[b'.hgsubstate'].data().splitlines()):
67 for i, l in enumerate(ctx[b'.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(b" ", 1)
72 revision, path = l.split(b" ", 1)
73 except ValueError:
73 except ValueError:
74 raise error.Abort(
74 raise error.Abort(
75 _(
75 _(
76 b"invalid subrepository revision "
76 b"invalid subrepository revision "
77 b"specifier in \'%s\' line %d"
77 b"specifier in \'%s\' line %d"
78 )
78 )
79 % (repo.pathto(b'.hgsubstate'), (i + 1))
79 % (repo.pathto(b'.hgsubstate'), (i + 1))
80 )
80 )
81 rev[path] = revision
81 rev[path] = revision
82 except IOError as err:
82 except IOError as err:
83 if err.errno != errno.ENOENT:
83 if err.errno != errno.ENOENT:
84 raise
84 raise
85
85
86 def remap(src):
86 def remap(src):
87 for pattern, repl in p.items(b'subpaths'):
87 for pattern, repl in p.items(b'subpaths'):
88 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
88 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
89 # does a string decode.
89 # does a string decode.
90 repl = stringutil.escapestr(repl)
90 repl = stringutil.escapestr(repl)
91 # However, we still want to allow back references to go
91 # However, we still want to allow back references to go
92 # through unharmed, so we turn r'\\1' into r'\1'. Again,
92 # through unharmed, so we turn r'\\1' into r'\1'. Again,
93 # extra escapes are needed because re.sub string decodes.
93 # extra escapes are needed because re.sub string decodes.
94 repl = re.sub(br'\\\\([0-9]+)', br'\\\1', repl)
94 repl = re.sub(br'\\\\([0-9]+)', br'\\\1', repl)
95 try:
95 try:
96 src = re.sub(pattern, repl, src, 1)
96 src = re.sub(pattern, repl, src, 1)
97 except re.error as e:
97 except re.error as e:
98 raise error.Abort(
98 raise error.Abort(
99 _(b"bad subrepository pattern in %s: %s")
99 _(b"bad subrepository pattern in %s: %s")
100 % (
100 % (
101 p.source(b'subpaths', pattern),
101 p.source(b'subpaths', pattern),
102 stringutil.forcebytestr(e),
102 stringutil.forcebytestr(e),
103 )
103 )
104 )
104 )
105 return src
105 return src
106
106
107 state = {}
107 state = {}
108 for path, src in p[b''].items():
108 for path, src in p[b''].items():
109 kind = b'hg'
109 kind = b'hg'
110 if src.startswith(b'['):
110 if src.startswith(b'['):
111 if b']' not in src:
111 if b']' not in src:
112 raise error.Abort(_(b'missing ] in subrepository source'))
112 raise error.Abort(_(b'missing ] in subrepository source'))
113 kind, src = src.split(b']', 1)
113 kind, src = src.split(b']', 1)
114 kind = kind[1:]
114 kind = kind[1:]
115 src = src.lstrip() # strip any extra whitespace after ']'
115 src = src.lstrip() # strip any extra whitespace after ']'
116
116
117 if not util.url(src).isabs():
117 if not util.url(src).isabs():
118 parent = _abssource(repo, abort=False)
118 parent = _abssource(repo, abort=False)
119 if parent:
119 if parent:
120 parent = util.url(parent)
120 parent = util.url(parent)
121 parent.path = posixpath.join(parent.path or b'', src)
121 parent.path = posixpath.join(parent.path or b'', src)
122 parent.path = posixpath.normpath(parent.path)
122 parent.path = posixpath.normpath(parent.path)
123 joined = bytes(parent)
123 joined = bytes(parent)
124 # Remap the full joined path and use it if it changes,
124 # Remap the full joined path and use it if it changes,
125 # else remap the original source.
125 # else remap the original source.
126 remapped = remap(joined)
126 remapped = remap(joined)
127 if remapped == joined:
127 if remapped == joined:
128 src = remap(src)
128 src = remap(src)
129 else:
129 else:
130 src = remapped
130 src = remapped
131
131
132 src = remap(src)
132 src = remap(src)
133 state[util.pconvert(path)] = (src.strip(), rev.get(path, b''), kind)
133 state[util.pconvert(path)] = (src.strip(), rev.get(path, b''), kind)
134
134
135 return state
135 return state
136
136
137
137
138 def writestate(repo, state):
138 def writestate(repo, state):
139 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
139 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
140 lines = [
140 lines = [
141 b'%s %s\n' % (state[s][1], s)
141 b'%s %s\n' % (state[s][1], s)
142 for s in sorted(state)
142 for s in sorted(state)
143 if state[s][1] != nullstate[1]
143 if state[s][1] != nullstate[1]
144 ]
144 ]
145 repo.wwrite(b'.hgsubstate', b''.join(lines), b'')
145 repo.wwrite(b'.hgsubstate', b''.join(lines), b'')
146
146
147
147
148 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
148 def submerge(repo, wctx, mctx, actx, overwrite, labels=None):
149 """delegated from merge.applyupdates: merging of .hgsubstate file
149 """delegated from merge.applyupdates: merging of .hgsubstate file
150 in working context, merging context and ancestor context"""
150 in working context, merging context and ancestor context"""
151 if mctx == actx: # backwards?
151 if mctx == actx: # backwards?
152 actx = wctx.p1()
152 actx = wctx.p1()
153 s1 = wctx.substate
153 s1 = wctx.substate
154 s2 = mctx.substate
154 s2 = mctx.substate
155 sa = actx.substate
155 sa = actx.substate
156 sm = {}
156 sm = {}
157
157
158 repo.ui.debug(b"subrepo merge %s %s %s\n" % (wctx, mctx, actx))
158 repo.ui.debug(b"subrepo merge %s %s %s\n" % (wctx, mctx, actx))
159
159
160 def debug(s, msg, r=b""):
160 def debug(s, msg, r=b""):
161 if r:
161 if r:
162 r = b"%s:%s:%s" % r
162 r = b"%s:%s:%s" % r
163 repo.ui.debug(b" subrepo %s: %s %s\n" % (s, msg, r))
163 repo.ui.debug(b" subrepo %s: %s %s\n" % (s, msg, r))
164
164
165 promptssrc = filemerge.partextras(labels)
165 promptssrc = filemerge.partextras(labels)
166 for s, l in sorted(pycompat.iteritems(s1)):
166 for s, l in sorted(pycompat.iteritems(s1)):
167 a = sa.get(s, nullstate)
167 a = sa.get(s, nullstate)
168 ld = l # local state with possible dirty flag for compares
168 ld = l # local state with possible dirty flag for compares
169 if wctx.sub(s).dirty():
169 if wctx.sub(s).dirty():
170 ld = (l[0], l[1] + b"+")
170 ld = (l[0], l[1] + b"+")
171 if wctx == actx: # overwrite
171 if wctx == actx: # overwrite
172 a = ld
172 a = ld
173
173
174 prompts = promptssrc.copy()
174 prompts = promptssrc.copy()
175 prompts[b's'] = s
175 prompts[b's'] = s
176 if s in s2:
176 if s in s2:
177 r = s2[s]
177 r = s2[s]
178 if ld == r or r == a: # no change or local is newer
178 if ld == r or r == a: # no change or local is newer
179 sm[s] = l
179 sm[s] = l
180 continue
180 continue
181 elif ld == a: # other side changed
181 elif ld == a: # other side changed
182 debug(s, b"other changed, get", r)
182 debug(s, b"other changed, get", r)
183 wctx.sub(s).get(r, overwrite)
183 wctx.sub(s).get(r, overwrite)
184 sm[s] = r
184 sm[s] = r
185 elif ld[0] != r[0]: # sources differ
185 elif ld[0] != r[0]: # sources differ
186 prompts[b'lo'] = l[0]
186 prompts[b'lo'] = l[0]
187 prompts[b'ro'] = r[0]
187 prompts[b'ro'] = r[0]
188 if repo.ui.promptchoice(
188 if repo.ui.promptchoice(
189 _(
189 _(
190 b' subrepository sources for %(s)s differ\n'
190 b' subrepository sources for %(s)s differ\n'
191 b'you can use (l)ocal%(l)s source (%(lo)s)'
191 b'you can use (l)ocal%(l)s source (%(lo)s)'
192 b' or (r)emote%(o)s source (%(ro)s).\n'
192 b' or (r)emote%(o)s source (%(ro)s).\n'
193 b'what do you want to do?'
193 b'what do you want to do?'
194 b'$$ &Local $$ &Remote'
194 b'$$ &Local $$ &Remote'
195 )
195 )
196 % prompts,
196 % prompts,
197 0,
197 0,
198 ):
198 ):
199 debug(s, b"prompt changed, get", r)
199 debug(s, b"prompt changed, get", r)
200 wctx.sub(s).get(r, overwrite)
200 wctx.sub(s).get(r, overwrite)
201 sm[s] = r
201 sm[s] = r
202 elif ld[1] == a[1]: # local side is unchanged
202 elif ld[1] == a[1]: # local side is unchanged
203 debug(s, b"other side changed, get", r)
203 debug(s, b"other side changed, get", r)
204 wctx.sub(s).get(r, overwrite)
204 wctx.sub(s).get(r, overwrite)
205 sm[s] = r
205 sm[s] = r
206 else:
206 else:
207 debug(s, b"both sides changed")
207 debug(s, b"both sides changed")
208 srepo = wctx.sub(s)
208 srepo = wctx.sub(s)
209 prompts[b'sl'] = srepo.shortid(l[1])
209 prompts[b'sl'] = srepo.shortid(l[1])
210 prompts[b'sr'] = srepo.shortid(r[1])
210 prompts[b'sr'] = srepo.shortid(r[1])
211 option = repo.ui.promptchoice(
211 option = repo.ui.promptchoice(
212 _(
212 _(
213 b' subrepository %(s)s diverged (local revision: %(sl)s, '
213 b' subrepository %(s)s diverged (local revision: %(sl)s, '
214 b'remote revision: %(sr)s)\n'
214 b'remote revision: %(sr)s)\n'
215 b'you can (m)erge, keep (l)ocal%(l)s or keep '
215 b'you can (m)erge, keep (l)ocal%(l)s or keep '
216 b'(r)emote%(o)s.\n'
216 b'(r)emote%(o)s.\n'
217 b'what do you want to do?'
217 b'what do you want to do?'
218 b'$$ &Merge $$ &Local $$ &Remote'
218 b'$$ &Merge $$ &Local $$ &Remote'
219 )
219 )
220 % prompts,
220 % prompts,
221 0,
221 0,
222 )
222 )
223 if option == 0:
223 if option == 0:
224 wctx.sub(s).merge(r)
224 wctx.sub(s).merge(r)
225 sm[s] = l
225 sm[s] = l
226 debug(s, b"merge with", r)
226 debug(s, b"merge with", r)
227 elif option == 1:
227 elif option == 1:
228 sm[s] = l
228 sm[s] = l
229 debug(s, b"keep local subrepo revision", l)
229 debug(s, b"keep local subrepo revision", l)
230 else:
230 else:
231 wctx.sub(s).get(r, overwrite)
231 wctx.sub(s).get(r, overwrite)
232 sm[s] = r
232 sm[s] = r
233 debug(s, b"get remote subrepo revision", r)
233 debug(s, b"get remote subrepo revision", r)
234 elif ld == a: # remote removed, local unchanged
234 elif ld == a: # remote removed, local unchanged
235 debug(s, b"remote removed, remove")
235 debug(s, b"remote removed, remove")
236 wctx.sub(s).remove()
236 wctx.sub(s).remove()
237 elif a == nullstate: # not present in remote or ancestor
237 elif a == nullstate: # not present in remote or ancestor
238 debug(s, b"local added, keep")
238 debug(s, b"local added, keep")
239 sm[s] = l
239 sm[s] = l
240 continue
240 continue
241 else:
241 else:
242 if repo.ui.promptchoice(
242 if repo.ui.promptchoice(
243 _(
243 _(
244 b' local%(l)s changed subrepository %(s)s'
244 b' local%(l)s changed subrepository %(s)s'
245 b' which remote%(o)s removed\n'
245 b' which remote%(o)s removed\n'
246 b'use (c)hanged version or (d)elete?'
246 b'use (c)hanged version or (d)elete?'
247 b'$$ &Changed $$ &Delete'
247 b'$$ &Changed $$ &Delete'
248 )
248 )
249 % prompts,
249 % prompts,
250 0,
250 0,
251 ):
251 ):
252 debug(s, b"prompt remove")
252 debug(s, b"prompt remove")
253 wctx.sub(s).remove()
253 wctx.sub(s).remove()
254
254
255 for s, r in sorted(s2.items()):
255 for s, r in sorted(s2.items()):
256 if s in s1:
256 if s in s1:
257 continue
257 continue
258 elif s not in sa:
258 elif s not in sa:
259 debug(s, b"remote added, get", r)
259 debug(s, b"remote added, get", r)
260 mctx.sub(s).get(r)
260 mctx.sub(s).get(r)
261 sm[s] = r
261 sm[s] = r
262 elif r != sa[s]:
262 elif r != sa[s]:
263 prompts = promptssrc.copy()
263 prompts = promptssrc.copy()
264 prompts[b's'] = s
264 prompts[b's'] = s
265 if (
265 if (
266 repo.ui.promptchoice(
266 repo.ui.promptchoice(
267 _(
267 _(
268 b' remote%(o)s changed subrepository %(s)s'
268 b' remote%(o)s changed subrepository %(s)s'
269 b' which local%(l)s removed\n'
269 b' which local%(l)s removed\n'
270 b'use (c)hanged version or (d)elete?'
270 b'use (c)hanged version or (d)elete?'
271 b'$$ &Changed $$ &Delete'
271 b'$$ &Changed $$ &Delete'
272 )
272 )
273 % prompts,
273 % prompts,
274 0,
274 0,
275 )
275 )
276 == 0
276 == 0
277 ):
277 ):
278 debug(s, b"prompt recreate", r)
278 debug(s, b"prompt recreate", r)
279 mctx.sub(s).get(r)
279 mctx.sub(s).get(r)
280 sm[s] = r
280 sm[s] = r
281
281
282 # record merged .hgsubstate
282 # record merged .hgsubstate
283 writestate(repo, sm)
283 writestate(repo, sm)
284 return sm
284 return sm
285
285
286
286
287 def precommit(ui, wctx, status, match, force=False):
287 def precommit(ui, wctx, status, match, force=False):
288 """Calculate .hgsubstate changes that should be applied before committing
288 """Calculate .hgsubstate changes that should be applied before committing
289
289
290 Returns (subs, commitsubs, newstate) where
290 Returns (subs, commitsubs, newstate) where
291 - subs: changed subrepos (including dirty ones)
291 - subs: changed subrepos (including dirty ones)
292 - commitsubs: dirty subrepos which the caller needs to commit recursively
292 - commitsubs: dirty subrepos which the caller needs to commit recursively
293 - newstate: new state dict which the caller must write to .hgsubstate
293 - newstate: new state dict which the caller must write to .hgsubstate
294
294
295 This also updates the given status argument.
295 This also updates the given status argument.
296 """
296 """
297 subs = []
297 subs = []
298 commitsubs = set()
298 commitsubs = set()
299 newstate = wctx.substate.copy()
299 newstate = wctx.substate.copy()
300
300
301 # only manage subrepos and .hgsubstate if .hgsub is present
301 # only manage subrepos and .hgsubstate if .hgsub is present
302 if b'.hgsub' in wctx:
302 if b'.hgsub' in wctx:
303 # we'll decide whether to track this ourselves, thanks
303 # we'll decide whether to track this ourselves, thanks
304 for c in status.modified, status.added, status.removed:
304 for c in status.modified, status.added, status.removed:
305 if b'.hgsubstate' in c:
305 if b'.hgsubstate' in c:
306 c.remove(b'.hgsubstate')
306 c.remove(b'.hgsubstate')
307
307
308 # compare current state to last committed state
308 # compare current state to last committed state
309 # build new substate based on last committed state
309 # build new substate based on last committed state
310 oldstate = wctx.p1().substate
310 oldstate = wctx.p1().substate
311 for s in sorted(newstate.keys()):
311 for s in sorted(newstate.keys()):
312 if not match(s):
312 if not match(s):
313 # ignore working copy, use old state if present
313 # ignore working copy, use old state if present
314 if s in oldstate:
314 if s in oldstate:
315 newstate[s] = oldstate[s]
315 newstate[s] = oldstate[s]
316 continue
316 continue
317 if not force:
317 if not force:
318 raise error.Abort(
318 raise error.Abort(
319 _(b"commit with new subrepo %s excluded") % s
319 _(b"commit with new subrepo %s excluded") % s
320 )
320 )
321 dirtyreason = wctx.sub(s).dirtyreason(True)
321 dirtyreason = wctx.sub(s).dirtyreason(True)
322 if dirtyreason:
322 if dirtyreason:
323 if not ui.configbool(b'ui', b'commitsubrepos'):
323 if not ui.configbool(b'ui', b'commitsubrepos'):
324 raise error.Abort(
324 raise error.Abort(
325 dirtyreason,
325 dirtyreason,
326 hint=_(b"use --subrepos for recursive commit"),
326 hint=_(b"use --subrepos for recursive commit"),
327 )
327 )
328 subs.append(s)
328 subs.append(s)
329 commitsubs.add(s)
329 commitsubs.add(s)
330 else:
330 else:
331 bs = wctx.sub(s).basestate()
331 bs = wctx.sub(s).basestate()
332 newstate[s] = (newstate[s][0], bs, newstate[s][2])
332 newstate[s] = (newstate[s][0], bs, newstate[s][2])
333 if oldstate.get(s, (None, None, None))[1] != bs:
333 if oldstate.get(s, (None, None, None))[1] != bs:
334 subs.append(s)
334 subs.append(s)
335
335
336 # check for removed subrepos
336 # check for removed subrepos
337 for p in wctx.parents():
337 for p in wctx.parents():
338 r = [s for s in p.substate if s not in newstate]
338 r = [s for s in p.substate if s not in newstate]
339 subs += [s for s in r if match(s)]
339 subs += [s for s in r if match(s)]
340 if subs:
340 if subs:
341 if not match(b'.hgsub') and b'.hgsub' in (
341 if not match(b'.hgsub') and b'.hgsub' in (
342 wctx.modified() + wctx.added()
342 wctx.modified() + wctx.added()
343 ):
343 ):
344 raise error.Abort(_(b"can't commit subrepos without .hgsub"))
344 raise error.Abort(_(b"can't commit subrepos without .hgsub"))
345 status.modified.insert(0, b'.hgsubstate')
345 status.modified.insert(0, b'.hgsubstate')
346
346
347 elif b'.hgsub' in status.removed:
347 elif b'.hgsub' in status.removed:
348 # clean up .hgsubstate when .hgsub is removed
348 # clean up .hgsubstate when .hgsub is removed
349 if b'.hgsubstate' in wctx and b'.hgsubstate' not in (
349 if b'.hgsubstate' in wctx and b'.hgsubstate' not in (
350 status.modified + status.added + status.removed
350 status.modified + status.added + status.removed
351 ):
351 ):
352 status.removed.insert(0, b'.hgsubstate')
352 status.removed.insert(0, b'.hgsubstate')
353
353
354 return subs, commitsubs, newstate
354 return subs, commitsubs, newstate
355
355
356
356
357 def reporelpath(repo):
357 def reporelpath(repo):
358 """return path to this (sub)repo as seen from outermost repo"""
358 """return path to this (sub)repo as seen from outermost repo"""
359 parent = repo
359 parent = repo
360 while util.safehasattr(parent, b'_subparent'):
360 while util.safehasattr(parent, b'_subparent'):
361 parent = parent._subparent
361 parent = parent._subparent
362 return repo.root[len(pathutil.normasprefix(parent.root)) :]
362 return repo.root[len(pathutil.normasprefix(parent.root)) :]
363
363
364
364
365 def subrelpath(sub):
365 def subrelpath(sub):
366 """return path to this subrepo as seen from outermost repo"""
366 """return path to this subrepo as seen from outermost repo"""
367 return sub._relpath
367 return sub._relpath
368
368
369
369
370 def _abssource(repo, push=False, abort=True):
370 def _abssource(repo, push=False, abort=True):
371 """return pull/push path of repo - either based on parent repo .hgsub info
371 """return pull/push path of repo - either based on parent repo .hgsub info
372 or on the top repo config. Abort or return None if no source found."""
372 or on the top repo config. Abort or return None if no source found."""
373 if util.safehasattr(repo, b'_subparent'):
373 if util.safehasattr(repo, b'_subparent'):
374 source = util.url(repo._subsource)
374 source = util.url(repo._subsource)
375 if source.isabs():
375 if source.isabs():
376 return bytes(source)
376 return bytes(source)
377 source.path = posixpath.normpath(source.path)
377 source.path = posixpath.normpath(source.path)
378 parent = _abssource(repo._subparent, push, abort=False)
378 parent = _abssource(repo._subparent, push, abort=False)
379 if parent:
379 if parent:
380 parent = util.url(util.pconvert(parent))
380 parent = util.url(util.pconvert(parent))
381 parent.path = posixpath.join(parent.path or b'', source.path)
381 parent.path = posixpath.join(parent.path or b'', source.path)
382 parent.path = posixpath.normpath(parent.path)
382 parent.path = posixpath.normpath(parent.path)
383 return bytes(parent)
383 return bytes(parent)
384 else: # recursion reached top repo
384 else: # recursion reached top repo
385 path = None
385 path = None
386 if util.safehasattr(repo, b'_subtoppath'):
386 if util.safehasattr(repo, b'_subtoppath'):
387 path = repo._subtoppath
387 path = repo._subtoppath
388 elif push and repo.ui.config(b'paths', b'default-push'):
388 elif push and repo.ui.config(b'paths', b'default-push'):
389 path = repo.ui.config(b'paths', b'default-push')
389 path = repo.ui.config(b'paths', b'default-push')
390 elif repo.ui.config(b'paths', b'default'):
390 elif repo.ui.config(b'paths', b'default'):
391 path = repo.ui.config(b'paths', b'default')
391 path = repo.ui.config(b'paths', b'default')
392 elif repo.shared():
392 elif repo.shared():
393 # chop off the .hg component to get the default path form. This has
393 # chop off the .hg component to get the default path form. This has
394 # already run through vfsmod.vfs(..., realpath=True), so it doesn't
394 # already run through vfsmod.vfs(..., realpath=True), so it doesn't
395 # have problems with 'C:'
395 # have problems with 'C:'
396 return os.path.dirname(repo.sharedpath)
396 return os.path.dirname(repo.sharedpath)
397 if path:
397 if path:
398 # issue5770: 'C:\' and 'C:' are not equivalent paths. The former is
398 # issue5770: 'C:\' and 'C:' are not equivalent paths. The former is
399 # as expected: an absolute path to the root of the C: drive. The
399 # as expected: an absolute path to the root of the C: drive. The
400 # latter is a relative path, and works like so:
400 # latter is a relative path, and works like so:
401 #
401 #
402 # C:\>cd C:\some\path
402 # C:\>cd C:\some\path
403 # C:\>D:
403 # C:\>D:
404 # D:\>python -c "import os; print os.path.abspath('C:')"
404 # D:\>python -c "import os; print os.path.abspath('C:')"
405 # C:\some\path
405 # C:\some\path
406 #
406 #
407 # D:\>python -c "import os; print os.path.abspath('C:relative')"
407 # D:\>python -c "import os; print os.path.abspath('C:relative')"
408 # C:\some\path\relative
408 # C:\some\path\relative
409 if util.hasdriveletter(path):
409 if util.hasdriveletter(path):
410 if len(path) == 2 or path[2:3] not in br'\/':
410 if len(path) == 2 or path[2:3] not in br'\/':
411 path = os.path.abspath(path)
411 path = os.path.abspath(path)
412 return path
412 return path
413
413
414 if abort:
414 if abort:
415 raise error.Abort(_(b"default path for subrepository not found"))
415 raise error.Abort(_(b"default path for subrepository not found"))
416
416
417
417
418 def newcommitphase(ui, ctx):
418 def newcommitphase(ui, ctx):
419 commitphase = phases.newcommitphase(ui)
419 commitphase = phases.newcommitphase(ui)
420 substate = getattr(ctx, "substate", None)
420 substate = getattr(ctx, "substate", None)
421 if not substate:
421 if not substate:
422 return commitphase
422 return commitphase
423 check = ui.config(b'phases', b'checksubrepos')
423 check = ui.config(b'phases', b'checksubrepos')
424 if check not in (b'ignore', b'follow', b'abort'):
424 if check not in (b'ignore', b'follow', b'abort'):
425 raise error.Abort(
425 raise error.Abort(
426 _(b'invalid phases.checksubrepos configuration: %s') % check
426 _(b'invalid phases.checksubrepos configuration: %s') % check
427 )
427 )
428 if check == b'ignore':
428 if check == b'ignore':
429 return commitphase
429 return commitphase
430 maxphase = phases.public
430 maxphase = phases.public
431 maxsub = None
431 maxsub = None
432 for s in sorted(substate):
432 for s in sorted(substate):
433 sub = ctx.sub(s)
433 sub = ctx.sub(s)
434 subphase = sub.phase(substate[s][1])
434 subphase = sub.phase(substate[s][1])
435 if maxphase < subphase:
435 if maxphase < subphase:
436 maxphase = subphase
436 maxphase = subphase
437 maxsub = s
437 maxsub = s
438 if commitphase < maxphase:
438 if commitphase < maxphase:
439 if check == b'abort':
439 if check == b'abort':
440 raise error.Abort(
440 raise error.Abort(
441 _(
441 _(
442 b"can't commit in %s phase"
442 b"can't commit in %s phase"
443 b" conflicting %s from subrepository %s"
443 b" conflicting %s from subrepository %s"
444 )
444 )
445 % (
445 % (
446 phases.phasenames[commitphase],
446 phases.phasenames[commitphase],
447 phases.phasenames[maxphase],
447 phases.phasenames[maxphase],
448 maxsub,
448 maxsub,
449 )
449 )
450 )
450 )
451 ui.warn(
451 ui.warn(
452 _(
452 _(
453 b"warning: changes are committed in"
453 b"warning: changes are committed in"
454 b" %s phase from subrepository %s\n"
454 b" %s phase from subrepository %s\n"
455 )
455 )
456 % (phases.phasenames[maxphase], maxsub)
456 % (phases.phasenames[maxphase], maxsub)
457 )
457 )
458 return maxphase
458 return maxphase
459 return commitphase
459 return commitphase
General Comments 0
You need to be logged in to leave comments. Login now