##// END OF EJS Templates
subrepo: add table-based dispatch for subrepo types
Augie Fackler -
r10177:5ca0d220 default
parent child Browse files
Show More
@@ -1,245 +1,255 b''
1 1 # subrepo.py - sub-repository handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 import errno, os
9 9 from i18n import _
10 10 import config, util, node, error
11 11 hg = None
12 12
13 nullstate = ('', '')
13 nullstate = ('', '', 'empty')
14 14
15 15 def state(ctx):
16 16 p = config.config()
17 17 def read(f, sections=None, remap=None):
18 18 if f in ctx:
19 19 p.parse(f, ctx[f].data(), sections, remap, read)
20 20 else:
21 21 raise util.Abort(_("subrepo spec file %s not found") % f)
22 22
23 23 if '.hgsub' in ctx:
24 24 read('.hgsub')
25 25
26 26 rev = {}
27 27 if '.hgsubstate' in ctx:
28 28 try:
29 29 for l in ctx['.hgsubstate'].data().splitlines():
30 30 revision, path = l.split(" ", 1)
31 31 rev[path] = revision
32 32 except IOError, err:
33 33 if err.errno != errno.ENOENT:
34 34 raise
35 35
36 36 state = {}
37 37 for path, src in p[''].items():
38 state[path] = (src, rev.get(path, ''))
38 kind = 'hg'
39 if src.startswith('['):
40 if ']' not in src:
41 raise util.Abort(_('missing ] in subrepo source'))
42 kind, src = src.split(']', 1)
43 kind = kind[1:]
44 state[path] = (src, rev.get(path, ''), kind)
39 45
40 46 return state
41 47
42 48 def writestate(repo, state):
43 49 repo.wwrite('.hgsubstate',
44 50 ''.join(['%s %s\n' % (state[s][1], s)
45 51 for s in sorted(state)]), '')
46 52
47 53 def submerge(repo, wctx, mctx, actx):
48 54 # working context, merging context, ancestor context
49 55 if mctx == actx: # backwards?
50 56 actx = wctx.p1()
51 57 s1 = wctx.substate
52 58 s2 = mctx.substate
53 59 sa = actx.substate
54 60 sm = {}
55 61
56 62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
57 63
58 64 def debug(s, msg, r=""):
59 65 if r:
60 r = "%s:%s" % r
66 r = "%s:%s:%s" % r
61 67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
62 68
63 69 for s, l in s1.items():
64 70 if wctx != actx and wctx.sub(s).dirty():
65 71 l = (l[0], l[1] + "+")
66 72 a = sa.get(s, nullstate)
67 73 if s in s2:
68 74 r = s2[s]
69 75 if l == r or r == a: # no change or local is newer
70 76 sm[s] = l
71 77 continue
72 78 elif l == a: # other side changed
73 79 debug(s, "other changed, get", r)
74 80 wctx.sub(s).get(r)
75 81 sm[s] = r
76 82 elif l[0] != r[0]: # sources differ
77 83 if repo.ui.promptchoice(
78 84 _(' subrepository sources for %s differ\n'
79 85 'use (l)ocal source (%s) or (r)emote source (%s)?')
80 86 % (s, l[0], r[0]),
81 87 (_('&Local'), _('&Remote')), 0):
82 88 debug(s, "prompt changed, get", r)
83 89 wctx.sub(s).get(r)
84 90 sm[s] = r
85 91 elif l[1] == a[1]: # local side is unchanged
86 92 debug(s, "other side changed, get", r)
87 93 wctx.sub(s).get(r)
88 94 sm[s] = r
89 95 else:
90 96 debug(s, "both sides changed, merge with", r)
91 97 wctx.sub(s).merge(r)
92 98 sm[s] = l
93 99 elif l == a: # remote removed, local unchanged
94 100 debug(s, "remote removed, remove")
95 101 wctx.sub(s).remove()
96 102 else:
97 103 if repo.ui.promptchoice(
98 104 _(' local changed subrepository %s which remote removed\n'
99 105 'use (c)hanged version or (d)elete?') % s,
100 106 (_('&Changed'), _('&Delete')), 0):
101 107 debug(s, "prompt remove")
102 108 wctx.sub(s).remove()
103 109
104 110 for s, r in s2.items():
105 111 if s in s1:
106 112 continue
107 113 elif s not in sa:
108 114 debug(s, "remote added, get", r)
109 115 mctx.sub(s).get(r)
110 116 sm[s] = r
111 117 elif r != sa[s]:
112 118 if repo.ui.promptchoice(
113 119 _(' remote changed subrepository %s which local removed\n'
114 120 'use (c)hanged version or (d)elete?') % s,
115 121 (_('&Changed'), _('&Delete')), 0) == 0:
116 122 debug(s, "prompt recreate", r)
117 123 wctx.sub(s).get(r)
118 124 sm[s] = r
119 125
120 126 # record merged .hgsubstate
121 127 writestate(repo, sm)
122 128
123 129 def _abssource(repo, push=False):
124 130 if hasattr(repo, '_subparent'):
125 131 source = repo._subsource
126 132 if source.startswith('/') or '://' in source:
127 133 return source
128 134 parent = _abssource(repo._subparent)
129 135 if '://' in parent:
130 136 if parent[-1] == '/':
131 137 parent = parent[:-1]
132 138 return parent + '/' + source
133 139 return os.path.join(parent, repo._subsource)
134 140 if push and repo.ui.config('paths', 'default-push'):
135 141 return repo.ui.config('paths', 'default-push', repo.root)
136 142 return repo.ui.config('paths', 'default', repo.root)
137 143
138 144 def subrepo(ctx, path):
139 145 # subrepo inherently violates our import layering rules
140 146 # because it wants to make repo objects from deep inside the stack
141 147 # so we manually delay the circular imports to not break
142 148 # scripts that don't use our demand-loading
143 149 global hg
144 150 import hg as h
145 151 hg = h
146 152
147 153 util.path_auditor(ctx._repo.root)(path)
148 154 state = ctx.substate.get(path, nullstate)
149 if state[0].startswith('['): # future expansion
150 raise error.Abort('unknown subrepo source %s' % state[0])
151 return hgsubrepo(ctx, path, state)
155 if state[2] not in types:
156 raise util.Abort(_('unknown subrepo type %s') % t)
157 return types[state[2]](ctx, path, state[:2])
152 158
153 159 # subrepo classes need to implement the following methods:
154 160 # __init__(self, ctx, path, state)
155 161 # dirty(self): returns true if the dirstate of the subrepo
156 162 # does not match current stored state
157 163 # commit(self, text, user, date): commit the current changes
158 164 # to the subrepo with the given log message. Use given
159 165 # user and date if possible. Return the new state of the subrepo.
160 166 # remove(self): remove the subrepo (should verify the dirstate
161 167 # is not dirty first)
162 168 # get(self, state): run whatever commands are needed to put the
163 169 # subrepo into this state
164 170 # merge(self, state): merge currently-saved state with the new state.
165 171 # push(self, force): perform whatever action is analagous to 'hg push'
166 172 # This may be a no-op on some systems.
167 173
168 174 class hgsubrepo(object):
169 175 def __init__(self, ctx, path, state):
170 176 self._path = path
171 177 self._state = state
172 178 r = ctx._repo
173 179 root = r.wjoin(path)
174 180 if os.path.exists(os.path.join(root, '.hg')):
175 181 self._repo = hg.repository(r.ui, root)
176 182 else:
177 183 util.makedirs(root)
178 184 self._repo = hg.repository(r.ui, root, create=True)
179 185 f = file(os.path.join(root, '.hg', 'hgrc'), 'w')
180 186 f.write('[paths]\ndefault = %s\n' % state[0])
181 187 f.close()
182 188 self._repo._subparent = r
183 189 self._repo._subsource = state[0]
184 190
185 191 def dirty(self):
186 192 r = self._state[1]
187 193 if r == '':
188 194 return True
189 195 w = self._repo[None]
190 196 if w.p1() != self._repo[r]: # version checked out changed
191 197 return True
192 198 return w.dirty() # working directory changed
193 199
194 200 def commit(self, text, user, date):
195 201 self._repo.ui.debug("committing subrepo %s\n" % self._path)
196 202 n = self._repo.commit(text, user, date)
197 203 if not n:
198 204 return self._repo['.'].hex() # different version checked out
199 205 return node.hex(n)
200 206
201 207 def remove(self):
202 208 # we can't fully delete the repository as it may contain
203 209 # local-only history
204 210 self._repo.ui.note(_('removing subrepo %s\n') % self._path)
205 211 hg.clean(self._repo, node.nullid, False)
206 212
207 213 def _get(self, state):
208 source, revision = state
214 source, revision, kind = state
209 215 try:
210 216 self._repo.lookup(revision)
211 217 except error.RepoError:
212 218 self._repo._subsource = source
213 219 self._repo.ui.status(_('pulling subrepo %s\n') % self._path)
214 220 srcurl = _abssource(self._repo)
215 221 other = hg.repository(self._repo.ui, srcurl)
216 222 self._repo.pull(other)
217 223
218 224 def get(self, state):
219 225 self._get(state)
220 source, revision = state
226 source, revision, kind = state
221 227 self._repo.ui.debug("getting subrepo %s\n" % self._path)
222 228 hg.clean(self._repo, revision, False)
223 229
224 230 def merge(self, state):
225 231 self._get(state)
226 232 cur = self._repo['.']
227 233 dst = self._repo[state[1]]
228 234 if dst.ancestor(cur) == cur:
229 235 self._repo.ui.debug("updating subrepo %s\n" % self._path)
230 236 hg.update(self._repo, state[1])
231 237 else:
232 238 self._repo.ui.debug("merging subrepo %s\n" % self._path)
233 239 hg.merge(self._repo, state[1], remind=False)
234 240
235 241 def push(self, force):
236 242 # push subrepos depth-first for coherent ordering
237 243 c = self._repo['']
238 244 subs = c.substate # only repos that are committed
239 245 for s in sorted(subs):
240 246 c.sub(s).push(force)
241 247
242 248 self._repo.ui.status(_('pushing subrepo %s\n') % self._path)
243 249 dsturl = _abssource(self._repo, True)
244 250 other = hg.repository(self._repo.ui, dsturl)
245 251 self._repo.push(other, force)
252
253 types = {
254 'hg': hgsubrepo,
255 }
@@ -1,106 +1,112 b''
1 1 #!/bin/sh
2 2
3 3 rm -rf sub
4 4 mkdir sub
5 5 cd sub
6 6 hg init t
7 7 cd t
8 8
9 9 echo % first revision, no sub
10 10 echo a > a
11 11 hg ci -Am0
12 12
13 13 echo % add first sub
14 14 echo s = s > .hgsub
15 15 hg add .hgsub
16 16 hg init s
17 17 echo a > s/a
18 18 hg -R s ci -Ams0
19 19 hg ci -m1
20 20
21 21 echo % add sub sub
22 22 echo ss = ss > s/.hgsub
23 23 hg init s/ss
24 24 echo a > s/ss/a
25 25 hg -R s add s/.hgsub
26 26 hg -R s/ss add s/ss/a
27 27 hg ci -m2
28 28
29 29 echo % bump sub rev
30 30 echo b > s/a
31 31 hg -R s ci -ms1
32 32 hg ci -m3
33 33
34 34 echo % leave sub dirty
35 35 echo c > s/a
36 36 hg ci -m4
37 37 hg tip -R s
38 38
39 39 echo % check caching
40 40 hg co 0
41 41 hg debugsub
42 42 echo % restore
43 43 hg co
44 44 hg debugsub
45 45
46 46 echo % new branch for merge tests
47 47 hg co 1
48 48 echo t = t >> .hgsub
49 49 hg init t
50 50 echo t > t/t
51 51 hg -R t add t
52 52 echo % 5
53 53 hg ci -m5 # add sub
54 54 echo t2 > t/t
55 55 echo % 6
56 56 hg st -R s
57 57 hg ci -m6 # change sub
58 58 hg debugsub
59 59 echo t3 > t/t
60 60 echo % 7
61 61 hg ci -m7 # change sub again for conflict test
62 62 hg rm .hgsub
63 63 echo % 8
64 64 hg ci -m8 # remove sub
65 65
66 66 echo % merge tests
67 67 hg co -C 3
68 68 hg merge 5 # test adding
69 69 hg debugsub
70 70 hg ci -m9
71 71 hg merge 6 --debug # test change
72 72 hg debugsub
73 73 echo conflict > t/t
74 74 hg ci -m10
75 75 HGMERGE=internal:merge hg merge --debug 7 # test conflict
76 76 echo % should conflict
77 77 cat t/t
78 78
79 79 echo % clone
80 80 cd ..
81 81 hg clone t tc
82 82 cd tc
83 83 hg debugsub
84 84
85 85 echo % push
86 86 echo bah > t/t
87 87 hg ci -m11
88 88 hg push | sed 's/ .*sub/ ...sub/g'
89 89
90 90 echo % push -f
91 91 echo bah > s/a
92 92 hg ci -m12
93 93 hg push | sed 's/ .*sub/ ...sub/g'
94 94 hg push -f | sed 's/ .*sub/ ...sub/g'
95 95
96 96 echo % update
97 97 cd ../t
98 98 hg up -C # discard our earlier merge
99 99 echo blah > t/t
100 100 hg ci -m13
101 101
102 102 echo % pull
103 103 cd ../tc
104 104 hg pull | sed 's/ .*sub/ ...sub/g'
105 105 hg up # should pull t
106 106 cat t/t
107
108 echo % bogus subrepo path aborts
109 echo 'bogus=[boguspath' >> .hgsub
110 hg ci -m 'bogus subrepo path'
111
112 exit 0
@@ -1,203 +1,205 b''
1 1 % first revision, no sub
2 2 adding a
3 3 % add first sub
4 4 adding a
5 5 committing subrepository s
6 6 % add sub sub
7 7 committing subrepository s
8 8 committing subrepository ss
9 9 % bump sub rev
10 10 committing subrepository s
11 11 % leave sub dirty
12 12 committing subrepository s
13 13 changeset: 3:1c833a7a9e3a
14 14 tag: tip
15 15 user: test
16 16 date: Thu Jan 01 00:00:00 1970 +0000
17 17 summary: 4
18 18
19 19 % check caching
20 20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
21 21 % restore
22 22 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 23 path s
24 24 source s
25 25 revision 1c833a7a9e3a4445c711aaf0f012379cd0d4034e
26 26 % new branch for merge tests
27 27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 28 adding t/t
29 29 % 5
30 30 committing subrepository t
31 31 created new head
32 32 % 6
33 33 committing subrepository t
34 34 path s
35 35 source s
36 36 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
37 37 path t
38 38 source t
39 39 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
40 40 % 7
41 41 committing subrepository t
42 42 % 8
43 43 % merge tests
44 44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 46 (branch merge, don't forget to commit)
47 47 path s
48 48 source s
49 49 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
50 50 path t
51 51 source t
52 52 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
53 53 created new head
54 54 searching for copies back to rev 2
55 55 resolving manifests
56 56 overwrite None partial False
57 57 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
58 58 .hgsubstate: versions differ -> m
59 59 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
60 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad
60 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
61 61 getting subrepo t
62 62 resolving manifests
63 63 overwrite True partial False
64 64 ancestor 60ca1237c194+ local 60ca1237c194+ remote 6747d179aa9a
65 65 t: remote is newer -> g
66 66 getting t
67 67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 68 (branch merge, don't forget to commit)
69 69 path s
70 70 source s
71 71 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
72 72 path t
73 73 source t
74 74 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
75 75 committing subrepository t
76 76 searching for copies back to rev 2
77 77 resolving manifests
78 78 overwrite None partial False
79 79 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
80 80 .hgsubstate: versions differ -> m
81 81 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
82 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4
82 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
83 83 merging subrepo t
84 84 searching for copies back to rev 2
85 85 resolving manifests
86 86 overwrite None partial False
87 87 ancestor 6747d179aa9a local 20a0db6fbf6c+ remote 7af322bc1198
88 88 t: versions differ -> m
89 89 preserving t for resolve of t
90 90 picked tool 'internal:merge' for t (binary False symlink False)
91 91 merging t
92 92 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
93 93 warning: conflicts during merge.
94 94 merging t failed!
95 95 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
96 96 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
97 97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 (branch merge, don't forget to commit)
99 99 % should conflict
100 100 <<<<<<< local
101 101 conflict
102 102 =======
103 103 t3
104 104 >>>>>>> other
105 105 % clone
106 106 updating to branch default
107 107 pulling subrepo s
108 108 requesting all changes
109 109 adding changesets
110 110 adding manifests
111 111 adding file changes
112 112 added 4 changesets with 5 changes to 3 files
113 113 pulling subrepo ss
114 114 requesting all changes
115 115 adding changesets
116 116 adding manifests
117 117 adding file changes
118 118 added 1 changesets with 1 changes to 1 files
119 119 pulling subrepo t
120 120 requesting all changes
121 121 adding changesets
122 122 adding manifests
123 123 adding file changes
124 124 added 4 changesets with 4 changes to 1 files (+1 heads)
125 125 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 126 path s
127 127 source s
128 128 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
129 129 path t
130 130 source t
131 131 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
132 132 % push
133 133 committing subrepository t
134 134 pushing ...sub/t
135 135 pushing ...subrepo ss
136 136 searching for changes
137 137 no changes found
138 138 pushing ...subrepo s
139 139 searching for changes
140 140 no changes found
141 141 pushing ...subrepo t
142 142 searching for changes
143 143 adding changesets
144 144 adding manifests
145 145 adding file changes
146 146 added 1 changesets with 1 changes to 1 files
147 147 searching for changes
148 148 adding changesets
149 149 adding manifests
150 150 adding file changes
151 151 added 1 changesets with 1 changes to 1 files
152 152 % push -f
153 153 committing subrepository s
154 154 abort: push creates new remote heads!
155 155 pushing ...sub/t
156 156 pushing ...subrepo ss
157 157 searching for changes
158 158 no changes found
159 159 pushing ...subrepo s
160 160 searching for changes
161 161 (did you forget to merge? use push -f to force)
162 162 pushing ...subrepo t
163 163 searching for changes
164 164 no changes found
165 165 searching for changes
166 166 adding changesets
167 167 adding manifests
168 168 adding file changes
169 169 added 1 changesets with 1 changes to 1 files
170 170 pushing ...sub/t
171 171 pushing ...subrepo ss
172 172 searching for changes
173 173 no changes found
174 174 pushing ...subrepo s
175 175 searching for changes
176 176 adding changesets
177 177 adding manifests
178 178 adding file changes
179 179 added 1 changesets with 1 changes to 1 files (+1 heads)
180 180 pushing ...subrepo t
181 181 searching for changes
182 182 no changes found
183 183 searching for changes
184 184 no changes found
185 185 % update
186 186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 187 committing subrepository t
188 188 % pull
189 189 pulling ...sub/t
190 190 searching for changes
191 191 adding changesets
192 192 adding manifests
193 193 adding file changes
194 194 added 1 changesets with 1 changes to 1 files
195 195 (run 'hg update' to get a working copy)
196 196 pulling subrepo t
197 197 searching for changes
198 198 adding changesets
199 199 adding manifests
200 200 adding file changes
201 201 added 1 changesets with 1 changes to 1 files
202 202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 203 blah
204 % bogus subrepo path aborts
205 abort: missing ] in subrepo source
General Comments 0
You need to be logged in to leave comments. Login now