Skip to content

Commit 0f87129

Browse files
committed
Add FilterList, Repository.load_filter_list
1 parent 7ee1ae5 commit 0f87129

File tree

7 files changed

+106
-0
lines changed

7 files changed

+106
-0
lines changed

pygit2/_libgit2/ffi.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ class GitDescribeOptionsC:
177177
class GitDescribeResultC:
178178
pass
179179

180+
class GitFilterListC:
181+
# opaque struct
182+
pass
183+
180184
class GitIndexC:
181185
pass
182186

@@ -318,6 +322,8 @@ def new(a: Literal['git_signature *']) -> GitSignatureC: ...
318322
@overload
319323
def new(a: Literal['git_signature **']) -> _Pointer[GitSignatureC]: ...
320324
@overload
325+
def new(a: Literal['git_filter_list **']) -> _Pointer[GitFilterListC]: ...
326+
@overload
321327
def new(a: Literal['int *']) -> int_c: ...
322328
@overload
323329
def new(a: Literal['int64_t *']) -> int64_t: ...

pygit2/_run.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
'net.h',
7878
'refspec.h',
7979
'repository.h',
80+
'filter.h',
8081
'commit.h',
8182
'revert.h',
8283
'stash.h',

pygit2/decl/filter.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
typedef enum {
2+
GIT_FILTER_TO_WORKTREE = ...,
3+
GIT_FILTER_TO_ODB = ...,
4+
} git_filter_mode_t;
5+
6+
typedef enum {
7+
GIT_FILTER_DEFAULT = ...,
8+
GIT_FILTER_ALLOW_UNSAFE = ...,
9+
GIT_FILTER_NO_SYSTEM_ATTRIBUTES = ...,
10+
GIT_FILTER_ATTRIBUTES_FROM_HEAD = ...,
11+
GIT_FILTER_ATTRIBUTES_FROM_COMMIT = ...,
12+
} git_filter_flag_t;
13+
14+
int git_filter_list_load(
15+
git_filter_list **filters,
16+
git_repository *repo,
17+
git_blob *blob,
18+
const char *path,
19+
git_filter_mode_t mode,
20+
uint32_t flags);
21+
22+
int git_filter_list_contains(
23+
git_filter_list *filters,
24+
const char *name);
25+
26+
void git_filter_list_free(git_filter_list *filters);

pygit2/decl/types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
typedef struct git_blob git_blob;
12
typedef struct git_commit git_commit;
23
typedef struct git_annotated_commit git_annotated_commit;
34
typedef struct git_config git_config;
5+
typedef struct git_filter_list git_filter_list;
46
typedef struct git_index git_index;
57
typedef struct git_index_conflict_iterator git_index_conflict_iterator;
68
typedef struct git_object git_object;

pygit2/filter.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@
2424
# Boston, MA 02110-1301, USA.
2525

2626
from collections.abc import Callable
27+
from typing import TYPE_CHECKING
2728

2829
from ._pygit2 import FilterSource
30+
from .ffi import C, ffi
31+
from .utils import to_bytes
32+
33+
if TYPE_CHECKING:
34+
from ._libgit2.ffi import GitFilterListC
2935

3036

3137
class Filter:
@@ -107,3 +113,25 @@ def close(self, write_next: Callable[[bytes], None]) -> None:
107113
Any remaining filtered output data must be written to
108114
`write_next` before returning.
109115
"""
116+
117+
118+
class FilterList:
119+
_pointer: GitFilterListC
120+
121+
@classmethod
122+
def _from_c(cls, ptr: GitFilterListC):
123+
if ptr == ffi.NULL:
124+
return None
125+
fl = cls.__new__(cls)
126+
fl._pointer = ptr
127+
return fl
128+
129+
def __contains__(self, name: str) -> bool:
130+
if not isinstance(name, str):
131+
raise TypeError('argument must be str')
132+
c_name = to_bytes(name)
133+
result = C.git_filter_list_contains(self._pointer, c_name)
134+
return bool(result)
135+
136+
def __del__(self):
137+
C.git_filter_list_free(self._pointer)

pygit2/repository.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
DescribeStrategy,
6565
DiffOption,
6666
FileMode,
67+
FilterMode,
6768
MergeFavor,
6869
MergeFileFlag,
6970
MergeFlag,
@@ -73,6 +74,7 @@
7374
)
7475
from .errors import check_error
7576
from .ffi import C, ffi
77+
from .filter import FilterList
7678
from .index import Index, IndexEntry, MergeFileResult
7779
from .packbuilder import PackBuilder
7880
from .references import References
@@ -235,6 +237,31 @@ def hashfile(
235237
oid = Oid(raw=bytes(ffi.buffer(c_oid.id)[:]))
236238
return oid
237239

240+
def load_filter_list(
241+
self, path: str, mode: FilterMode = FilterMode.TO_ODB
242+
) -> FilterList | None:
243+
"""
244+
Load the filter list for a given path.
245+
May return None if there are no filters to apply to this path.
246+
247+
Parameters:
248+
249+
path
250+
Relative path of the file to be filtered
251+
252+
mode
253+
Filtering direction: ODB to worktree (SMUDGE), or worktree to ODB
254+
(CLEAN).
255+
"""
256+
c_filters = ffi.new('git_filter_list **')
257+
c_path = to_bytes(path)
258+
c_mode = int(mode)
259+
260+
err = C.git_filter_list_load(c_filters, self._repo, ffi.NULL, c_path, c_mode, 0)
261+
check_error(err)
262+
fl = FilterList._from_c(c_filters[0])
263+
return fl
264+
238265
def __iter__(self) -> Iterator[Oid]:
239266
return iter(self.odb)
240267

test/test_filter.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,19 @@ def test_filter_cleanup(dirtyrepo: Repository, rot13_filter: Filter) -> None:
136136
# Indirectly test that pygit2_filter_cleanup has the GIL
137137
# before calling pygit2_filter_payload_free.
138138
dirtyrepo.diff()
139+
140+
141+
def test_filterlist_none(testrepo: Repository) -> None:
142+
fl = testrepo.load_filter_list('hello.txt')
143+
assert fl is None
144+
145+
146+
def test_filterlist_crlf(testrepo: Repository) -> None:
147+
testrepo.config['core.autocrlf'] = True
148+
149+
fl = testrepo.load_filter_list('hello.txt')
150+
assert fl is not None
151+
assert 'crlf' in fl
152+
153+
with pytest.raises(TypeError):
154+
1234 in fl # type: ignore

0 commit comments

Comments
 (0)