##// END OF EJS Templates
share: remove unexpected heading from "verbose" container in help test...
Martin von Zweigbergk -
r46698:c80f9e3d default
parent child Browse files
Show More
@@ -1,237 +1,236 b''
1 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
1 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 '''share a common history between several working directories
6 '''share a common history between several working directories
7
7
8 The share extension introduces a new command :hg:`share` to create a new
8 The share extension introduces a new command :hg:`share` to create a new
9 working directory. This is similar to :hg:`clone`, but doesn't involve
9 working directory. This is similar to :hg:`clone`, but doesn't involve
10 copying or linking the storage of the repository. This allows working on
10 copying or linking the storage of the repository. This allows working on
11 different branches or changes in parallel without the associated cost in
11 different branches or changes in parallel without the associated cost in
12 terms of disk space.
12 terms of disk space.
13
13
14 Note: destructive operations or extensions like :hg:`rollback` should be
14 Note: destructive operations or extensions like :hg:`rollback` should be
15 used with care as they can result in confusing problems.
15 used with care as they can result in confusing problems.
16
16
17 Automatic Pooled Storage for Clones
17 Automatic Pooled Storage for Clones
18 -----------------------------------
18 -----------------------------------
19
19
20 When this extension is active, :hg:`clone` can be configured to
20 When this extension is active, :hg:`clone` can be configured to
21 automatically share/pool storage across multiple clones. This
21 automatically share/pool storage across multiple clones. This
22 mode effectively converts :hg:`clone` to :hg:`clone` + :hg:`share`.
22 mode effectively converts :hg:`clone` to :hg:`clone` + :hg:`share`.
23 The benefit of using this mode is the automatic management of
23 The benefit of using this mode is the automatic management of
24 store paths and intelligent pooling of related repositories.
24 store paths and intelligent pooling of related repositories.
25
25
26 The following ``share.`` config options influence this feature:
26 The following ``share.`` config options influence this feature:
27
27
28 ``share.pool``
28 ``share.pool``
29 Filesystem path where shared repository data will be stored. When
29 Filesystem path where shared repository data will be stored. When
30 defined, :hg:`clone` will automatically use shared repository
30 defined, :hg:`clone` will automatically use shared repository
31 storage instead of creating a store inside each clone.
31 storage instead of creating a store inside each clone.
32
32
33 ``share.poolnaming``
33 ``share.poolnaming``
34 How directory names in ``share.pool`` are constructed.
34 How directory names in ``share.pool`` are constructed.
35
35
36 "identity" means the name is derived from the first changeset in the
36 "identity" means the name is derived from the first changeset in the
37 repository. In this mode, different remotes share storage if their
37 repository. In this mode, different remotes share storage if their
38 root/initial changeset is identical. In this mode, the local shared
38 root/initial changeset is identical. In this mode, the local shared
39 repository is an aggregate of all encountered remote repositories.
39 repository is an aggregate of all encountered remote repositories.
40
40
41 "remote" means the name is derived from the source repository's
41 "remote" means the name is derived from the source repository's
42 path or URL. In this mode, storage is only shared if the path or URL
42 path or URL. In this mode, storage is only shared if the path or URL
43 requested in the :hg:`clone` command matches exactly to a repository
43 requested in the :hg:`clone` command matches exactly to a repository
44 that was cloned before.
44 that was cloned before.
45
45
46 The default naming mode is "identity".
46 The default naming mode is "identity".
47
47
48 .. container:: verbose
48 .. container:: verbose
49
49
50 Sharing requirements and configs of source repository with shares
50 Sharing requirements and configs of source repository with shares:
51 -----------------------------------------------------------------
52
51
53 By default creating a shared repository only enables sharing a common
52 By default creating a shared repository only enables sharing a common
54 history and does not share requirements and configs between them. This
53 history and does not share requirements and configs between them. This
55 may lead to problems in some cases, for example when you upgrade the
54 may lead to problems in some cases, for example when you upgrade the
56 storage format from one repository but does not set related configs
55 storage format from one repository but does not set related configs
57 in the shares.
56 in the shares.
58
57
59 Setting `format.exp-share-safe = True` enables sharing configs and
58 Setting `format.exp-share-safe = True` enables sharing configs and
60 requirements. This only applies to shares which are done after enabling
59 requirements. This only applies to shares which are done after enabling
61 the config option.
60 the config option.
62
61
63 For enabling this in existing shares, enable the config option and reshare.
62 For enabling this in existing shares, enable the config option and reshare.
64
63
65 For resharing existing shares, make sure your working directory is clean
64 For resharing existing shares, make sure your working directory is clean
66 and there are no untracked files, delete that share and create a new share.
65 and there are no untracked files, delete that share and create a new share.
67 '''
66 '''
68
67
69 from __future__ import absolute_import
68 from __future__ import absolute_import
70
69
71 import errno
70 import errno
72 from mercurial.i18n import _
71 from mercurial.i18n import _
73 from mercurial import (
72 from mercurial import (
74 bookmarks,
73 bookmarks,
75 commands,
74 commands,
76 error,
75 error,
77 extensions,
76 extensions,
78 hg,
77 hg,
79 registrar,
78 registrar,
80 txnutil,
79 txnutil,
81 util,
80 util,
82 )
81 )
83
82
84 cmdtable = {}
83 cmdtable = {}
85 command = registrar.command(cmdtable)
84 command = registrar.command(cmdtable)
86 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
85 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
87 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
86 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
88 # be specifying the version(s) of Mercurial they are tested with, or
87 # be specifying the version(s) of Mercurial they are tested with, or
89 # leave the attribute unspecified.
88 # leave the attribute unspecified.
90 testedwith = b'ships-with-hg-core'
89 testedwith = b'ships-with-hg-core'
91
90
92
91
93 @command(
92 @command(
94 b'share',
93 b'share',
95 [
94 [
96 (b'U', b'noupdate', None, _(b'do not create a working directory')),
95 (b'U', b'noupdate', None, _(b'do not create a working directory')),
97 (b'B', b'bookmarks', None, _(b'also share bookmarks')),
96 (b'B', b'bookmarks', None, _(b'also share bookmarks')),
98 (
97 (
99 b'',
98 b'',
100 b'relative',
99 b'relative',
101 None,
100 None,
102 _(b'point to source using a relative path'),
101 _(b'point to source using a relative path'),
103 ),
102 ),
104 ],
103 ],
105 _(b'[-U] [-B] SOURCE [DEST]'),
104 _(b'[-U] [-B] SOURCE [DEST]'),
106 helpcategory=command.CATEGORY_REPO_CREATION,
105 helpcategory=command.CATEGORY_REPO_CREATION,
107 norepo=True,
106 norepo=True,
108 )
107 )
109 def share(
108 def share(
110 ui, source, dest=None, noupdate=False, bookmarks=False, relative=False
109 ui, source, dest=None, noupdate=False, bookmarks=False, relative=False
111 ):
110 ):
112 """create a new shared repository
111 """create a new shared repository
113
112
114 Initialize a new repository and working directory that shares its
113 Initialize a new repository and working directory that shares its
115 history (and optionally bookmarks) with another repository.
114 history (and optionally bookmarks) with another repository.
116
115
117 .. note::
116 .. note::
118
117
119 using rollback or extensions that destroy/modify history (mq,
118 using rollback or extensions that destroy/modify history (mq,
120 rebase, etc.) can cause considerable confusion with shared
119 rebase, etc.) can cause considerable confusion with shared
121 clones. In particular, if two shared clones are both updated to
120 clones. In particular, if two shared clones are both updated to
122 the same changeset, and one of them destroys that changeset
121 the same changeset, and one of them destroys that changeset
123 with rollback, the other clone will suddenly stop working: all
122 with rollback, the other clone will suddenly stop working: all
124 operations will fail with "abort: working directory has unknown
123 operations will fail with "abort: working directory has unknown
125 parent". The only known workaround is to use debugsetparents on
124 parent". The only known workaround is to use debugsetparents on
126 the broken clone to reset it to a changeset that still exists.
125 the broken clone to reset it to a changeset that still exists.
127 """
126 """
128
127
129 hg.share(
128 hg.share(
130 ui,
129 ui,
131 source,
130 source,
132 dest=dest,
131 dest=dest,
133 update=not noupdate,
132 update=not noupdate,
134 bookmarks=bookmarks,
133 bookmarks=bookmarks,
135 relative=relative,
134 relative=relative,
136 )
135 )
137 return 0
136 return 0
138
137
139
138
140 @command(b'unshare', [], b'', helpcategory=command.CATEGORY_MAINTENANCE)
139 @command(b'unshare', [], b'', helpcategory=command.CATEGORY_MAINTENANCE)
141 def unshare(ui, repo):
140 def unshare(ui, repo):
142 """convert a shared repository to a normal one
141 """convert a shared repository to a normal one
143
142
144 Copy the store data to the repo and remove the sharedpath data.
143 Copy the store data to the repo and remove the sharedpath data.
145 """
144 """
146
145
147 if not repo.shared():
146 if not repo.shared():
148 raise error.Abort(_(b"this is not a shared repo"))
147 raise error.Abort(_(b"this is not a shared repo"))
149
148
150 hg.unshare(ui, repo)
149 hg.unshare(ui, repo)
151
150
152
151
153 # Wrap clone command to pass auto share options.
152 # Wrap clone command to pass auto share options.
154 def clone(orig, ui, source, *args, **opts):
153 def clone(orig, ui, source, *args, **opts):
155 pool = ui.config(b'share', b'pool')
154 pool = ui.config(b'share', b'pool')
156 if pool:
155 if pool:
157 pool = util.expandpath(pool)
156 pool = util.expandpath(pool)
158
157
159 opts['shareopts'] = {
158 opts['shareopts'] = {
160 b'pool': pool,
159 b'pool': pool,
161 b'mode': ui.config(b'share', b'poolnaming'),
160 b'mode': ui.config(b'share', b'poolnaming'),
162 }
161 }
163
162
164 return orig(ui, source, *args, **opts)
163 return orig(ui, source, *args, **opts)
165
164
166
165
167 def extsetup(ui):
166 def extsetup(ui):
168 extensions.wrapfunction(bookmarks, b'_getbkfile', getbkfile)
167 extensions.wrapfunction(bookmarks, b'_getbkfile', getbkfile)
169 extensions.wrapfunction(bookmarks.bmstore, b'_recordchange', recordchange)
168 extensions.wrapfunction(bookmarks.bmstore, b'_recordchange', recordchange)
170 extensions.wrapfunction(bookmarks.bmstore, b'_writerepo', writerepo)
169 extensions.wrapfunction(bookmarks.bmstore, b'_writerepo', writerepo)
171 extensions.wrapcommand(commands.table, b'clone', clone)
170 extensions.wrapcommand(commands.table, b'clone', clone)
172
171
173
172
174 def _hassharedbookmarks(repo):
173 def _hassharedbookmarks(repo):
175 """Returns whether this repo has shared bookmarks"""
174 """Returns whether this repo has shared bookmarks"""
176 if bookmarks.bookmarksinstore(repo):
175 if bookmarks.bookmarksinstore(repo):
177 # Kind of a lie, but it means that we skip our custom reads and writes
176 # Kind of a lie, but it means that we skip our custom reads and writes
178 # from/to the source repo.
177 # from/to the source repo.
179 return False
178 return False
180 try:
179 try:
181 shared = repo.vfs.read(b'shared').splitlines()
180 shared = repo.vfs.read(b'shared').splitlines()
182 except IOError as inst:
181 except IOError as inst:
183 if inst.errno != errno.ENOENT:
182 if inst.errno != errno.ENOENT:
184 raise
183 raise
185 return False
184 return False
186 return hg.sharedbookmarks in shared
185 return hg.sharedbookmarks in shared
187
186
188
187
189 def getbkfile(orig, repo):
188 def getbkfile(orig, repo):
190 if _hassharedbookmarks(repo):
189 if _hassharedbookmarks(repo):
191 srcrepo = hg.sharedreposource(repo)
190 srcrepo = hg.sharedreposource(repo)
192 if srcrepo is not None:
191 if srcrepo is not None:
193 # just orig(srcrepo) doesn't work as expected, because
192 # just orig(srcrepo) doesn't work as expected, because
194 # HG_PENDING refers repo.root.
193 # HG_PENDING refers repo.root.
195 try:
194 try:
196 fp, pending = txnutil.trypending(
195 fp, pending = txnutil.trypending(
197 repo.root, repo.vfs, b'bookmarks'
196 repo.root, repo.vfs, b'bookmarks'
198 )
197 )
199 if pending:
198 if pending:
200 # only in this case, bookmark information in repo
199 # only in this case, bookmark information in repo
201 # is up-to-date.
200 # is up-to-date.
202 return fp
201 return fp
203 fp.close()
202 fp.close()
204 except IOError as inst:
203 except IOError as inst:
205 if inst.errno != errno.ENOENT:
204 if inst.errno != errno.ENOENT:
206 raise
205 raise
207
206
208 # otherwise, we should read bookmarks from srcrepo,
207 # otherwise, we should read bookmarks from srcrepo,
209 # because .hg/bookmarks in srcrepo might be already
208 # because .hg/bookmarks in srcrepo might be already
210 # changed via another sharing repo
209 # changed via another sharing repo
211 repo = srcrepo
210 repo = srcrepo
212
211
213 # TODO: Pending changes in repo are still invisible in
212 # TODO: Pending changes in repo are still invisible in
214 # srcrepo, because bookmarks.pending is written only into repo.
213 # srcrepo, because bookmarks.pending is written only into repo.
215 # See also https://www.mercurial-scm.org/wiki/SharedRepository
214 # See also https://www.mercurial-scm.org/wiki/SharedRepository
216 return orig(repo)
215 return orig(repo)
217
216
218
217
219 def recordchange(orig, self, tr):
218 def recordchange(orig, self, tr):
220 # Continue with write to local bookmarks file as usual
219 # Continue with write to local bookmarks file as usual
221 orig(self, tr)
220 orig(self, tr)
222
221
223 if _hassharedbookmarks(self._repo):
222 if _hassharedbookmarks(self._repo):
224 srcrepo = hg.sharedreposource(self._repo)
223 srcrepo = hg.sharedreposource(self._repo)
225 if srcrepo is not None:
224 if srcrepo is not None:
226 category = b'share-bookmarks'
225 category = b'share-bookmarks'
227 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
226 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
228
227
229
228
230 def writerepo(orig, self, repo):
229 def writerepo(orig, self, repo):
231 # First write local bookmarks file in case we ever unshare
230 # First write local bookmarks file in case we ever unshare
232 orig(self, repo)
231 orig(self, repo)
233
232
234 if _hassharedbookmarks(self._repo):
233 if _hassharedbookmarks(self._repo):
235 srcrepo = hg.sharedreposource(self._repo)
234 srcrepo = hg.sharedreposource(self._repo)
236 if srcrepo is not None:
235 if srcrepo is not None:
237 orig(self, srcrepo)
236 orig(self, srcrepo)
General Comments 0
You need to be logged in to leave comments. Login now