# HG changeset patch # User Ryan McElroy # Date 1418509915 28800 # Node ID 141baca1605917f36178679c81a6e70a032de0ff # Parent 21446f4d5c62bf052e284f22f7ecc17be801415d share: implement shared bookmark functionality This does not cause any behavioral change unless a 'bookmarks.shared' marker file exists. A future change will add UI to create this file when a repository is shared. diff -r 21446f4d5c62 -r 141baca16059 hgext/share.py --- a/hgext/share.py Sat Dec 13 13:56:05 2014 -0800 +++ b/hgext/share.py Sat Dec 13 14:31:55 2014 -0800 @@ -6,7 +6,9 @@ '''share a common history between several working directories''' from mercurial.i18n import _ -from mercurial import cmdutil, hg, util +from mercurial import cmdutil, hg, util, extensions, bookmarks +from mercurial.hg import repository, parseurl +import errno cmdtable = {} command = cmdutil.command(cmdtable) @@ -67,3 +69,61 @@ # update store, spath, sopener and sjoin of repo repo.unfiltered().__init__(repo.baseui, repo.root) + +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) diff -r 21446f4d5c62 -r 141baca16059 tests/test-share.t --- a/tests/test-share.t Sat Dec 13 13:56:05 2014 -0800 +++ b/tests/test-share.t Sat Dec 13 14:31:55 2014 -0800 @@ -128,6 +128,175 @@ $ cd .. + +test sharing bookmarks (manually add bookmarks.shared file for now) + + $ hg share repo1 repo3 && touch repo3/.hg/bookmarks.shared + updating working directory + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd repo1 + $ hg bookmark bm1 + $ hg bookmarks + * bm1 2:c2e0ac586386 + $ cd ../repo2 + $ hg book bm2 + $ hg bookmarks + * bm2 3:0e6e70d1d5f1 + $ cd ../repo3 + $ hg bookmarks + bm1 2:c2e0ac586386 + $ hg book bm3 + $ hg bookmarks + bm1 2:c2e0ac586386 + * bm3 2:c2e0ac586386 + $ cd ../repo1 + $ hg bookmarks + * bm1 2:c2e0ac586386 + bm3 2:c2e0ac586386 + +test that commits work + + $ echo 'shared bookmarks' > a + $ hg commit -m 'testing shared bookmarks' + $ hg bookmarks + * bm1 3:b87954705719 + bm3 2:c2e0ac586386 + $ cd ../repo3 + $ hg bookmarks + bm1 3:b87954705719 + * bm3 2:c2e0ac586386 + $ echo 'more shared bookmarks' > a + $ hg commit -m 'testing shared bookmarks' + created new head + $ hg bookmarks + bm1 3:b87954705719 + * bm3 4:62f4ded848e4 + $ cd ../repo1 + $ hg bookmarks + * bm1 3:b87954705719 + bm3 4:62f4ded848e4 + $ cd .. + +test pushing bookmarks works + + $ hg clone repo3 repo4 + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd repo4 + $ hg boo bm4 + $ echo foo > b + $ hg commit -m 'foo in b' + $ hg boo + bm1 3:b87954705719 + bm3 4:62f4ded848e4 + * bm4 5:92793bfc8cad + $ hg push -B bm4 + pushing to $TESTTMP/repo3 (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + exporting bookmark bm4 + $ cd ../repo1 + $ hg bookmarks + * bm1 3:b87954705719 + bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ cd ../repo3 + $ hg bookmarks + bm1 3:b87954705719 + * bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ cd .. + +test behavior when sharing a shared repo + + $ hg share repo3 repo5 && touch repo5/.hg/bookmarks.shared + updating working directory + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd repo5 + $ hg book + bm1 3:b87954705719 + bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ cd .. + +test what happens when an active bookmark is deleted + + $ cd repo1 + $ hg boo -d bm3 + $ hg boo + * bm1 3:b87954705719 + bm4 5:92793bfc8cad + $ cd ../repo3 + $ hg boo + bm1 3:b87954705719 + bm4 5:92793bfc8cad + $ cd .. + +verify that bookmarks are not written on failed transaction + + $ cat > failpullbookmarks.py << EOF + > """A small extension that makes bookmark pulls fail, for testing""" + > from mercurial import extensions, exchange, error + > def _pullbookmarks(orig, pullop): + > orig(pullop) + > raise error.HookAbort('forced failure by extension') + > def extsetup(ui): + > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks) + > EOF + $ cd repo4 + $ hg boo + bm1 3:b87954705719 + bm3 4:62f4ded848e4 + * bm4 5:92793bfc8cad + $ cd ../repo3 + $ hg boo + bm1 3:b87954705719 + bm4 5:92793bfc8cad + $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4 + pulling from $TESTTMP/repo4 (glob) + searching for changes + no changes found + adding remote bookmark bm3 + abort: forced failure by extension + [255] + $ hg boo + bm1 3:b87954705719 + bm4 5:92793bfc8cad + $ hg pull $TESTTMP/repo4 + pulling from $TESTTMP/repo4 (glob) + searching for changes + no changes found + adding remote bookmark bm3 + $ hg boo + bm1 3:b87954705719 + * bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ cd .. + +verify bookmark behavior after unshare + + $ cd repo3 + $ hg unshare + $ hg boo + bm1 3:b87954705719 + * bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ hg boo -d bm4 + $ hg boo bm5 + $ hg boo + bm1 3:b87954705719 + bm3 4:62f4ded848e4 + * bm5 4:62f4ded848e4 + $ cd ../repo1 + $ hg boo + * bm1 3:b87954705719 + bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ cd .. + Explicitly kill daemons to let the test exit on Windows $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS