Пример #1
0
def add_repo():
    """Create a new repository object

    Return {'url': '/<repo_id>/'}
    """

    auth_context = get_auth_context()

    if not auth_context.can_create_repos:
        return abort(http.UNAUTHORIZED)

    # create a new auth context as the initial write key
    writer = KeyAuthContext()
    writer.save()

    meta = RepoMeta()
    meta.is_public = bool(request.form.get('is_public'))
    meta.access_admin.append(auth_context)
    meta.access_write.append(writer)

    meta.save()

    name = meta._id

    repo = Repository(os.path.join(current_app.config.get('git_root'), name))
    repo.init()

    return jsonify({
        'url': url_for('.get_repo', repo_id=name),
        'write_key': writer._id,
    })
Пример #2
0
def add_repo():
    """Create a new repository object

    Return {'url': '/<repo_id>/'}
    """

    auth_context = get_auth_context()

    if not auth_context.can_create_repos:
        return abort(http.UNAUTHORIZED)

    meta = RepoMeta()
    meta.is_public = bool(request.form.get('is_public'))
    meta.access_admin.append(auth_context)

    meta.save()

    name = meta._id

    repo = Repository(
        os.path.join(
            current_app.config.get('git_root'),
            name
        )
    )
    repo.init()

    return jsonify({
        'url': url_for('.get_repo', repo_id=name)
    })
Пример #3
0
def get_repo(repo_id):
    """Return information about the repo.

    Meta: (immutable through this endpoint)
        * file tree
        * date repo was created
        * list of users by access level
        * access level of current auth context

    Settings: (mutable)
        * list of users by access level
        * remote repos with which to sync
        * privacy setting: (public or private)
    """
    auth_context = get_auth_context()

    try:
        if not auth_context.can_read_repo(repo_id):
            return abort(http.UNAUTHORIZED)
    except KeyError:
        pass

    repo = Repository(os.path.join(current_app.config.get('git_root'),
                                   repo_id))

    if not repo.is_valid_repo:
        abort(404)

    return jsonify({})
Пример #4
0
 def cloned_repo(self):
     return Repository(
         os.path.join(
             self.base_path,
             CLONED_REPO,
         )
     )
Пример #5
0
    def setUp(self):
        self.entry_path = os.getcwd()

        # base path containing all repos in test
        self.base_path = tempfile.mkdtemp()

        # path of the primary repo
        self.path = os.path.join(
            self.base_path,
            TEST_REPO,
        )

        # instantiate the repo's Repository object
        self.repo = Repository(self.path)

        # initialize the repo
        self.repo.init()

        # change directory to the repo's root
        os.chdir(self.path)
    def setUp(self):
        self.entry_path = os.getcwd()

        # base path containing all repos in test
        self.base_path = tempfile.mkdtemp()

        # path of the primary repo
        self.path = os.path.join(self.base_path, TEST_REPO)

        # instantiate the repo's Repository object
        self.repo = Repository(self.path)

        # initialize the repo
        self.repo.init()

        # change directory to the repo's root
        os.chdir(self.path)
Пример #7
0
from flask import Flask, jsonify, render_template, request
import json
import os
import tempfile

app = Flask(__name__)

from git_subprocess import Repository

repo_path = '/tmp/test/'

# Set up a git repository for a storage backend
repo = Repository(repo_path or tempfile.mkdtemp())
repo.init()


# Homepage - just render the template
@app.route('/')
def index():
    return render_template('index.html')


# DELETE verb
@app.route('/api/files/', methods=[
    'DELETE',
])
def delete_files():
    # since multiple items could be deleted at once, iterate the list.
    for id in json.loads(request.form.get('ids', '[]')):
        repo._rm_file(id)
    repo.commit(
Пример #8
0
def get_repo(id):
    return Repository(os.path.join(current_app.config.get('git_root'), id))
class GitSubprocessTestCase(unittest.TestCase):
    def setUp(self):
        self.entry_path = os.getcwd()

        # base path containing all repos in test
        self.base_path = tempfile.mkdtemp()

        # path of the primary repo
        self.path = os.path.join(self.base_path, TEST_REPO)

        # instantiate the repo's Repository object
        self.repo = Repository(self.path)

        # initialize the repo
        self.repo.init()

        # change directory to the repo's root
        os.chdir(self.path)

    @property
    def cloned_repo(self):
        return Repository(os.path.join(self.base_path, CLONED_REPO))

    def tearDown(self):
        shutil.rmtree(self.base_path)
        os.chdir(self.entry_path)

    def test_init(self):
        utils.silence(subprocess.check_call, ("git", "status"))

    def test_stage_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)

        self.assertIn("A  {}".format(FILE_NAME), subprocess.check_output(("git", "status", "-s")))

    def test_unstage_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo._unstage_file(FILE_NAME)

        self.assertIn("?? {}".format(FILE_NAME), subprocess.check_output(("git", "status", "-s")))

    def test_commit(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertNotIn(FILE_NAME, subprocess.check_output(("git", "status", "-s")))

    def test_add_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo.add_file(file_path=FILE_NAME, commit_author=AUTHOR_STRING, commit_message=COMMIT_MESSAGE)

        self.assertNotIn(FILE_NAME, subprocess.check_output(("git", "status", "-s")))

    def test_rm_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo._rm_file(FILE_NAME)
        self.assertIn("D  {}".format(FILE_NAME), subprocess.check_output(("git", "status", "-s")))

    def test_delete_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo.delete_file(file_path=FILE_NAME, commit_author=AUTHOR_STRING, commit_message=COMMIT_MESSAGE)

        self.assertNotIn(FILE_NAME, subprocess.check_output(("git", "status", "-s")))

    def test_mv_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo._mv_file(FILE_NAME, FILE_NAME_2)

        self.assertIn("R  foo.txt -> bar.txt", subprocess.check_output(("git", "status", "-s")))

    def test_move_file(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo.move_file(
            old_path=FILE_NAME, new_path=FILE_NAME_2, commit_author=AUTHOR_STRING, commit_message=COMMIT_MESSAGE
        )

        self.assertNotIn(FILE_NAME, subprocess.check_output(("git", "status", "-s")))
        self.assertNotIn(FILE_NAME_2, subprocess.check_output(("git", "status", "-s")))

    def test_clone_repo(self):
        subprocess.call(("touch", FILE_NAME), cwd=self.path)
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.cloned_repo.clone_from(self.path)

        self.assertNotEqual(
            os.path.abspath(self.cloned_repo.path), os.path.abspath(self.repo.path)  # new repo  # old repo
        )

        self.assertIn(FILE_NAME, subprocess.check_output("ls"))

    def test_staged_files(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)
        subprocess.call(("touch", FILE_NAME_2))
        self.repo._stage_file(FILE_NAME_2)

        self.assertEqual(set(self.repo.staged_files), set((FILE_NAME_2,)))

    def test_unstaged_files(self):
        subprocess.call(("touch", FILE_NAME))
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)
        subprocess.call(("touch", FILE_NAME_2))

        self.assertEqual(set(self.repo.unstaged_files), set((FILE_NAME_2,)))

    def test_untracked_files(self):
        subprocess.call(("touch", FILE_NAME))
        subprocess.call(("touch", FILE_NAME_2))

        self.assertEqual(set(self.repo.untracked_files), set((FILE_NAME, FILE_NAME_2)))

    def test_get_file_contents_by_sha(self):
        content_versions = ("0", "1", "2", "3", "4")
        for text in content_versions:
            with open(FILE_NAME, "w") as f:
                f.write(text)
            self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        shas = self._get_shas(FILE_NAME)

        for i, text in enumerate(content_versions):
            self.assertEqual(self.repo._get_file_content(path=FILE_NAME, sha=shas[i]), text)

    def _get_shas(self, path):
        try:
            with open(os.devnull) as f:
                return [
                    x.strip('"')
                    for x in subprocess.check_output(("git", "log", '--format="%H"', "--", path), stderr=f)
                    .strip()
                    .split("\n")[::-1]
                ]
        except subprocess.CalledProcessError:
            return []

    def test_get_file_invalid_path(self):
        with self.assertRaises(ValueError):
            self.repo.get_file("not_a_real_file.txt")

    def test_get_file(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(repr(self.repo.get_file(FILE_NAME)), "^<File")

    def test_get_file_content(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(self.repo.get_file(FILE_NAME).content, file_content)

    def test_get_file_author(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(self.repo.get_file(FILE_NAME).author, AUTHOR_STRING)

    def test_get_file_refspec(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(str(self.repo.get_file(FILE_NAME)), FILE_NAME)

    def test_get_file_commit_message(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(self.repo.get_file(FILE_NAME).message, COMMIT_MESSAGE)

    def test_get_file_version(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(repr(self.repo.get_file(FILE_NAME).versions[0]), "^<FileVersion")

    def test_get_file_version_refspec(self):
        file_content = "Center for Open Science"
        with open(FILE_NAME, "w") as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(str(self.repo.get_file(FILE_NAME).versions[0]), "^(\w){40} -- foo\.txt$")

    def test_get_file_version_by_sha(self):
        content_versions = ("0", "1", "2", "3", "4")
        for text in content_versions:
            with open(FILE_NAME, "w") as f:
                f.write(text)
            self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        shas = self._get_shas(FILE_NAME)

        for i, text in enumerate(content_versions):
            self.assertEqual(self.repo.get_file(path=FILE_NAME).get_version_by_sha(shas[i]).content, str(i))
class GitSubprocessTestCase(unittest.TestCase):
    def setUp(self):
        self.entry_path = os.getcwd()

        # base path containing all repos in test
        self.base_path = tempfile.mkdtemp()

        # path of the primary repo
        self.repo_path = os.path.join(
            self.base_path,
            TEST_REPO,
        )

        # make sure that path exists
        os.mkdir(self.repo_path)

        # instantiate the repo's Repository object
        self.repo = Repository(self.repo_path)

        # initialize the repo
        self.repo.init()

        # change directory to the repo's root
        os.chdir(self.repo_path)

    @property
    def cloned_repo(self):
        return Repository(
            os.path.join(
                self.base_path,
                CLONED_REPO,
            )
        )

    def tearDown(self):
        shutil.rmtree(self.base_path)
        os.chdir(self.entry_path)

    def test_init(self):
        utils.silence(
            subprocess.check_call,
            ('git', 'status')
        )

    def test_stage_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)

        self.assertIn(
            'A  {}'.format(FILE_NAME),
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_unstage_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo._unstage_file(FILE_NAME)

        self.assertIn(
            '?? {}'.format(FILE_NAME),
            subprocess.check_output(('git', 'status', '-s'))
        )

    def test_commit(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_add_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo.add_file(
            file_path=FILE_NAME,
            commit_author=AUTHOR_STRING,
            commit_message=COMMIT_MESSAGE,
        )

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_rm_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo._rm_file(FILE_NAME)
        self.assertIn(
            'D  {}'.format(FILE_NAME),
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_delete_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo.delete_file(
            file_path=FILE_NAME,
            commit_author=AUTHOR_STRING,
            commit_message=COMMIT_MESSAGE,
        )

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s'))
        )

    def test_mv_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo._mv_file(FILE_NAME, FILE_NAME_2)

        self.assertIn(
            'R  foo.txt -> bar.txt',
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_move_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo.move_file(
            old_path=FILE_NAME,
            new_path=FILE_NAME_2,
            commit_author=AUTHOR_STRING,
            commit_message=COMMIT_MESSAGE,
        )

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s'))
        )
        self.assertNotIn(
            FILE_NAME_2,
            subprocess.check_output(('git', 'status', '-s'))
        )

    def test_clone_repo(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.cloned_repo.clone_from(self.repo_path)

        os.chdir(self.cloned_repo.repo_path)

        self.assertNotEqual(
            os.getcwd(),  # new repo
            os.path.abspath(self.repo_path)  # old repo
        )

        self.assertIn(
            FILE_NAME,
            subprocess.check_output('ls')
        )

    def test_staged_files(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)
        subprocess.call(('touch', FILE_NAME_2))
        self.repo._stage_file(FILE_NAME_2)

        self.assertEqual(
            set(self.repo.staged_files),
            set((FILE_NAME_2,)),
        )

    def test_unstaged_files(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)
        subprocess.call(('touch', FILE_NAME_2))

        self.assertEqual(
            set(self.repo.unstaged_files),
            set((FILE_NAME_2,)),
        )

    def test_untracked_files(self):
        subprocess.call(('touch', FILE_NAME))
        subprocess.call(('touch', FILE_NAME_2))

        self.assertEqual(
            set(self.repo.untracked_files),
            set((FILE_NAME, FILE_NAME_2,)),
        )

    def test_get_file_contents_by_sha(self):
        content_versions = ('0', '1', '2', '3', '4')
        for text in content_versions:
            with open(FILE_NAME, 'w') as f:
                f.write(text)
            self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        shas = self._get_shas(FILE_NAME)

        for i, text in enumerate(content_versions):
            self.assertEqual(
                self.repo._get_file_content(path=FILE_NAME, sha=shas[i]),
                text
            )

    def _get_shas(self, path):
        try:
            with open(os.devnull) as f:
                return [
                    x.strip('"')
                    for x
                    in subprocess.check_output(
                        ('git', 'log', '--format="%H"', '--', path),
                        stderr=f
                    ).strip().split('\n')[::-1]
                ]
        except subprocess.CalledProcessError:
            return []

    def test_get_file_contents(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        sha = subprocess.check_output(
            ('git', 'log', '--format="%H"', '--', 'foo.txt')
        ).strip('"\n')

        self.assertEqual(
            self.repo._get_file_content(path=FILE_NAME),
            file_content
        )

    def test_get_file_invalid_path(self):
        with self.assertRaises(ValueError):
            self.repo.get_file('not_a_real_file.txt')

    def test_get_file(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(
            repr(self.repo.get_file(FILE_NAME)),
            '^<File'
        )

    def test_get_file_content(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            self.repo.get_file(FILE_NAME).content,
            file_content
        )

    def test_get_file_author(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            self.repo.get_file(FILE_NAME).author,
            AUTHOR_STRING
        )

    def test_get_file_refspec(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            str(self.repo.get_file(FILE_NAME)),
            FILE_NAME
        )

    def test_get_file_version(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(
            repr(self.repo.get_file(FILE_NAME).versions[0]),
            '^<FileVersion'
        )

    def test_get_file_version_refspec(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(
            str(self.repo.get_file(FILE_NAME).versions[0]),
            '^(\w){40} -- foo\.txt$'
        )
Пример #11
0
from flask import abort, Flask, jsonify, render_template, request
import json
import os
import tempfile
from distutils.dir_util import mkpath

app = Flask(__name__)

from git_subprocess import Repository

repo_path = '/tmp/test' #'/Users/lyndsysimon/Documents/Development/cos/hgrid-example/'

# Set up a git repository for a storage backend
repo = Repository(repo_path or tempfile.mkdtemp())
repo.init()

# Homepage - just render the template
@app.route('/')
def index():
    return render_template('index.html')

# DELETE verb
@app.route('/api/files/', methods=['DELETE', ])
def delete_files():
    # since multiple items could be deleted at once, iterate the list.
    for id in json.loads(request.form.get('ids', '[]')):
        repo._rm_file(id)
    repo.commit(
        author='Internet User <*****@*****.**>',
        message='Deleted file(s)',
    )
Пример #12
0
class GitSubprocessTestCase(unittest.TestCase):
    def setUp(self):
        self.entry_path = os.getcwd()

        # base path containing all repos in test
        self.base_path = tempfile.mkdtemp()

        # path of the primary repo
        self.path = os.path.join(
            self.base_path,
            TEST_REPO,
        )

        # instantiate the repo's Repository object
        self.repo = Repository(self.path)

        # initialize the repo
        self.repo.init()

        # change directory to the repo's root
        os.chdir(self.path)

    @property
    def cloned_repo(self):
        return Repository(
            os.path.join(
                self.base_path,
                CLONED_REPO,
            )
        )

    def tearDown(self):
        shutil.rmtree(self.base_path)
        os.chdir(self.entry_path)

    def test_init(self):
        utils.silence(
            subprocess.check_call,
            ('git', 'status')
        )

    def test_stage_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)

        self.assertIn(
            'A  {}'.format(FILE_NAME),
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_unstage_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo._unstage_file(FILE_NAME)

        self.assertIn(
            '?? {}'.format(FILE_NAME),
            subprocess.check_output(('git', 'status', '-s'))
        )

    def test_commit(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_add_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo.add_file(
            file_path=FILE_NAME,
            commit_author=AUTHOR_STRING,
            commit_message=COMMIT_MESSAGE,
        )

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_rm_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo._rm_file(FILE_NAME)
        self.assertIn(
            'D  {}'.format(FILE_NAME),
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_delete_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo.delete_file(
            file_path=FILE_NAME,
            commit_author=AUTHOR_STRING,
            commit_message=COMMIT_MESSAGE,
        )

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s'))
        )

    def test_mv_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo._mv_file(FILE_NAME, FILE_NAME_2)

        self.assertIn(
            'R  foo.txt -> bar.txt',
            subprocess.check_output(('git', 'status', '-s')),
        )

    def test_move_file(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.repo.move_file(
            old_path=FILE_NAME,
            new_path=FILE_NAME_2,
            commit_author=AUTHOR_STRING,
            commit_message=COMMIT_MESSAGE,
        )

        self.assertNotIn(
            FILE_NAME,
            subprocess.check_output(('git', 'status', '-s'))
        )
        self.assertNotIn(
            FILE_NAME_2,
            subprocess.check_output(('git', 'status', '-s'))
        )

    def test_clone_repo(self):
        subprocess.call(
            ('touch', FILE_NAME),
            cwd=self.path,
        )
        self.repo._stage_file(FILE_NAME)
        self.repo.commit(AUTHOR_STRING, COMMIT_MESSAGE)

        self.cloned_repo.clone_from(self.path)

        self.assertNotEqual(
            os.path.abspath(self.cloned_repo.path),  # new repo
            os.path.abspath(self.repo.path)  # old repo
        )

        self.assertIn(
            FILE_NAME,
            subprocess.check_output('ls')
        )

    def test_staged_files(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)
        subprocess.call(('touch', FILE_NAME_2))
        self.repo._stage_file(FILE_NAME_2)

        self.assertEqual(
            set(self.repo.staged_files),
            set((FILE_NAME_2,)),
        )

    def test_unstaged_files(self):
        subprocess.call(('touch', FILE_NAME))
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)
        subprocess.call(('touch', FILE_NAME_2))

        self.assertEqual(
            set(self.repo.unstaged_files),
            set((FILE_NAME_2,)),
        )

    def test_untracked_files(self):
        subprocess.call(('touch', FILE_NAME))
        subprocess.call(('touch', FILE_NAME_2))

        self.assertEqual(
            set(self.repo.untracked_files),
            set((FILE_NAME, FILE_NAME_2,)),
        )

    def test_get_file_contents_by_sha(self):
        content_versions = ('0', '1', '2', '3', '4')
        for text in content_versions:
            with open(FILE_NAME, 'w') as f:
                f.write(text)
            self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        shas = self._get_shas(FILE_NAME)

        for i, text in enumerate(content_versions):
            self.assertEqual(
                self.repo._get_file_content(path=FILE_NAME, sha=shas[i]),
                text
            )

    def _get_shas(self, path):
        try:
            with open(os.devnull) as f:
                return [
                    x.strip('"')
                    for x
                    in subprocess.check_output(
                        ('git', 'log', '--format="%H"', '--', path),
                        stderr=f
                    ).strip().split('\n')[::-1]
                ]
        except subprocess.CalledProcessError:
            return []

    def test_get_file_invalid_path(self):
        with self.assertRaises(ValueError):
            self.repo.get_file('not_a_real_file.txt')

    def test_get_file(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(
            repr(self.repo.get_file(FILE_NAME)),
            '^<File'
        )

    def test_get_file_content(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            self.repo.get_file(FILE_NAME).content,
            file_content
        )

    def test_get_file_author(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            self.repo.get_file(FILE_NAME).author,
            AUTHOR_STRING
        )

    def test_get_file_refspec(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            str(self.repo.get_file(FILE_NAME)),
            FILE_NAME
        )

    def test_get_file_commit_message(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertEqual(
            self.repo.get_file(FILE_NAME).message,
            COMMIT_MESSAGE
        )

    def test_get_file_version(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(
            repr(self.repo.get_file(FILE_NAME).versions[0]),
            '^<FileVersion'
        )

    def test_get_file_version_refspec(self):
        file_content = 'Center for Open Science'
        with open(FILE_NAME, 'w') as f:
            f.write(file_content)
        self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        self.assertRegexpMatches(
            str(self.repo.get_file(FILE_NAME).versions[0]),
            '^(\w){40} -- foo\.txt$'
        )

    def test_get_file_version_by_sha(self):
        content_versions = ('0', '1', '2', '3', '4')
        for text in content_versions:
            with open(FILE_NAME, 'w') as f:
                f.write(text)
            self.repo.add_file(FILE_NAME, AUTHOR_STRING, COMMIT_MESSAGE)

        shas = self._get_shas(FILE_NAME)

        for i, text in enumerate(content_versions):
            self.assertEqual(
                self.repo.get_file(path=FILE_NAME).get_version_by_sha(shas[i]).content,
                str(i)
            )