share.py
129 lines
| 4.2 KiB
| text/x-python
|
PythonLexer
/ hgext / share.py
Matt Mackall
|
r8801 | # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> | ||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Matt Mackall
|
r8801 | |||
Cédric Duval
|
r8894 | '''share a common history between several working directories''' | ||
Dirkjan Ochtman
|
r8873 | |||
Matt Mackall
|
r8801 | from mercurial.i18n import _ | ||
Ryan McElroy
|
r23548 | from mercurial import cmdutil, hg, util, extensions, bookmarks | ||
from mercurial.hg import repository, parseurl | ||||
import errno | ||||
Simon Heimberg
|
r15079 | |||
Gregory Szorc
|
r21253 | cmdtable = {} | ||
command = cmdutil.command(cmdtable) | ||||
Augie Fackler
|
r16743 | testedwith = 'internal' | ||
Gregory Szorc
|
r21253 | @command('share', | ||
[('U', 'noupdate', None, _('do not create a working copy'))], | ||||
Gregory Szorc
|
r21772 | _('[-U] SOURCE [DEST]'), | ||
norepo=True) | ||||
Matt Mackall
|
r8807 | def share(ui, source, dest=None, noupdate=False): | ||
Martin Geisler
|
r10798 | """create a new shared repository | ||
Matt Mackall
|
r8801 | |||
Martin Geisler
|
r9273 | Initialize a new repository and working directory that shares its | ||
history with another repository. | ||||
Matt Mackall
|
r8801 | |||
Erik Zielke
|
r12389 | .. note:: | ||
Simon Heimberg
|
r19997 | |||
Erik Zielke
|
r12389 | using rollback or extensions that destroy/modify history (mq, | ||
rebase, etc.) can cause considerable confusion with shared | ||||
clones. In particular, if two shared clones are both updated to | ||||
the same changeset, and one of them destroys that changeset | ||||
with rollback, the other clone will suddenly stop working: all | ||||
operations will fail with "abort: working directory has unknown | ||||
parent". The only known workaround is to use debugsetparents on | ||||
Matt Mackall
|
r19399 | the broken clone to reset it to a changeset that still exists. | ||
Matt Mackall
|
r8801 | """ | ||
return hg.share(ui, source, dest, not noupdate) | ||||
Gregory Szorc
|
r21253 | @command('unshare', [], '') | ||
Simon Heimberg
|
r15079 | def unshare(ui, repo): | ||
"""convert a shared repository to a normal one | ||||
Copy the store data to the repo and remove the sharedpath data. | ||||
""" | ||||
if repo.sharedpath == repo.path: | ||||
raise util.Abort(_("this is not a shared repo")) | ||||
destlock = lock = None | ||||
lock = repo.lock() | ||||
try: | ||||
# we use locks here because if we race with commit, we | ||||
# can end up with extra data in the cloned revlogs that's | ||||
# not pointed to by changesets, thus causing verify to | ||||
# fail | ||||
destlock = hg.copystore(ui, repo, repo.path) | ||||
sharefile = repo.join('sharedpath') | ||||
util.rename(sharefile, sharefile + '.old') | ||||
repo.requirements.discard('sharedpath') | ||||
repo._writerequirements() | ||||
finally: | ||||
destlock and destlock.release() | ||||
lock and lock.release() | ||||
# update store, spath, sopener and sjoin of repo | ||||
Brodie Rao
|
r20056 | repo.unfiltered().__init__(repo.baseui, repo.root) | ||
Ryan McElroy
|
r23548 | |||
def extsetup(ui): | ||||
extensions.wrapfunction(bookmarks.bmstore, 'getbkfile', getbkfile) | ||||
extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange) | ||||
extensions.wrapfunction(bookmarks.bmstore, 'write', write) | ||||
def _hassharedbookmarks(repo): | ||||
"""Returns whether this repo has shared bookmarks""" | ||||
try: | ||||
repo.vfs.read('bookmarks.shared') | ||||
return True | ||||
except IOError, inst: | ||||
if inst.errno != errno.ENOENT: | ||||
raise | ||||
return False | ||||
def _getsrcrepo(repo): | ||||
""" | ||||
Returns the source repository object for a given shared repository. | ||||
If repo is not a shared repository, return None. | ||||
""" | ||||
srcrepo = None | ||||
try: | ||||
# strip because some tools write with newline after | ||||
sharedpath = repo.vfs.read('sharedpath').strip() | ||||
# the sharedpath always ends in the .hg; we want the path to the repo | ||||
source = sharedpath.rsplit('/.hg', 1)[0] | ||||
srcurl, branches = parseurl(source) | ||||
srcrepo = repository(repo.ui, srcurl) | ||||
except IOError, inst: | ||||
if inst.errno != errno.ENOENT: | ||||
raise | ||||
return srcrepo | ||||
def getbkfile(orig, self, repo): | ||||
if _hassharedbookmarks(repo): | ||||
srcrepo = _getsrcrepo(repo) | ||||
if srcrepo is not None: | ||||
repo = srcrepo | ||||
return orig(self, repo) | ||||
def recordchange(orig, self, tr): | ||||
# Continue with write to local bookmarks file as usual | ||||
orig(self, tr) | ||||
if _hassharedbookmarks(self._repo): | ||||
srcrepo = _getsrcrepo(self._repo) | ||||
if srcrepo is not None: | ||||
category = 'share-bookmarks' | ||||
tr.addpostclose(category, lambda tr: self._writerepo(srcrepo)) | ||||
def write(orig, self): | ||||
# First write local bookmarks file in case we ever unshare | ||||
orig(self) | ||||
if _hassharedbookmarks(self._repo): | ||||
srcrepo = _getsrcrepo(self._repo) | ||||
if srcrepo is not None: | ||||
self._writerepo(srcrepo) | ||||