##// END OF EJS Templates
merge with stable
Benoit Boissinot -
r10670:97022cef merge default
parent child Browse files
Show More
@@ -1,92 +1,92 b''
1 Summary: Mercurial -- a distributed SCM
1 Summary: Mercurial -- a distributed SCM
2 Name: mercurial
2 Name: mercurial
3 Version: snapshot
3 Version: snapshot
4 Release: 0
4 Release: 0
5 License: GPLv2+
5 License: GPLv2+
6 Group: Development/Tools
6 Group: Development/Tools
7 URL: http://mercurial.selenic.com/
7 URL: http://mercurial.selenic.com/
8 Source0: http://mercurial.selenic.com/release/%{name}-%{version}.tar.gz
8 Source0: http://mercurial.selenic.com/release/%{name}-%{version}.tar.gz
9 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
9 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
10
10
11 # From the README:
11 # From the README:
12 #
12 #
13 # Note: some distributions fails to include bits of distutils by
13 # Note: some distributions fails to include bits of distutils by
14 # default, you'll need python-dev to install. You'll also need a C
14 # default, you'll need python-dev to install. You'll also need a C
15 # compiler and a 3-way merge tool like merge, tkdiff, or kdiff3.
15 # compiler and a 3-way merge tool like merge, tkdiff, or kdiff3.
16 #
16 #
17 # python-devel provides an adequate python-dev. The merge tool is a
17 # python-devel provides an adequate python-dev. The merge tool is a
18 # run-time dependency.
18 # run-time dependency.
19 #
19 #
20 BuildRequires: python >= 2.4, python-devel, make, gcc, docutils >= 0.5
20 BuildRequires: python >= 2.4, python-devel, make, gcc, docutils >= 0.5
21 Provides: hg = %{version}-%{release}
21 Provides: hg = %{version}-%{release}
22 Requires: python >= 2.4
22 Requires: python >= 2.4
23 # The hgk extension uses the wish tcl interpreter, but we don't enforce it
23 # The hgk extension uses the wish tcl interpreter, but we don't enforce it
24 #Requires: tk
24 #Requires: tk
25
25
26 %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
26 %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
27 %define emacs_lispdir %{_datadir}/emacs/site-lisp
27 %define emacs_lispdir %{_datadir}/emacs/site-lisp
28
28
29 %description
29 %description
30 Mercurial is a fast, lightweight source control management system designed
30 Mercurial is a fast, lightweight source control management system designed
31 for efficient handling of very large distributed projects.
31 for efficient handling of very large distributed projects.
32
32
33 %prep
33 %prep
34 %setup -q
34 %setup -q
35
35
36 %build
36 %build
37 make all
37 make all
38
38
39 %install
39 %install
40 rm -rf $RPM_BUILD_ROOT
40 rm -rf $RPM_BUILD_ROOT
41 python setup.py install --root $RPM_BUILD_ROOT --prefix %{_prefix}
41 python setup.py install --root $RPM_BUILD_ROOT --prefix %{_prefix}
42 make install-doc DESTDIR=$RPM_BUILD_ROOT MANDIR=%{_mandir}
42 make install-doc DESTDIR=$RPM_BUILD_ROOT MANDIR=%{_mandir}
43
43
44 install contrib/hgk $RPM_BUILD_ROOT%{_bindir}
44 install contrib/hgk $RPM_BUILD_ROOT%{_bindir}
45 install contrib/convert-repo $RPM_BUILD_ROOT%{_bindir}/mercurial-convert-repo
45 install contrib/convert-repo $RPM_BUILD_ROOT%{_bindir}/mercurial-convert-repo
46 install contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}
46 install contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}
47 install contrib/git-viz/hg-viz $RPM_BUILD_ROOT%{_bindir}
47 install contrib/git-viz/hg-viz $RPM_BUILD_ROOT%{_bindir}
48 install contrib/git-viz/git-rev-tree $RPM_BUILD_ROOT%{_bindir}
48 install contrib/git-viz/git-rev-tree $RPM_BUILD_ROOT%{_bindir}
49
49
50 bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
50 bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
51 mkdir -p $bash_completion_dir
51 mkdir -p $bash_completion_dir
52 install -m 644 contrib/bash_completion $bash_completion_dir/mercurial.sh
52 install -m 644 contrib/bash_completion $bash_completion_dir/mercurial.sh
53
53
54 zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions
54 zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions
55 mkdir -p $zsh_completion_dir
55 mkdir -p $zsh_completion_dir
56 install -m 644 contrib/zsh_completion $zsh_completion_dir/_mercurial
56 install -m 644 contrib/zsh_completion $zsh_completion_dir/_mercurial
57
57
58 mkdir -p $RPM_BUILD_ROOT%{emacs_lispdir}
58 mkdir -p $RPM_BUILD_ROOT%{emacs_lispdir}
59 install contrib/mercurial.el $RPM_BUILD_ROOT%{emacs_lispdir}
59 install contrib/mercurial.el $RPM_BUILD_ROOT%{emacs_lispdir}
60 install contrib/mq.el $RPM_BUILD_ROOT%{emacs_lispdir}
60 install contrib/mq.el $RPM_BUILD_ROOT%{emacs_lispdir}
61
61
62 mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/mercurial/hgrc.d
62 mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/mercurial/hgrc.d
63 install contrib/mergetools.hgrc $RPM_BUILD_ROOT%{_sysconfdir}/mercurial/hgrc.d/mergetools.rc
63 install contrib/mergetools.hgrc $RPM_BUILD_ROOT%{_sysconfdir}/mercurial/hgrc.d/mergetools.rc
64
64
65 %clean
65 %clean
66 rm -rf $RPM_BUILD_ROOT
66 rm -rf $RPM_BUILD_ROOT
67
67
68 %files
68 %files
69 %defattr(-,root,root,-)
69 %defattr(-,root,root,-)
70 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html doc/ja *.cgi contrib/*.fcgi
70 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html *.cgi contrib/*.fcgi
71 %doc %attr(644,root,root) %{_mandir}/man?/hg*
71 %doc %attr(644,root,root) %{_mandir}/man?/hg*
72 %doc %attr(644,root,root) contrib/*.svg contrib/sample.hgrc
72 %doc %attr(644,root,root) contrib/*.svg contrib/sample.hgrc
73 %{_sysconfdir}/bash_completion.d/mercurial.sh
73 %{_sysconfdir}/bash_completion.d/mercurial.sh
74 %{_datadir}/zsh/site-functions/_mercurial
74 %{_datadir}/zsh/site-functions/_mercurial
75 %{_datadir}/emacs/site-lisp/mercurial.el
75 %{_datadir}/emacs/site-lisp/mercurial.el
76 %{_datadir}/emacs/site-lisp/mq.el
76 %{_datadir}/emacs/site-lisp/mq.el
77 %{_bindir}/hg
77 %{_bindir}/hg
78 %{_bindir}/hgk
78 %{_bindir}/hgk
79 %{_bindir}/hg-ssh
79 %{_bindir}/hg-ssh
80 %{_bindir}/hg-viz
80 %{_bindir}/hg-viz
81 %{_bindir}/git-rev-tree
81 %{_bindir}/git-rev-tree
82 %{_bindir}/mercurial-convert-repo
82 %{_bindir}/mercurial-convert-repo
83 %dir %{_sysconfdir}/bash_completion.d/
83 %dir %{_sysconfdir}/bash_completion.d/
84 %dir %{_datadir}/zsh/site-functions/
84 %dir %{_datadir}/zsh/site-functions/
85 %dir %{_sysconfdir}/mercurial
85 %dir %{_sysconfdir}/mercurial
86 %dir %{_sysconfdir}/mercurial/hgrc.d
86 %dir %{_sysconfdir}/mercurial/hgrc.d
87 %config(noreplace) %{_sysconfdir}/mercurial/hgrc.d/mergetools.rc
87 %config(noreplace) %{_sysconfdir}/mercurial/hgrc.d/mergetools.rc
88 %if "%{?pythonver}" != "2.4"
88 %if "%{?pythonver}" != "2.4"
89 %{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info
89 %{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info
90 %endif
90 %endif
91 %{_libdir}/python%{pythonver}/site-packages/%{name}
91 %{_libdir}/python%{pythonver}/site-packages/%{name}
92 %{_libdir}/python%{pythonver}/site-packages/hgext
92 %{_libdir}/python%{pythonver}/site-packages/hgext
@@ -1,361 +1,369 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
8 import errno, os, re, xml.dom.minidom, shutil
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 = ('', '', 'empty')
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 p.parse(f, ctx[f].data(), sections, remap, read)
19 p.parse(f, ctx[f].data(), sections, remap, read)
20 else:
20 else:
21 raise util.Abort(_("subrepo spec file %s not found") % f)
21 raise util.Abort(_("subrepo spec file %s not found") % f)
22
22
23 if '.hgsub' in ctx:
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:
28 try:
28 try:
29 for l in ctx['.hgsubstate'].data().splitlines():
29 for l in ctx['.hgsubstate'].data().splitlines():
30 revision, path = l.split(" ", 1)
30 revision, path = l.split(" ", 1)
31 rev[path] = revision
31 rev[path] = revision
32 except IOError, err:
32 except IOError, err:
33 if err.errno != errno.ENOENT:
33 if err.errno != errno.ENOENT:
34 raise
34 raise
35
35
36 state = {}
36 state = {}
37 for path, src in p[''].items():
37 for path, src in p[''].items():
38 kind = 'hg'
38 kind = 'hg'
39 if src.startswith('['):
39 if src.startswith('['):
40 if ']' not in src:
40 if ']' not in src:
41 raise util.Abort(_('missing ] in subrepo source'))
41 raise util.Abort(_('missing ] in subrepo source'))
42 kind, src = src.split(']', 1)
42 kind, src = src.split(']', 1)
43 kind = kind[1:]
43 kind = kind[1:]
44 state[path] = (src.strip(), rev.get(path, ''), kind)
44 state[path] = (src.strip(), rev.get(path, ''), kind)
45
45
46 return state
46 return state
47
47
48 def writestate(repo, state):
48 def writestate(repo, state):
49 repo.wwrite('.hgsubstate',
49 repo.wwrite('.hgsubstate',
50 ''.join(['%s %s\n' % (state[s][1], s)
50 ''.join(['%s %s\n' % (state[s][1], s)
51 for s in sorted(state)]), '')
51 for s in sorted(state)]), '')
52
52
53 def submerge(repo, wctx, mctx, actx):
53 def submerge(repo, wctx, mctx, actx):
54 # working context, merging context, ancestor context
54 # working context, merging context, ancestor context
55 if mctx == actx: # backwards?
55 if mctx == actx: # backwards?
56 actx = wctx.p1()
56 actx = wctx.p1()
57 s1 = wctx.substate
57 s1 = wctx.substate
58 s2 = mctx.substate
58 s2 = mctx.substate
59 sa = actx.substate
59 sa = actx.substate
60 sm = {}
60 sm = {}
61
61
62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
63
63
64 def debug(s, msg, r=""):
64 def debug(s, msg, r=""):
65 if r:
65 if r:
66 r = "%s:%s:%s" % r
66 r = "%s:%s:%s" % r
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
68
68
69 for s, l in s1.items():
69 for s, l in s1.items():
70 if wctx != actx and wctx.sub(s).dirty():
70 if wctx != actx and wctx.sub(s).dirty():
71 l = (l[0], l[1] + "+")
71 l = (l[0], l[1] + "+")
72 a = sa.get(s, nullstate)
72 a = sa.get(s, nullstate)
73 if s in s2:
73 if s in s2:
74 r = s2[s]
74 r = s2[s]
75 if l == r or r == a: # no change or local is newer
75 if l == r or r == a: # no change or local is newer
76 sm[s] = l
76 sm[s] = l
77 continue
77 continue
78 elif l == a: # other side changed
78 elif l == a: # other side changed
79 debug(s, "other changed, get", r)
79 debug(s, "other changed, get", r)
80 wctx.sub(s).get(r)
80 wctx.sub(s).get(r)
81 sm[s] = r
81 sm[s] = r
82 elif l[0] != r[0]: # sources differ
82 elif l[0] != r[0]: # sources differ
83 if repo.ui.promptchoice(
83 if repo.ui.promptchoice(
84 _(' subrepository sources for %s differ\n'
84 _(' subrepository sources for %s differ\n'
85 'use (l)ocal source (%s) or (r)emote source (%s)?')
85 'use (l)ocal source (%s) or (r)emote source (%s)?')
86 % (s, l[0], r[0]),
86 % (s, l[0], r[0]),
87 (_('&Local'), _('&Remote')), 0):
87 (_('&Local'), _('&Remote')), 0):
88 debug(s, "prompt changed, get", r)
88 debug(s, "prompt changed, get", r)
89 wctx.sub(s).get(r)
89 wctx.sub(s).get(r)
90 sm[s] = r
90 sm[s] = r
91 elif l[1] == a[1]: # local side is unchanged
91 elif l[1] == a[1]: # local side is unchanged
92 debug(s, "other side changed, get", r)
92 debug(s, "other side changed, get", r)
93 wctx.sub(s).get(r)
93 wctx.sub(s).get(r)
94 sm[s] = r
94 sm[s] = r
95 else:
95 else:
96 debug(s, "both sides changed, merge with", r)
96 debug(s, "both sides changed, merge with", r)
97 wctx.sub(s).merge(r)
97 wctx.sub(s).merge(r)
98 sm[s] = l
98 sm[s] = l
99 elif l == a: # remote removed, local unchanged
99 elif l == a: # remote removed, local unchanged
100 debug(s, "remote removed, remove")
100 debug(s, "remote removed, remove")
101 wctx.sub(s).remove()
101 wctx.sub(s).remove()
102 else:
102 else:
103 if repo.ui.promptchoice(
103 if repo.ui.promptchoice(
104 _(' local changed subrepository %s which remote removed\n'
104 _(' local changed subrepository %s which remote removed\n'
105 'use (c)hanged version or (d)elete?') % s,
105 'use (c)hanged version or (d)elete?') % s,
106 (_('&Changed'), _('&Delete')), 0):
106 (_('&Changed'), _('&Delete')), 0):
107 debug(s, "prompt remove")
107 debug(s, "prompt remove")
108 wctx.sub(s).remove()
108 wctx.sub(s).remove()
109
109
110 for s, r in s2.items():
110 for s, r in s2.items():
111 if s in s1:
111 if s in s1:
112 continue
112 continue
113 elif s not in sa:
113 elif s not in sa:
114 debug(s, "remote added, get", r)
114 debug(s, "remote added, get", r)
115 mctx.sub(s).get(r)
115 mctx.sub(s).get(r)
116 sm[s] = r
116 sm[s] = r
117 elif r != sa[s]:
117 elif r != sa[s]:
118 if repo.ui.promptchoice(
118 if repo.ui.promptchoice(
119 _(' remote changed subrepository %s which local removed\n'
119 _(' remote changed subrepository %s which local removed\n'
120 'use (c)hanged version or (d)elete?') % s,
120 'use (c)hanged version or (d)elete?') % s,
121 (_('&Changed'), _('&Delete')), 0) == 0:
121 (_('&Changed'), _('&Delete')), 0) == 0:
122 debug(s, "prompt recreate", r)
122 debug(s, "prompt recreate", r)
123 wctx.sub(s).get(r)
123 wctx.sub(s).get(r)
124 sm[s] = r
124 sm[s] = r
125
125
126 # record merged .hgsubstate
126 # record merged .hgsubstate
127 writestate(repo, sm)
127 writestate(repo, sm)
128
128
129 def _abssource(repo, push=False):
129 def _abssource(repo, push=False):
130 if hasattr(repo, '_subparent'):
130 if hasattr(repo, '_subparent'):
131 source = repo._subsource
131 source = repo._subsource
132 if source.startswith('/') or '://' in source:
132 if source.startswith('/') or '://' in source:
133 return source
133 return source
134 parent = _abssource(repo._subparent)
134 parent = _abssource(repo._subparent, push)
135 if '://' in parent:
135 if '://' in parent:
136 if parent[-1] == '/':
136 if parent[-1] == '/':
137 parent = parent[:-1]
137 parent = parent[:-1]
138 return parent + '/' + source
138 return parent + '/' + source
139 return os.path.join(parent, repo._subsource)
139 return os.path.join(parent, repo._subsource)
140 if push and repo.ui.config('paths', 'default-push'):
140 if push and repo.ui.config('paths', 'default-push'):
141 return repo.ui.config('paths', 'default-push', repo.root)
141 return repo.ui.config('paths', 'default-push', repo.root)
142 return repo.ui.config('paths', 'default', repo.root)
142 return repo.ui.config('paths', 'default', repo.root)
143
143
144 def subrepo(ctx, path):
144 def subrepo(ctx, path):
145 # subrepo inherently violates our import layering rules
145 # subrepo inherently violates our import layering rules
146 # because it wants to make repo objects from deep inside the stack
146 # because it wants to make repo objects from deep inside the stack
147 # so we manually delay the circular imports to not break
147 # so we manually delay the circular imports to not break
148 # scripts that don't use our demand-loading
148 # scripts that don't use our demand-loading
149 global hg
149 global hg
150 import hg as h
150 import hg as h
151 hg = h
151 hg = h
152
152
153 util.path_auditor(ctx._repo.root)(path)
153 util.path_auditor(ctx._repo.root)(path)
154 state = ctx.substate.get(path, nullstate)
154 state = ctx.substate.get(path, nullstate)
155 if state[2] not in types:
155 if state[2] not in types:
156 raise util.Abort(_('unknown subrepo type %s') % state[2])
156 raise util.Abort(_('unknown subrepo type %s') % state[2])
157 return types[state[2]](ctx, path, state[:2])
157 return types[state[2]](ctx, path, state[:2])
158
158
159 # subrepo classes need to implement the following methods:
159 # subrepo classes need to implement the following methods:
160 # __init__(self, ctx, path, state)
160 # __init__(self, ctx, path, state)
161 # dirty(self): returns true if the dirstate of the subrepo
161 # dirty(self): returns true if the dirstate of the subrepo
162 # does not match current stored state
162 # does not match current stored state
163 # commit(self, text, user, date): commit the current changes
163 # commit(self, text, user, date): commit the current changes
164 # to the subrepo with the given log message. Use given
164 # to the subrepo with the given log message. Use given
165 # user and date if possible. Return the new state of the subrepo.
165 # user and date if possible. Return the new state of the subrepo.
166 # remove(self): remove the subrepo (should verify the dirstate
166 # remove(self): remove the subrepo (should verify the dirstate
167 # is not dirty first)
167 # is not dirty first)
168 # get(self, state): run whatever commands are needed to put the
168 # get(self, state): run whatever commands are needed to put the
169 # subrepo into this state
169 # subrepo into this state
170 # merge(self, state): merge currently-saved state with the new state.
170 # merge(self, state): merge currently-saved state with the new state.
171 # push(self, force): perform whatever action is analagous to 'hg push'
171 # push(self, force): perform whatever action is analagous to 'hg push'
172 # This may be a no-op on some systems.
172 # This may be a no-op on some systems.
173
173
174 class hgsubrepo(object):
174 class hgsubrepo(object):
175 def __init__(self, ctx, path, state):
175 def __init__(self, ctx, path, state):
176 self._path = path
176 self._path = path
177 self._state = state
177 self._state = state
178 r = ctx._repo
178 r = ctx._repo
179 root = r.wjoin(path)
179 root = r.wjoin(path)
180 if os.path.exists(os.path.join(root, '.hg')):
180 create = False
181 self._repo = hg.repository(r.ui, root)
181 if not os.path.exists(os.path.join(root, '.hg')):
182 else:
182 create = True
183 util.makedirs(root)
183 util.makedirs(root)
184 self._repo = hg.repository(r.ui, root, create=True)
184 self._repo = hg.repository(r.ui, root, create=create)
185 f = file(os.path.join(root, '.hg', 'hgrc'), 'w')
186 f.write('[paths]\ndefault = %s\n' % os.path.join(
187 _abssource(ctx._repo), path))
188 f.close()
189 self._repo._subparent = r
185 self._repo._subparent = r
190 self._repo._subsource = state[0]
186 self._repo._subsource = state[0]
191
187
188 if create:
189 fp = self._repo.opener("hgrc", "w", text=True)
190 fp.write('[paths]\n')
191
192 def addpathconfig(key, value):
193 fp.write('%s = %s\n' % (key, value))
194 self._repo.ui.setconfig('paths', key, value)
195
196 defpath = os.path.join(_abssource(ctx._repo), path)
197 addpathconfig('default', defpath)
198 fp.close()
199
192 def dirty(self):
200 def dirty(self):
193 r = self._state[1]
201 r = self._state[1]
194 if r == '':
202 if r == '':
195 return True
203 return True
196 w = self._repo[None]
204 w = self._repo[None]
197 if w.p1() != self._repo[r]: # version checked out changed
205 if w.p1() != self._repo[r]: # version checked out change
198 return True
206 return True
199 return w.dirty() # working directory changed
207 return w.dirty() # working directory changed
200
208
201 def commit(self, text, user, date):
209 def commit(self, text, user, date):
202 self._repo.ui.debug("committing subrepo %s\n" % self._path)
210 self._repo.ui.debug("committing subrepo %s\n" % self._path)
203 n = self._repo.commit(text, user, date)
211 n = self._repo.commit(text, user, date)
204 if not n:
212 if not n:
205 return self._repo['.'].hex() # different version checked out
213 return self._repo['.'].hex() # different version checked out
206 return node.hex(n)
214 return node.hex(n)
207
215
208 def remove(self):
216 def remove(self):
209 # we can't fully delete the repository as it may contain
217 # we can't fully delete the repository as it may contain
210 # local-only history
218 # local-only history
211 self._repo.ui.note(_('removing subrepo %s\n') % self._path)
219 self._repo.ui.note(_('removing subrepo %s\n') % self._path)
212 hg.clean(self._repo, node.nullid, False)
220 hg.clean(self._repo, node.nullid, False)
213
221
214 def _get(self, state):
222 def _get(self, state):
215 source, revision, kind = state
223 source, revision, kind = state
216 try:
224 try:
217 self._repo.lookup(revision)
225 self._repo.lookup(revision)
218 except error.RepoError:
226 except error.RepoError:
219 self._repo._subsource = source
227 self._repo._subsource = source
220 srcurl = _abssource(self._repo)
228 srcurl = _abssource(self._repo)
221 self._repo.ui.status(_('pulling subrepo %s from %s\n') % (self._path, srcurl))
229 self._repo.ui.status(_('pulling subrepo %s from %s\n') % (self._path, srcurl))
222 other = hg.repository(self._repo.ui, srcurl)
230 other = hg.repository(self._repo.ui, srcurl)
223 self._repo.pull(other)
231 self._repo.pull(other)
224
232
225 def get(self, state):
233 def get(self, state):
226 self._get(state)
234 self._get(state)
227 source, revision, kind = state
235 source, revision, kind = state
228 self._repo.ui.debug("getting subrepo %s\n" % self._path)
236 self._repo.ui.debug("getting subrepo %s\n" % self._path)
229 hg.clean(self._repo, revision, False)
237 hg.clean(self._repo, revision, False)
230
238
231 def merge(self, state):
239 def merge(self, state):
232 self._get(state)
240 self._get(state)
233 cur = self._repo['.']
241 cur = self._repo['.']
234 dst = self._repo[state[1]]
242 dst = self._repo[state[1]]
235 anc = dst.ancestor(cur)
243 anc = dst.ancestor(cur)
236 if anc == cur:
244 if anc == cur:
237 self._repo.ui.debug("updating subrepo %s\n" % self._path)
245 self._repo.ui.debug("updating subrepo %s\n" % self._path)
238 hg.update(self._repo, state[1])
246 hg.update(self._repo, state[1])
239 elif anc == dst:
247 elif anc == dst:
240 self._repo.ui.debug("skipping subrepo %s\n" % self._path)
248 self._repo.ui.debug("skipping subrepo %s\n" % self._path)
241 else:
249 else:
242 self._repo.ui.debug("merging subrepo %s\n" % self._path)
250 self._repo.ui.debug("merging subrepo %s\n" % self._path)
243 hg.merge(self._repo, state[1], remind=False)
251 hg.merge(self._repo, state[1], remind=False)
244
252
245 def push(self, force):
253 def push(self, force):
246 # push subrepos depth-first for coherent ordering
254 # push subrepos depth-first for coherent ordering
247 c = self._repo['']
255 c = self._repo['']
248 subs = c.substate # only repos that are committed
256 subs = c.substate # only repos that are committed
249 for s in sorted(subs):
257 for s in sorted(subs):
250 c.sub(s).push(force)
258 c.sub(s).push(force)
251
259
252 self._repo.ui.status(_('pushing subrepo %s\n') % self._path)
260 self._repo.ui.status(_('pushing subrepo %s\n') % self._path)
253 dsturl = _abssource(self._repo, True)
261 dsturl = _abssource(self._repo, True)
254 other = hg.repository(self._repo.ui, dsturl)
262 other = hg.repository(self._repo.ui, dsturl)
255 self._repo.push(other, force)
263 self._repo.push(other, force)
256
264
257 class svnsubrepo(object):
265 class svnsubrepo(object):
258 def __init__(self, ctx, path, state):
266 def __init__(self, ctx, path, state):
259 self._path = path
267 self._path = path
260 self._state = state
268 self._state = state
261 self._ctx = ctx
269 self._ctx = ctx
262 self._ui = ctx._repo.ui
270 self._ui = ctx._repo.ui
263
271
264 def _svncommand(self, commands):
272 def _svncommand(self, commands):
265 cmd = ['svn'] + commands + [self._path]
273 cmd = ['svn'] + commands + [self._path]
266 cmd = [util.shellquote(arg) for arg in cmd]
274 cmd = [util.shellquote(arg) for arg in cmd]
267 cmd = util.quotecommand(' '.join(cmd))
275 cmd = util.quotecommand(' '.join(cmd))
268 env = dict(os.environ)
276 env = dict(os.environ)
269 # Avoid localized output, preserve current locale for everything else.
277 # Avoid localized output, preserve current locale for everything else.
270 env['LC_MESSAGES'] = 'C'
278 env['LC_MESSAGES'] = 'C'
271 write, read, err = util.popen3(cmd, env=env, newlines=True)
279 write, read, err = util.popen3(cmd, env=env, newlines=True)
272 retdata = read.read()
280 retdata = read.read()
273 err = err.read().strip()
281 err = err.read().strip()
274 if err:
282 if err:
275 raise util.Abort(err)
283 raise util.Abort(err)
276 return retdata
284 return retdata
277
285
278 def _wcrev(self):
286 def _wcrev(self):
279 output = self._svncommand(['info', '--xml'])
287 output = self._svncommand(['info', '--xml'])
280 doc = xml.dom.minidom.parseString(output)
288 doc = xml.dom.minidom.parseString(output)
281 entries = doc.getElementsByTagName('entry')
289 entries = doc.getElementsByTagName('entry')
282 if not entries:
290 if not entries:
283 return 0
291 return 0
284 return int(entries[0].getAttribute('revision') or 0)
292 return int(entries[0].getAttribute('revision') or 0)
285
293
286 def _wcchanged(self):
294 def _wcchanged(self):
287 """Return (changes, extchanges) where changes is True
295 """Return (changes, extchanges) where changes is True
288 if the working directory was changed, and extchanges is
296 if the working directory was changed, and extchanges is
289 True if any of these changes concern an external entry.
297 True if any of these changes concern an external entry.
290 """
298 """
291 output = self._svncommand(['status', '--xml'])
299 output = self._svncommand(['status', '--xml'])
292 externals, changes = [], []
300 externals, changes = [], []
293 doc = xml.dom.minidom.parseString(output)
301 doc = xml.dom.minidom.parseString(output)
294 for e in doc.getElementsByTagName('entry'):
302 for e in doc.getElementsByTagName('entry'):
295 s = e.getElementsByTagName('wc-status')
303 s = e.getElementsByTagName('wc-status')
296 if not s:
304 if not s:
297 continue
305 continue
298 item = s[0].getAttribute('item')
306 item = s[0].getAttribute('item')
299 props = s[0].getAttribute('props')
307 props = s[0].getAttribute('props')
300 path = e.getAttribute('path')
308 path = e.getAttribute('path')
301 if item == 'external':
309 if item == 'external':
302 externals.append(path)
310 externals.append(path)
303 if (item not in ('', 'normal', 'unversioned', 'external')
311 if (item not in ('', 'normal', 'unversioned', 'external')
304 or props not in ('', 'none')):
312 or props not in ('', 'none')):
305 changes.append(path)
313 changes.append(path)
306 for path in changes:
314 for path in changes:
307 for ext in externals:
315 for ext in externals:
308 if path == ext or path.startswith(ext + os.sep):
316 if path == ext or path.startswith(ext + os.sep):
309 return True, True
317 return True, True
310 return bool(changes), False
318 return bool(changes), False
311
319
312 def dirty(self):
320 def dirty(self):
313 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
321 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
314 return False
322 return False
315 return True
323 return True
316
324
317 def commit(self, text, user, date):
325 def commit(self, text, user, date):
318 # user and date are out of our hands since svn is centralized
326 # user and date are out of our hands since svn is centralized
319 changed, extchanged = self._wcchanged()
327 changed, extchanged = self._wcchanged()
320 if not changed:
328 if not changed:
321 return self._wcrev()
329 return self._wcrev()
322 if extchanged:
330 if extchanged:
323 # Do not try to commit externals
331 # Do not try to commit externals
324 raise util.Abort(_('cannot commit svn externals'))
332 raise util.Abort(_('cannot commit svn externals'))
325 commitinfo = self._svncommand(['commit', '-m', text])
333 commitinfo = self._svncommand(['commit', '-m', text])
326 self._ui.status(commitinfo)
334 self._ui.status(commitinfo)
327 newrev = re.search('Committed revision ([\d]+).', commitinfo)
335 newrev = re.search('Committed revision ([\d]+).', commitinfo)
328 if not newrev:
336 if not newrev:
329 raise util.Abort(commitinfo.splitlines()[-1])
337 raise util.Abort(commitinfo.splitlines()[-1])
330 newrev = newrev.groups()[0]
338 newrev = newrev.groups()[0]
331 self._ui.status(self._svncommand(['update', '-r', newrev]))
339 self._ui.status(self._svncommand(['update', '-r', newrev]))
332 return newrev
340 return newrev
333
341
334 def remove(self):
342 def remove(self):
335 if self.dirty():
343 if self.dirty():
336 self._ui.warn(_('not removing repo %s because '
344 self._ui.warn(_('not removing repo %s because '
337 'it has changes.\n' % self._path))
345 'it has changes.\n' % self._path))
338 return
346 return
339 self._ui.note(_('removing subrepo %s\n') % self._path)
347 self._ui.note(_('removing subrepo %s\n') % self._path)
340 shutil.rmtree(self._ctx.repo.join(self._path))
348 shutil.rmtree(self._ctx.repo.join(self._path))
341
349
342 def get(self, state):
350 def get(self, state):
343 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
351 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
344 if not re.search('Checked out revision [\d]+.', status):
352 if not re.search('Checked out revision [\d]+.', status):
345 raise util.Abort(status.splitlines()[-1])
353 raise util.Abort(status.splitlines()[-1])
346 self._ui.status(status)
354 self._ui.status(status)
347
355
348 def merge(self, state):
356 def merge(self, state):
349 old = int(self._state[1])
357 old = int(self._state[1])
350 new = int(state[1])
358 new = int(state[1])
351 if new > old:
359 if new > old:
352 self.get(state)
360 self.get(state)
353
361
354 def push(self, force):
362 def push(self, force):
355 # nothing for svn
363 # nothing for svn
356 pass
364 pass
357
365
358 types = {
366 types = {
359 'hg': hgsubrepo,
367 'hg': hgsubrepo,
360 'svn': svnsubrepo,
368 'svn': svnsubrepo,
361 }
369 }
@@ -1,179 +1,180 b''
1 #!/bin/sh
1 #!/bin/sh
2 # Test basic extension support
2 # Test basic extension support
3
3
4 "$TESTDIR/hghave" no-outer-repo || exit 80
4 "$TESTDIR/hghave" no-outer-repo || exit 80
5
5
6 cat > foobar.py <<EOF
6 cat > foobar.py <<EOF
7 import os
7 import os
8 from mercurial import commands
8 from mercurial import commands
9
9
10 def uisetup(ui):
10 def uisetup(ui):
11 ui.write("uisetup called\\n")
11 ui.write("uisetup called\\n")
12
12
13 def reposetup(ui, repo):
13 def reposetup(ui, repo):
14 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
14 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
15 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
15 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
16
16
17 def foo(ui, *args, **kwargs):
17 def foo(ui, *args, **kwargs):
18 ui.write("Foo\\n")
18 ui.write("Foo\\n")
19
19
20 def bar(ui, *args, **kwargs):
20 def bar(ui, *args, **kwargs):
21 ui.write("Bar\\n")
21 ui.write("Bar\\n")
22
22
23 cmdtable = {
23 cmdtable = {
24 "foo": (foo, [], "hg foo"),
24 "foo": (foo, [], "hg foo"),
25 "bar": (bar, [], "hg bar"),
25 "bar": (bar, [], "hg bar"),
26 }
26 }
27
27
28 commands.norepo += ' bar'
28 commands.norepo += ' bar'
29 EOF
29 EOF
30 abspath=`pwd`/foobar.py
30 abspath=`pwd`/foobar.py
31
31
32 mkdir barfoo
32 mkdir barfoo
33 cp foobar.py barfoo/__init__.py
33 cp foobar.py barfoo/__init__.py
34 barfoopath=`pwd`/barfoo
34 barfoopath=`pwd`/barfoo
35
35
36 hg init a
36 hg init a
37 cd a
37 cd a
38 echo foo > file
38 echo foo > file
39 hg add file
39 hg add file
40 hg commit -m 'add file'
40 hg commit -m 'add file'
41
41
42 echo '[extensions]' >> $HGRCPATH
42 echo '[extensions]' >> $HGRCPATH
43 echo "foobar = $abspath" >> $HGRCPATH
43 echo "foobar = $abspath" >> $HGRCPATH
44 hg foo
44 hg foo
45
45
46 cd ..
46 cd ..
47 hg clone a b
47 hg clone a b
48
48
49 hg bar
49 hg bar
50 echo 'foobar = !' >> $HGRCPATH
50 echo 'foobar = !' >> $HGRCPATH
51
51
52 echo '% module/__init__.py-style'
52 echo '% module/__init__.py-style'
53 echo "barfoo = $barfoopath" >> $HGRCPATH
53 echo "barfoo = $barfoopath" >> $HGRCPATH
54 cd a
54 cd a
55 hg foo
55 hg foo
56 echo 'barfoo = !' >> $HGRCPATH
56 echo 'barfoo = !' >> $HGRCPATH
57
57
58 # check that extensions are loaded in phases
58 # check that extensions are loaded in phases
59 cat > foo.py <<EOF
59 cat > foo.py <<EOF
60 import os
60 import os
61 name = os.path.basename(__file__).rsplit('.', 1)[0]
61 name = os.path.basename(__file__).rsplit('.', 1)[0]
62 print "1) %s imported" % name
62 print "1) %s imported" % name
63 def uisetup(ui):
63 def uisetup(ui):
64 print "2) %s uisetup" % name
64 print "2) %s uisetup" % name
65 def extsetup():
65 def extsetup():
66 print "3) %s extsetup" % name
66 print "3) %s extsetup" % name
67 def reposetup(ui, repo):
67 def reposetup(ui, repo):
68 print "4) %s reposetup" % name
68 print "4) %s reposetup" % name
69 EOF
69 EOF
70
70
71 cp foo.py bar.py
71 cp foo.py bar.py
72 echo 'foo = foo.py' >> $HGRCPATH
72 echo 'foo = foo.py' >> $HGRCPATH
73 echo 'bar = bar.py' >> $HGRCPATH
73 echo 'bar = bar.py' >> $HGRCPATH
74
74
75 # command with no output, we just want to see the extensions loaded
75 # command with no output, we just want to see the extensions loaded
76 hg paths
76 hg paths
77
77
78 # check hgweb's load order
78 # check hgweb's load order
79 echo '% hgweb.cgi'
79 echo '% hgweb.cgi'
80 cat > hgweb.cgi <<EOF
80 cat > hgweb.cgi <<EOF
81 #!/usr/bin/env python
81 #!/usr/bin/env python
82 from mercurial import demandimport; demandimport.enable()
82 from mercurial import demandimport; demandimport.enable()
83 from mercurial.hgweb import hgweb
83 from mercurial.hgweb import hgweb
84 from mercurial.hgweb import wsgicgi
84 from mercurial.hgweb import wsgicgi
85
85
86 application = hgweb('.', 'test repo')
86 application = hgweb('.', 'test repo')
87 wsgicgi.launch(application)
87 wsgicgi.launch(application)
88 EOF
88 EOF
89 SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
89 SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
90 | grep '^[0-9]) ' # ignores HTML output
90 | grep '^[0-9]) ' # ignores HTML output
91
91
92 echo 'foo = !' >> $HGRCPATH
92 echo 'foo = !' >> $HGRCPATH
93 echo 'bar = !' >> $HGRCPATH
93 echo 'bar = !' >> $HGRCPATH
94
94
95 cd ..
95 cd ..
96 cat > empty.py <<EOF
96 cat > empty.py <<EOF
97 '''empty cmdtable
97 '''empty cmdtable
98 '''
98 '''
99 cmdtable = {}
99 cmdtable = {}
100 EOF
100 EOF
101 emptypath=`pwd`/empty.py
101 emptypath=`pwd`/empty.py
102 echo "empty = $emptypath" >> $HGRCPATH
102 echo "empty = $emptypath" >> $HGRCPATH
103 hg help empty
103 hg help empty
104 echo 'empty = !' >> $HGRCPATH
104 echo 'empty = !' >> $HGRCPATH
105
105
106 cat > debugextension.py <<EOF
106 cat > debugextension.py <<EOF
107 '''only debugcommands
107 '''only debugcommands
108 '''
108 '''
109 def debugfoobar(ui, repo, *args, **opts):
109 def debugfoobar(ui, repo, *args, **opts):
110 "yet another debug command"
110 "yet another debug command"
111 pass
111 pass
112
112
113 def foo(ui, repo, *args, **opts):
113 def foo(ui, repo, *args, **opts):
114 """yet another foo command
114 """yet another foo command
115
115
116 This command has been DEPRECATED since forever.
116 This command has been DEPRECATED since forever.
117 """
117 """
118 pass
118 pass
119
119
120 cmdtable = {
120 cmdtable = {
121 "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
121 "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
122 "foo": (foo, (), "hg foo")
122 "foo": (foo, (), "hg foo")
123 }
123 }
124 EOF
124 EOF
125 debugpath=`pwd`/debugextension.py
125 debugpath=`pwd`/debugextension.py
126 echo "debugextension = $debugpath" >> $HGRCPATH
126 echo "debugextension = $debugpath" >> $HGRCPATH
127 echo "% hg help"
127 echo "% hg help"
128 hg help debugextension
128 hg help debugextension
129 echo "% hg help --verbose"
129 echo "% hg help --verbose"
130 hg --verbose help debugextension
130 hg --verbose help debugextension
131 echo "% hg help --debug"
131 echo "% hg help --debug"
132 hg --debug help debugextension
132 hg --debug help debugextension
133 echo 'debugextension = !' >> $HGRCPATH
133 echo 'debugextension = !' >> $HGRCPATH
134
134
135 echo % issue811
135 echo % issue811
136 debugpath=`pwd`/debugissue811.py
136 debugpath=`pwd`/debugissue811.py
137 cat > debugissue811.py <<EOF
137 cat > debugissue811.py <<EOF
138 '''show all loaded extensions
138 '''show all loaded extensions
139 '''
139 '''
140 from mercurial import extensions, commands
140 from mercurial import extensions, commands
141
141
142 def debugextensions(ui):
142 def debugextensions(ui):
143 "yet another debug command"
143 "yet another debug command"
144 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
144 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
145
145
146 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
146 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
147 commands.norepo += " debugextensions"
147 commands.norepo += " debugextensions"
148 EOF
148 EOF
149 echo "debugissue811 = $debugpath" >> $HGRCPATH
149 echo "debugissue811 = $debugpath" >> $HGRCPATH
150 echo "mq=" >> $HGRCPATH
150 echo "mq=" >> $HGRCPATH
151 echo "hgext.mq=" >> $HGRCPATH
151 echo "hgext.mq=" >> $HGRCPATH
152 echo "hgext/mq=" >> $HGRCPATH
152 echo "hgext/mq=" >> $HGRCPATH
153
153
154 echo % show extensions
154 echo % show extensions
155 hg debugextensions
155 hg debugextensions
156
156
157 echo '% disabled extension commands'
157 echo '% disabled extension commands'
158 HGRCPATH=
158 HGRCPATH=
159 hg help email
159 hg help email
160 hg qdel
160 hg qdel
161 hg churn
161 hg churn
162 echo '% disabled extensions'
162 echo '% disabled extensions'
163 hg help churn
163 hg help churn
164 hg help patchbomb
164 hg help patchbomb
165 echo '% broken disabled extension and command'
165 echo '% broken disabled extension and command'
166 mkdir hgext
166 mkdir hgext
167 echo > hgext/__init__.py
167 echo > hgext/__init__.py
168 cat > hgext/broken.py <<EOF
168 cat > hgext/broken.py <<EOF
169 "broken extension'
169 "broken extension'
170 EOF
170 EOF
171 TMPPYTHONPATH="$PYTHONPATH"
171 cat > path.py <<EOF
172 PYTHONPATH="`pwd`:$PYTHONPATH"
172 import os, sys
173 export PYTHONPATH
173 sys.path.insert(0, os.environ['HGEXTPATH'])
174 hg help broken
174 EOF
175 hg help foo > /dev/null
175 HGEXTPATH=`pwd`
176 PYTHONPATH="$TMPPYTHONPATH"
176 export HGEXTPATH
177 export PYTHONPATH
177 hg --config extensions.path=./path.py help broken
178 hg --config extensions.path=./path.py help foo > /dev/null
178
179
179 exit 0
180 exit 0
General Comments 0
You need to be logged in to leave comments. Login now