From 636d33dbf99e81fbee566da15af96ab65b449a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 20 May 2015 15:53:58 +0200 Subject: [PATCH] Add support for querying attributes Expose a method in the repository which allows querying an attribute for a file and converts the result to the python equivalent. --- pygit2/__init__.py | 6 ++++ pygit2/decl.h | 15 ++++++++++ pygit2/repository.py | 38 ++++++++++++++++++++++++ test/test_attributes.py | 65 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 test/test_attributes.py diff --git a/pygit2/__init__.py b/pygit2/__init__.py index a2dd7bfae..d963bcaa5 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -65,6 +65,12 @@ GIT_REPOSITORY_INIT_SHARED_GROUP = C.GIT_REPOSITORY_INIT_SHARED_GROUP GIT_REPOSITORY_INIT_SHARED_ALL = C.GIT_REPOSITORY_INIT_SHARED_ALL +# GIT_ATTR_CHECK_* +GIT_ATTR_CHECK_FILE_THEN_INDEX = C.GIT_ATTR_CHECK_FILE_THEN_INDEX +GIT_ATTR_CHECK_INDEX_THEN_FILE = C.GIT_ATTR_CHECK_INDEX_THEN_FILE +GIT_ATTR_CHECK_INDEX_ONLY = C.GIT_ATTR_CHECK_INDEX_ONLY +GIT_ATTR_CHECK_NO_SYSTEM = C.GIT_ATTR_CHECK_NO_SYSTEM + def init_repository(path, bare=False, flags=GIT_REPOSITORY_INIT_MKPATH, diff --git a/pygit2/decl.h b/pygit2/decl.h index 415dbf407..2ea73f2ff 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -702,3 +702,18 @@ int git_merge_commits(git_index **out, git_repository *repo, const git_commit *o int git_merge_trees(git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *opts); int git_merge_file_from_index(git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts); void git_merge_file_result_free(git_merge_file_result *result); + +#define GIT_ATTR_CHECK_FILE_THEN_INDEX ... +#define GIT_ATTR_CHECK_INDEX_THEN_FILE ... +#define GIT_ATTR_CHECK_INDEX_ONLY ... +#define GIT_ATTR_CHECK_NO_SYSTEM ... + +typedef enum { + GIT_ATTR_UNSPECIFIED_T = 0, + GIT_ATTR_TRUE_T, + GIT_ATTR_FALSE_T, + GIT_ATTR_VALUE_T, +} git_attr_t; + +int git_attr_get(const char **value_out, git_repository *repo, uint32_t flags, const char *path, const char *name); +git_attr_t git_attr_value(const char *attr); diff --git a/pygit2/repository.py b/pygit2/repository.py index 3a2e919ca..c81e45160 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -764,3 +764,41 @@ def ahead_behind(self, local, upstream): check_error(err) return int(ahead[0]), int(behind[0]) + + # + # Git attributes + # + def attr(self, path, name, flags=0): + """Retrieve an attribute for a file by path + + Arguments + + path + The path of the file to look up attributes for, relative to the + workdir root + name + The name of the attribute to look up + flags + A combination of GIT_ATTR_CHECK_ flags which determine the + lookup order + + Returns either a boolean, None (if the value is unspecified) or string + with the value of the attribute. + """ + + cvalue = ffi.new('char **') + err = C.git_attr_get(cvalue, self._repo, flags, to_bytes(path), to_bytes(name)) + check_error(err) + + # Now let's see if we can figure out what the value is + attr_kind = C.git_attr_value(cvalue[0]) + if attr_kind == C.GIT_ATTR_UNSPECIFIED_T: + return None + elif attr_kind == C.GIT_ATTR_TRUE_T: + return True + elif attr_kind == C.GIT_ATTR_FALSE_T: + return False + elif attr_kind == C.GIT_ATTR_VALUE_T: + return ffi.string(cvalue[0]) + + assert False, "the attribute value from libgit2 is invalid" diff --git a/test/test_attributes.py b/test/test_attributes.py new file mode 100644 index 000000000..75bf435dc --- /dev/null +++ b/test/test_attributes.py @@ -0,0 +1,65 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2014 The pygit2 contributors +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, +# as published by the Free Software Foundation. +# +# In addition to the permissions in the GNU General Public License, +# the authors give you unlimited permission to link the compiled +# version of this file into combinations with other programs, +# and to distribute those combinations without any restriction +# coming from the use of this file. (The General Public License +# restrictions do apply in other respects; for example, they cover +# modification of the file, and distribution when not linked into +# a combined executable.) +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +# Import from the future +from __future__ import absolute_import +from __future__ import unicode_literals + +# Import from the Standard Library +import binascii +import unittest +import tempfile +import os +from os.path import join, realpath +import sys + +# Import from pygit2 +from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT +from pygit2 import init_repository, clone_repository, discover_repository +from pygit2 import Oid, Reference, hashfile +import pygit2 +from . import utils + +try: + import __pypy__ +except ImportError: + __pypy__ = None + +class RepositorySignatureTest(utils.RepoTestCase): + + def test_no_attr(self): + self.assertIsNone(self.repo.attr('file', 'foo')) + + with open(join(self.repo.workdir, '.gitattributes'), 'wb+') as f: + f.write('*.py text\n') + f.write('*.jpg -text\n') + f.write('*.sh eol=lf\n') + + self.assertIsNone(self.repo.attr('file.py', 'foo')) + self.assertTrue(self.repo.attr('file.py', 'text')) + self.assertFalse(self.repo.attr('file.jpg', 'text')) + self.assertEqual("lf", self.repo.attr('file.sh', 'eol'))