##// END OF EJS Templates
share: pass named arguments...
Gregory Szorc -
r27353:98e59d9e default
parent child Browse files
Show More
@@ -1,176 +1,177 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 mercurial.i18n import _
40 from mercurial.i18n import _
41 from mercurial import cmdutil, commands, hg, util, extensions, bookmarks, error
41 from mercurial import cmdutil, commands, hg, util, extensions, bookmarks, error
42 from mercurial.hg import repository, parseurl
42 from mercurial.hg import repository, parseurl
43 import errno
43 import errno
44
44
45 cmdtable = {}
45 cmdtable = {}
46 command = cmdutil.command(cmdtable)
46 command = cmdutil.command(cmdtable)
47 # Note for extension authors: ONLY specify testedwith = 'internal' for
47 # Note for extension authors: ONLY specify testedwith = 'internal' for
48 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
48 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
49 # be specifying the version(s) of Mercurial they are tested with, or
49 # be specifying the version(s) of Mercurial they are tested with, or
50 # leave the attribute unspecified.
50 # leave the attribute unspecified.
51 testedwith = 'internal'
51 testedwith = 'internal'
52
52
53 @command('share',
53 @command('share',
54 [('U', 'noupdate', None, _('do not create a working directory')),
54 [('U', 'noupdate', None, _('do not create a working directory')),
55 ('B', 'bookmarks', None, _('also share bookmarks'))],
55 ('B', 'bookmarks', None, _('also share bookmarks'))],
56 _('[-U] [-B] SOURCE [DEST]'),
56 _('[-U] [-B] SOURCE [DEST]'),
57 norepo=True)
57 norepo=True)
58 def share(ui, source, dest=None, noupdate=False, bookmarks=False):
58 def share(ui, source, dest=None, noupdate=False, bookmarks=False):
59 """create a new shared repository
59 """create a new shared repository
60
60
61 Initialize a new repository and working directory that shares its
61 Initialize a new repository and working directory that shares its
62 history (and optionally bookmarks) with another repository.
62 history (and optionally bookmarks) with another repository.
63
63
64 .. note::
64 .. note::
65
65
66 using rollback or extensions that destroy/modify history (mq,
66 using rollback or extensions that destroy/modify history (mq,
67 rebase, etc.) can cause considerable confusion with shared
67 rebase, etc.) can cause considerable confusion with shared
68 clones. In particular, if two shared clones are both updated to
68 clones. In particular, if two shared clones are both updated to
69 the same changeset, and one of them destroys that changeset
69 the same changeset, and one of them destroys that changeset
70 with rollback, the other clone will suddenly stop working: all
70 with rollback, the other clone will suddenly stop working: all
71 operations will fail with "abort: working directory has unknown
71 operations will fail with "abort: working directory has unknown
72 parent". The only known workaround is to use debugsetparents on
72 parent". The only known workaround is to use debugsetparents on
73 the broken clone to reset it to a changeset that still exists.
73 the broken clone to reset it to a changeset that still exists.
74 """
74 """
75
75
76 return hg.share(ui, source, dest, not noupdate, bookmarks)
76 return hg.share(ui, source, dest=dest, update=not noupdate,
77 bookmarks=bookmarks)
77
78
78 @command('unshare', [], '')
79 @command('unshare', [], '')
79 def unshare(ui, repo):
80 def unshare(ui, repo):
80 """convert a shared repository to a normal one
81 """convert a shared repository to a normal one
81
82
82 Copy the store data to the repo and remove the sharedpath data.
83 Copy the store data to the repo and remove the sharedpath data.
83 """
84 """
84
85
85 if not repo.shared():
86 if not repo.shared():
86 raise error.Abort(_("this is not a shared repo"))
87 raise error.Abort(_("this is not a shared repo"))
87
88
88 destlock = lock = None
89 destlock = lock = None
89 lock = repo.lock()
90 lock = repo.lock()
90 try:
91 try:
91 # we use locks here because if we race with commit, we
92 # we use locks here because if we race with commit, we
92 # can end up with extra data in the cloned revlogs that's
93 # can end up with extra data in the cloned revlogs that's
93 # not pointed to by changesets, thus causing verify to
94 # not pointed to by changesets, thus causing verify to
94 # fail
95 # fail
95
96
96 destlock = hg.copystore(ui, repo, repo.path)
97 destlock = hg.copystore(ui, repo, repo.path)
97
98
98 sharefile = repo.join('sharedpath')
99 sharefile = repo.join('sharedpath')
99 util.rename(sharefile, sharefile + '.old')
100 util.rename(sharefile, sharefile + '.old')
100
101
101 repo.requirements.discard('sharedpath')
102 repo.requirements.discard('sharedpath')
102 repo._writerequirements()
103 repo._writerequirements()
103 finally:
104 finally:
104 destlock and destlock.release()
105 destlock and destlock.release()
105 lock and lock.release()
106 lock and lock.release()
106
107
107 # update store, spath, svfs and sjoin of repo
108 # update store, spath, svfs and sjoin of repo
108 repo.unfiltered().__init__(repo.baseui, repo.root)
109 repo.unfiltered().__init__(repo.baseui, repo.root)
109
110
110 # Wrap clone command to pass auto share options.
111 # Wrap clone command to pass auto share options.
111 def clone(orig, ui, source, *args, **opts):
112 def clone(orig, ui, source, *args, **opts):
112 pool = ui.config('share', 'pool', None)
113 pool = ui.config('share', 'pool', None)
113 if pool:
114 if pool:
114 pool = util.expandpath(pool)
115 pool = util.expandpath(pool)
115
116
116 opts['shareopts'] = dict(
117 opts['shareopts'] = dict(
117 pool=pool,
118 pool=pool,
118 mode=ui.config('share', 'poolnaming', 'identity'),
119 mode=ui.config('share', 'poolnaming', 'identity'),
119 )
120 )
120
121
121 return orig(ui, source, *args, **opts)
122 return orig(ui, source, *args, **opts)
122
123
123 def extsetup(ui):
124 def extsetup(ui):
124 extensions.wrapfunction(bookmarks, '_getbkfile', getbkfile)
125 extensions.wrapfunction(bookmarks, '_getbkfile', getbkfile)
125 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
126 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
126 extensions.wrapfunction(bookmarks.bmstore, '_writerepo', writerepo)
127 extensions.wrapfunction(bookmarks.bmstore, '_writerepo', writerepo)
127 extensions.wrapcommand(commands.table, 'clone', clone)
128 extensions.wrapcommand(commands.table, 'clone', clone)
128
129
129 def _hassharedbookmarks(repo):
130 def _hassharedbookmarks(repo):
130 """Returns whether this repo has shared bookmarks"""
131 """Returns whether this repo has shared bookmarks"""
131 try:
132 try:
132 shared = repo.vfs.read('shared').splitlines()
133 shared = repo.vfs.read('shared').splitlines()
133 except IOError as inst:
134 except IOError as inst:
134 if inst.errno != errno.ENOENT:
135 if inst.errno != errno.ENOENT:
135 raise
136 raise
136 return False
137 return False
137 return 'bookmarks' in shared
138 return 'bookmarks' in shared
138
139
139 def _getsrcrepo(repo):
140 def _getsrcrepo(repo):
140 """
141 """
141 Returns the source repository object for a given shared repository.
142 Returns the source repository object for a given shared repository.
142 If repo is not a shared repository, return None.
143 If repo is not a shared repository, return None.
143 """
144 """
144 if repo.sharedpath == repo.path:
145 if repo.sharedpath == repo.path:
145 return None
146 return None
146
147
147 # the sharedpath always ends in the .hg; we want the path to the repo
148 # the sharedpath always ends in the .hg; we want the path to the repo
148 source = repo.vfs.split(repo.sharedpath)[0]
149 source = repo.vfs.split(repo.sharedpath)[0]
149 srcurl, branches = parseurl(source)
150 srcurl, branches = parseurl(source)
150 return repository(repo.ui, srcurl)
151 return repository(repo.ui, srcurl)
151
152
152 def getbkfile(orig, repo):
153 def getbkfile(orig, repo):
153 if _hassharedbookmarks(repo):
154 if _hassharedbookmarks(repo):
154 srcrepo = _getsrcrepo(repo)
155 srcrepo = _getsrcrepo(repo)
155 if srcrepo is not None:
156 if srcrepo is not None:
156 repo = srcrepo
157 repo = srcrepo
157 return orig(repo)
158 return orig(repo)
158
159
159 def recordchange(orig, self, tr):
160 def recordchange(orig, self, tr):
160 # Continue with write to local bookmarks file as usual
161 # Continue with write to local bookmarks file as usual
161 orig(self, tr)
162 orig(self, tr)
162
163
163 if _hassharedbookmarks(self._repo):
164 if _hassharedbookmarks(self._repo):
164 srcrepo = _getsrcrepo(self._repo)
165 srcrepo = _getsrcrepo(self._repo)
165 if srcrepo is not None:
166 if srcrepo is not None:
166 category = 'share-bookmarks'
167 category = 'share-bookmarks'
167 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
168 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
168
169
169 def writerepo(orig, self, repo):
170 def writerepo(orig, self, repo):
170 # First write local bookmarks file in case we ever unshare
171 # First write local bookmarks file in case we ever unshare
171 orig(self, repo)
172 orig(self, repo)
172
173
173 if _hassharedbookmarks(self._repo):
174 if _hassharedbookmarks(self._repo):
174 srcrepo = _getsrcrepo(self._repo)
175 srcrepo = _getsrcrepo(self._repo)
175 if srcrepo is not None:
176 if srcrepo is not None:
176 orig(self, srcrepo)
177 orig(self, srcrepo)
General Comments 0
You need to be logged in to leave comments. Login now