├── .gitignore ├── .travis.yml ├── .vim-template:.py ├── setup.py ├── indicium └── git │ ├── test.py │ └── __init__.py └── README.rst /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/_build/ 2 | __pycache__/ 3 | /.coverage 4 | *.egg-info/ 5 | *.py[cod] 6 | .*.sw[op] 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "3.4" 5 | - "3.5" 6 | install: 7 | - pip install coverage coveralls 8 | script: 9 | - coverage run --source indicium --omit='indicium/git/test/py' setup.py test 10 | - coverage report -m 11 | after_success: coveralls 12 | -------------------------------------------------------------------------------- /.vim-template:.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:fenc=utf-8 4 | # 5 | # Copyright © %YEAR% Igalia S.L. 6 | # 7 | # Distributed under terms of the GPLv3 or, at your option, 8 | # under the terms of the Apache 2.0 license. 9 | 10 | """ 11 | %HERE% 12 | """ 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:fenc=utf-8 4 | # 5 | # Copyright © 2016 Igalia S.L. 6 | # 7 | # Distributed under terms of the GPLv3 or, at your option, 8 | # under the terms of the Apache 2.0 license. 9 | 10 | from setuptools import setup 11 | 12 | if __name__ == "__main__": 13 | setup( 14 | name="indicium-git", 15 | version="0.1.0a0", 16 | description="Generic key-value interface to a Git repository", 17 | author="Adrián Pérez de Castro", 18 | author_email="aperez@igalia.com", 19 | url="https://github.com/aperezdc/indicium-git", 20 | license=["GPLv3", "Apache-2.0"], 21 | packages=["indicium.git"], 22 | install_requires=[ 23 | "dulwich>=0.12", 24 | "indicium>=0.1.0a2", 25 | ], 26 | classifiers=[ 27 | "Development Status :: 3 - Alpha", 28 | "Intended Audience :: Developers", 29 | "Natural Language :: English", 30 | "Programming Language :: Python :: 3.4", 31 | "Programming Language :: Python :: 3.5", 32 | "Programming Language :: Python", 33 | "Operating System :: OS Independent", 34 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 35 | "License :: OSI Approved :: Apache Software License", 36 | ], 37 | test_suite="indicium.git.test", 38 | ) 39 | -------------------------------------------------------------------------------- /indicium/git/test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:fenc=utf-8 4 | # 5 | # Copyright © 2016 Igalia S.L. 6 | # 7 | # Distributed under terms of the GPLv3 or, at your option, 8 | # under the terms of the Apache 2.0 license. 9 | 10 | from ..test.test_base import TestStoreMethodsMixin, TestStoreOperationsMixin 11 | from ..test.test_fs import TestFSStoreMixin 12 | from indicium.base import BytesSerializer, Serializer 13 | from indicium.git import GitStore 14 | import unittest, tempfile, shutil 15 | 16 | 17 | class TestGitStoreAutocommit(unittest.TestCase, 18 | TestStoreMethodsMixin, 19 | TestStoreOperationsMixin, 20 | TestFSStoreMixin): 21 | 22 | autocommit = True 23 | 24 | def setUp(self): 25 | self.tmpdir_path = tempfile.mkdtemp(prefix="indicium-gitstore") 26 | self.s = BytesSerializer(GitStore(self.tmpdir_path, 27 | extension=".foo", autocommit=self.autocommit)) 28 | 29 | def tearDown(self): 30 | super(TestGitStoreAutocommit, self).tearDown() 31 | shutil.rmtree(self.tmpdir_path, ignore_errors=True) 32 | 33 | 34 | class TestGitStore(TestGitStoreAutocommit): 35 | autocommit = False 36 | 37 | def tearDown(self): 38 | self.s.child.commit("Commit message") 39 | super(TestGitStore, self).tearDown() 40 | 41 | class TestExistingStoreDir(unittest.TestCase): 42 | def setUp(self): 43 | self.tmpdir_path = tempfile.mkdtemp(prefix="indicium-gitstore") 44 | 45 | def tearDown(self): 46 | shutil.rmtree(self.tmpdir_path, ignore_errors=True) 47 | 48 | def test_reopen(self): 49 | GitStore(self.tmpdir_path).put("/answer", b"42") 50 | self.assertEqual(b"42", GitStore(self.tmpdir_path).get("/answer")) 51 | -------------------------------------------------------------------------------- /indicium/git/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:fenc=utf-8 4 | # 5 | # Copyright © 2016 Igalia S.L. 6 | # 7 | # Distributed under terms of the GPLv3 or, at your option, 8 | # under the terms of the Apache 2.0 license. 9 | 10 | from ..fs import FSStore 11 | from ..key import normalize 12 | from os import path as P 13 | from dulwich.repo import Repo 14 | 15 | 16 | class GitStore(FSStore): 17 | __slots__ = ("_autocommit", "_repo") 18 | 19 | author = "GitStore " 20 | 21 | def __init__(self, path=".", extension=".data", autocommit=True): 22 | super(GitStore, self).__init__(path, extension) 23 | self._autocommit = autocommit 24 | gitdir = P.join(self._path, ".git") 25 | if P.isdir(gitdir): 26 | self._repo = Repo(self._path) 27 | else: 28 | self._repo = Repo.init(self._path) 29 | 30 | def commit(self, message, author=None): 31 | message += "\n\nCommitted by indicium.git.GitStore" 32 | if author is None: 33 | author = self.author 34 | author = author.encode() 35 | self._repo.do_commit(committer=author, author=author, 36 | message=message.encode()) 37 | 38 | def put(self, key, value): 39 | super(GitStore, self).put(key, value) 40 | self._repo.stage([self.path_for_key(key)]) 41 | if self._autocommit: 42 | self.commit("put: {!s}".format(normalize(key))) 43 | 44 | def delete(self, key): 45 | path = self.path_for_key(key) 46 | if not P.exists(P.join(self._path, path)): 47 | return 48 | super(GitStore, self).delete(key) 49 | self._repo.stage([path]) 50 | if self._autocommit: 51 | self.commit("delete: {!s}".format(normalize(key))) 52 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Indicium Git Store 3 | ==================== 4 | 5 | .. image:: https://img.shields.io/travis/aperezdc/indicium-git.svg?style=flat 6 | :target: https://travis-ci.org/aperezdc/indicium-git 7 | :alt: Build Status 8 | 9 | .. image:: https://img.shields.io/coveralls/aperezdc/indicium-git/master.svg?style=flat 10 | :target: https://coveralls.io/r/aperezdc/indicium-git?branch=master 11 | :alt: Code Coverage 12 | 13 | A Git-based key-value store backend for `Indicium 14 | `_. 15 | 16 | 17 | Usage 18 | ===== 19 | 20 | .. code-block:: python 21 | 22 | # Instantiate and write some data. 23 | from indicium.git import GitStore 24 | store = GitStore("./data", autocommit=False) 25 | store.put("/the-answer", b"10") 26 | 27 | # Not needed with autocommit=True 28 | store.commit("Note down the answer to everything") 29 | 30 | # Create a new commit with the correct answer. 31 | store.put("/the-answer", b"42") 32 | store.commit("Fix the answer to everything") 33 | 34 | The ``./data`` directory will contain a Git repository which can be inspected 35 | with ``git``. Every call to ``.commit()`` adds a commit to it or, with the 36 | auto-commit mode enabled, every call to ``.put()`` and ``.delete()`` will 37 | implicitly add a commit. 38 | 39 | 40 | Installation 41 | ============ 42 | 43 | All stable releases are uploaded to `PyPI `_, so you 44 | can install them and upgrade using ``pip``:: 45 | 46 | pip install indicium-git 47 | 48 | Alternatively, you can install the latest development code —at your own risk— 49 | directly from the Git repository:: 50 | 51 | pip install git://github.com/aperezdc/indicium-git 52 | 53 | 54 | Development 55 | =========== 56 | 57 | If you want to contribute, please use the usual GitHub workflow: 58 | 59 | 1. Clone the repository. 60 | 2. Hack on your clone. 61 | 3. Send a pull request for review. 62 | 63 | If you do not have programming skills, you can still contribute by `reporting 64 | issues `__ that you may 65 | encounter. Contributions to the documentation are very welcome, too! 66 | --------------------------------------------------------------------------------