##// END OF EJS Templates
Merge with mpm
Martin Geisler -
r10181:b42186ef merge default
parent child Browse files
Show More
@@ -0,0 +1,72 b''
1 #!/bin/sh
2
3 "$TESTDIR/hghave" svn || exit 80
4
5 escapedwd=$(pwd | \
6 python -c \
7 "import sys,urllib; print urllib.pathname2url(sys.stdin.read().strip())"
8 )
9 filterpath="sed s+$escapedwd+/root+"
10
11 echo % create subversion repo
12
13 SVNREPO="file://$escapedwd/svn-repo"
14 WCROOT="$(pwd)/svn-wc"
15 svnadmin create svn-repo
16 svn co $SVNREPO svn-wc
17 cd svn-wc
18 echo alpha > alpha
19 svn add alpha
20 svn ci -m 'Add alpha'
21 cd ..
22
23 echo % create hg repo
24
25 rm -rf sub
26 mkdir sub
27 cd sub
28 hg init t
29 cd t
30
31 echo % first revision, no sub
32 echo a > a
33 hg ci -Am0
34
35 echo % add first svn sub
36 echo "s = [svn]$SVNREPO" >> .hgsub
37 svn co --quiet $SVNREPO s
38 hg add .hgsub
39 hg ci -m1
40 echo % debugsub
41 hg debugsub | $filterpath
42
43 echo
44 echo % change file in svn and hg, commit
45 echo a >> a
46 echo alpha >> s/alpha
47 hg commit -m 'Message!'
48 hg debugsub | $filterpath
49
50 echo
51 echo a > s/a
52 echo % should be empty despite change to s/a
53 hg st
54
55 echo
56 echo % add a commit from svn
57 pushd "$WCROOT" > /dev/null
58 svn up
59 echo xyz >> alpha
60 svn ci -m 'amend a from svn'
61 popd > /dev/null
62 echo % this commit from hg will fail
63 echo zzz >> s/alpha
64 hg ci -m 'amend alpha from hg'
65
66 echo
67 echo % clone
68 cd ..
69 hg clone t tc
70 cd tc
71 echo % debugsub in clone
72 hg debugsub | $filterpath
@@ -0,0 +1,48 b''
1 % create subversion repo
2 Checked out revision 0.
3 A alpha
4 Adding alpha
5 Transmitting file data .
6 Committed revision 1.
7 % create hg repo
8 % first revision, no sub
9 adding a
10 % add first svn sub
11 committing subrepository s
12 % debugsub
13 path s
14 source file:///root/svn-repo
15 revision 1
16
17 % change file in svn and hg, commit
18 committing subrepository s
19 Sending s/alpha
20 Transmitting file data .
21 Committed revision 2.
22 At revision 2.
23 path s
24 source file:///root/svn-repo
25 revision 2
26
27 % should be empty despite change to s/a
28
29 % add a commit from svn
30 U alpha
31 Updated to revision 2.
32 Sending alpha
33 Transmitting file data .
34 Committed revision 3.
35 % this commit from hg will fail
36 committing subrepository s
37 abort: svn: Commit failed (details follow):
38 svn: File '/alpha' is out of date
39
40 % clone
41 updating to branch default
42 A s/alpha
43 Checked out revision 2.
44 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 % debugsub in clone
46 path s
47 source file:///root/svn-repo
48 revision 2
@@ -32,7 +32,7 b' def timer(func, title=None):'
32 def perfwalk(ui, repo, *pats):
32 def perfwalk(ui, repo, *pats):
33 try:
33 try:
34 m = cmdutil.match(repo, pats, {})
34 m = cmdutil.match(repo, pats, {})
35 timer(lambda: len(list(repo.dirstate.walk(m, True, False))))
35 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
36 except:
36 except:
37 try:
37 try:
38 m = cmdutil.match(repo, pats, {})
38 m = cmdutil.match(repo, pats, {})
@@ -150,4 +150,3 b' cmdtable = {'
150 'perftemplating': (perftemplating, []),
150 'perftemplating': (perftemplating, []),
151 'perfdiffwd': (perfdiffwd, []),
151 'perfdiffwd': (perfdiffwd, []),
152 }
152 }
153
@@ -42,11 +42,11 b' def reposetup(ui, repo):'
42 # to start an inotify server if it won't start.
42 # to start an inotify server if it won't start.
43 _inotifyon = True
43 _inotifyon = True
44
44
45 def status(self, match, ignored, clean, unknown=True):
45 def status(self, match, subrepos, ignored, clean, unknown=True):
46 files = match.files()
46 files = match.files()
47 if '.' in files:
47 if '.' in files:
48 files = []
48 files = []
49 if self._inotifyon and not ignored and not self._dirty:
49 if self._inotifyon and not ignored and not subrepos and not self._dirty:
50 cli = client(ui, repo)
50 cli = client(ui, repo)
51 try:
51 try:
52 result = cli.statusquery(files, match, False,
52 result = cli.statusquery(files, match, False,
@@ -70,7 +70,7 b' def reposetup(ui, repo):'
70 result = r2
70 result = r2
71 return result
71 return result
72 return super(inotifydirstate, self).status(
72 return super(inotifydirstate, self).status(
73 match, ignored, clean, unknown)
73 match, subrepos, ignored, clean, unknown)
74
74
75 repo.dirstate.__class__ = inotifydirstate
75 repo.dirstate.__class__ = inotifydirstate
76
76
@@ -638,7 +638,8 b' class workingctx(changectx):'
638 return self._parents[0].ancestor(c2) # punt on two parents for now
638 return self._parents[0].ancestor(c2) # punt on two parents for now
639
639
640 def walk(self, match):
640 def walk(self, match):
641 return sorted(self._repo.dirstate.walk(match, True, False))
641 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
642 True, False))
642
643
643 def dirty(self, missing=False):
644 def dirty(self, missing=False):
644 "check whether a working directory is modified"
645 "check whether a working directory is modified"
@@ -425,7 +425,7 b' class dirstate(object):'
425 return True
425 return True
426 return False
426 return False
427
427
428 def walk(self, match, unknown, ignored):
428 def walk(self, match, subrepos, unknown, ignored):
429 '''
429 '''
430 Walk recursively through the directory tree, finding all files
430 Walk recursively through the directory tree, finding all files
431 matched by match.
431 matched by match.
@@ -486,7 +486,8 b' class dirstate(object):'
486 files = set(match.files())
486 files = set(match.files())
487 if not files or '.' in files:
487 if not files or '.' in files:
488 files = ['']
488 files = ['']
489 results = {'.hg': None}
489 results = dict.fromkeys(subrepos)
490 results['.hg'] = None
490
491
491 # step 1: find all explicit files
492 # step 1: find all explicit files
492 for ff in sorted(files):
493 for ff in sorted(files):
@@ -564,11 +565,12 b' class dirstate(object):'
564 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
565 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
565 st = None
566 st = None
566 results[nf] = st
567 results[nf] = st
567
568 for s in subrepos:
569 del results[s]
568 del results['.hg']
570 del results['.hg']
569 return results
571 return results
570
572
571 def status(self, match, ignored, clean, unknown):
573 def status(self, match, subrepos, ignored, clean, unknown):
572 '''Determine the status of the working copy relative to the
574 '''Determine the status of the working copy relative to the
573 dirstate and return a tuple of lists (unsure, modified, added,
575 dirstate and return a tuple of lists (unsure, modified, added,
574 removed, deleted, unknown, ignored, clean), where:
576 removed, deleted, unknown, ignored, clean), where:
@@ -609,7 +611,8 b' class dirstate(object):'
609 dadd = deleted.append
611 dadd = deleted.append
610 cadd = clean.append
612 cadd = clean.append
611
613
612 for fn, st in self.walk(match, listunknown, listignored).iteritems():
614 for fn, st in self.walk(match, subrepos, listunknown,
615 listignored).iteritems():
613 if fn not in dmap:
616 if fn not in dmap:
614 if (listignored or match.exact(fn)) and self._dirignore(fn):
617 if (listignored or match.exact(fn)) and self._dirignore(fn):
615 if listignored:
618 if listignored:
@@ -1000,7 +1000,9 b' class localrepository(repo.repository):'
1000 match.bad = bad
1000 match.bad = bad
1001
1001
1002 if working: # we need to scan the working dir
1002 if working: # we need to scan the working dir
1003 s = self.dirstate.status(match, listignored, listclean, listunknown)
1003 subrepos = ctx1.substate.keys()
1004 s = self.dirstate.status(match, subrepos, listignored,
1005 listclean, listunknown)
1004 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1006 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1005
1007
1006 # check for any possibly clean files
1008 # check for any possibly clean files
@@ -5,23 +5,23 b''
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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 import errno, os
8 import errno, os, re
9 from i18n import _
9 from i18n import _
10 import config, util, node, error
10 import config, util, node, error
11 hg = None
11 hg = None
12
12
13 nullstate = ('', '')
13 nullstate = ('', '', 'empty')
14
14
15 def state(ctx):
15 def state(ctx):
16 p = config.config()
16 p = config.config()
17 def read(f, sections=None, remap=None):
17 def read(f, sections=None, remap=None):
18 if f in ctx:
18 if f in ctx:
19 try:
19 p.parse(f, ctx[f].data(), sections, remap, read)
20 p.parse(f, ctx[f].data(), sections, remap)
20 else:
21 except IOError, err:
21 raise util.Abort(_("subrepo spec file %s not found") % f)
22 if err.errno != errno.ENOENT:
22
23 raise
23 if '.hgsub' in ctx:
24 read('.hgsub')
24 read('.hgsub')
25
25
26 rev = {}
26 rev = {}
27 if '.hgsubstate' in ctx:
27 if '.hgsubstate' in ctx:
@@ -35,7 +35,13 b' def state(ctx):'
35
35
36 state = {}
36 state = {}
37 for path, src in p[''].items():
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 return state
46 return state
41
47
@@ -45,6 +51,7 b' def writestate(repo, state):'
45 for s in sorted(state)]), '')
51 for s in sorted(state)]), '')
46
52
47 def submerge(repo, wctx, mctx, actx):
53 def submerge(repo, wctx, mctx, actx):
54 # working context, merging context, ancestor context
48 if mctx == actx: # backwards?
55 if mctx == actx: # backwards?
49 actx = wctx.p1()
56 actx = wctx.p1()
50 s1 = wctx.substate
57 s1 = wctx.substate
@@ -56,7 +63,7 b' def submerge(repo, wctx, mctx, actx):'
56
63
57 def debug(s, msg, r=""):
64 def debug(s, msg, r=""):
58 if r:
65 if r:
59 r = "%s:%s" % r
66 r = "%s:%s:%s" % r
60 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
61
68
62 for s, l in s1.items():
69 for s, l in s1.items():
@@ -105,7 +112,7 b' def submerge(repo, wctx, mctx, actx):'
105 continue
112 continue
106 elif s not in sa:
113 elif s not in sa:
107 debug(s, "remote added, get", r)
114 debug(s, "remote added, get", r)
108 wctx.sub(s).get(r)
115 mctx.sub(s).get(r)
109 sm[s] = r
116 sm[s] = r
110 elif r != sa[s]:
117 elif r != sa[s]:
111 if repo.ui.promptchoice(
118 if repo.ui.promptchoice(
@@ -145,9 +152,9 b' def subrepo(ctx, path):'
145
152
146 util.path_auditor(ctx._repo.root)(path)
153 util.path_auditor(ctx._repo.root)(path)
147 state = ctx.substate.get(path, nullstate)
154 state = ctx.substate.get(path, nullstate)
148 if state[0].startswith('['): # future expansion
155 if state[2] not in types:
149 raise error.Abort('unknown subrepo source %s' % state[0])
156 raise util.Abort(_('unknown subrepo type %s') % t)
150 return hgsubrepo(ctx, path, state)
157 return types[state[2]](ctx, path, state[:2])
151
158
152 # subrepo classes need to implement the following methods:
159 # subrepo classes need to implement the following methods:
153 # __init__(self, ctx, path, state)
160 # __init__(self, ctx, path, state)
@@ -204,7 +211,7 b' class hgsubrepo(object):'
204 hg.clean(self._repo, node.nullid, False)
211 hg.clean(self._repo, node.nullid, False)
205
212
206 def _get(self, state):
213 def _get(self, state):
207 source, revision = state
214 source, revision, kind = state
208 try:
215 try:
209 self._repo.lookup(revision)
216 self._repo.lookup(revision)
210 except error.RepoError:
217 except error.RepoError:
@@ -216,7 +223,7 b' class hgsubrepo(object):'
216
223
217 def get(self, state):
224 def get(self, state):
218 self._get(state)
225 self._get(state)
219 source, revision = state
226 source, revision, kind = state
220 self._repo.ui.debug("getting subrepo %s\n" % self._path)
227 self._repo.ui.debug("getting subrepo %s\n" % self._path)
221 hg.clean(self._repo, revision, False)
228 hg.clean(self._repo, revision, False)
222
229
@@ -242,3 +249,89 b' class hgsubrepo(object):'
242 dsturl = _abssource(self._repo, True)
249 dsturl = _abssource(self._repo, True)
243 other = hg.repository(self._repo.ui, dsturl)
250 other = hg.repository(self._repo.ui, dsturl)
244 self._repo.push(other, force)
251 self._repo.push(other, force)
252
253 class svnsubrepo(object):
254 def __init__(self, ctx, path, state):
255 self._path = path
256 self._state = state
257 self._ctx = ctx
258 self._ui = ctx._repo.ui
259
260 def _svncommand(self, commands):
261 cmd = ['svn'] + commands + [self._path]
262 cmd = [util.shellquote(arg) for arg in cmd]
263 cmd = util.quotecommand(' '.join(cmd))
264 write, read, err = util.popen3(cmd)
265 retdata = read.read()
266 err = err.read().strip()
267 if err:
268 raise util.Abort(err)
269 return retdata
270
271 def _wcrev(self):
272 info = self._svncommand(['info'])
273 mat = re.search('Revision: ([\d]+)\n', info)
274 if not mat:
275 return 0
276 return mat.groups()[0]
277
278 def _url(self):
279 info = self._svncommand(['info'])
280 mat = re.search('URL: ([^\n]+)\n', info)
281 if not mat:
282 return 0
283 return mat.groups()[0]
284
285 def _wcclean(self):
286 status = self._svncommand(['status'])
287 status = '\n'.join([s for s in status.splitlines() if s[0] != '?'])
288 if status.strip():
289 return False
290 return True
291
292 def dirty(self):
293 if self._wcrev() == self._state[1] and self._wcclean():
294 return False
295 return True
296
297 def commit(self, text, user, date):
298 # user and date are out of our hands since svn is centralized
299 if self._wcclean():
300 return self._wcrev()
301 commitinfo = self._svncommand(['commit', '-m', text])
302 self._ui.status(commitinfo)
303 newrev = re.search('Committed revision ([\d]+).', commitinfo)
304 if not newrev:
305 raise util.Abort(commitinfo.splitlines()[-1])
306 newrev = newrev.groups()[0]
307 self._ui.status(self._svncommand(['update', '-r', newrev]))
308 return newrev
309
310 def remove(self):
311 if self.dirty():
312 self._repo.ui.warn('Not removing repo %s because'
313 'it has changes.\n' % self._path)
314 return
315 self._repo.ui.note('removing subrepo %s\n' % self._path)
316 shutil.rmtree(self._ctx.repo.join(self._path))
317
318 def get(self, state):
319 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
320 if not re.search('Checked out revision [\d]+.', status):
321 raise util.Abort(status.splitlines()[-1])
322 self._ui.status(status)
323
324 def merge(self, state):
325 old = int(self._state[1])
326 new = int(state[1])
327 if new > old:
328 self.get(state)
329
330 def push(self, force):
331 # nothing for svn
332 pass
333
334 types = {
335 'hg': hgsubrepo,
336 'svn': svnsubrepo,
337 }
@@ -104,3 +104,9 b' cd ../tc'
104 hg pull | sed 's/ .*sub/ ...sub/g'
104 hg pull | sed 's/ .*sub/ ...sub/g'
105 hg up # should pull t
105 hg up # should pull t
106 cat t/t
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
@@ -57,7 +57,7 b' resolving manifests'
57 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
57 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
58 .hgsubstate: versions differ -> m
58 .hgsubstate: versions differ -> m
59 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
59 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
60 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad
60 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
61 getting subrepo t
61 getting subrepo t
62 resolving manifests
62 resolving manifests
63 overwrite True partial False
63 overwrite True partial False
@@ -79,7 +79,7 b' resolving manifests'
79 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
79 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
80 .hgsubstate: versions differ -> m
80 .hgsubstate: versions differ -> m
81 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
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 merging subrepo t
83 merging subrepo t
84 searching for copies back to rev 2
84 searching for copies back to rev 2
85 resolving manifests
85 resolving manifests
@@ -201,3 +201,5 b' adding file changes'
201 added 1 changesets with 1 changes to 1 files
201 added 1 changesets with 1 changes to 1 files
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 blah
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