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