Skip to content

Commit fce010d

Browse files
committed
status: add untracked_files and ignored kwargs
1 parent 73f9efd commit fce010d

6 files changed

Lines changed: 100 additions & 7 deletions

File tree

pygit2/_pygit2.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ class Repository:
496496
def revparse_single(self, revision: str) -> Object: ...
497497
def set_odb(self, odb: Odb) -> None: ...
498498
def set_refdb(self, refdb: Refdb) -> None: ...
499-
def status(self) -> dict[str,int]: ...
499+
def status(self, untracked_files: str = "all", ignored: bool = True) -> dict[str,int]: ...
500500
def status_file(self, path: str) -> int: ...
501501
def walk(self, oid: _OidArg | None, sort_mode: int = GIT_SORT_NONE) -> Walker: ...
502502

src/repository.c

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* Boston, MA 02110-1301, USA.
2626
*/
2727

28+
#include <git2/status.h>
2829
#define PY_SSIZE_T_CLEAN
2930
#include <Python.h>
3031
#include "error.h"
@@ -1771,20 +1772,62 @@ Repository_compress_references(Repository *self)
17711772
}
17721773

17731774
PyDoc_STRVAR(Repository_status__doc__,
1774-
"status() -> dict[str, int]\n"
1775+
"status(untracked_files: str = \"all\", ignored: bool = False) -> dict[str, int]\n"
17751776
"\n"
17761777
"Reads the status of the repository and returns a dictionary with file\n"
1777-
"paths as keys and status flags as values. See pygit2.GIT_STATUS_*.");
1778+
"paths as keys and status flags as values. See pygit2.GIT_STATUS_*.\n"
1779+
"\n"
1780+
"Parameters:\n"
1781+
"\n"
1782+
"untracked_files\n"
1783+
" How to handle untracked files, defaults to \"all\"\n"
1784+
" \"no\": do not return untracked files\n"
1785+
" \"normal\": include untracked files/directories but no dot recurse subdirectories\n"
1786+
" \"all\": include all files in untracked directories\n"
1787+
" Using `untracked_files=\"no\"` or \"normal\"can be faster than \"all\" when the worktree\n"
1788+
"ignored\n"
1789+
" Whether to show ignored files with untracked files. Ignored when untracked_files == \"no\"\n"
1790+
" Defaults to False.\n"
1791+
"contains many untracked files/directories.");
17781792

17791793
PyObject *
1780-
Repository_status(Repository *self)
1794+
Repository_status(Repository *self, PyObject *args, PyObject *kw)
17811795
{
17821796
PyObject *dict;
17831797
int err;
17841798
size_t len, i;
17851799
git_status_list *list;
17861800

1787-
err = git_status_list_new(&list, self->repo, NULL);
1801+
char *untracked_files = "all";
1802+
static char *kwlist[] = {"untracked_files", "ignored", NULL};
1803+
1804+
PyObject* ignored = Py_False;
1805+
1806+
if (!PyArg_ParseTupleAndKeywords(args, kw, "|sO", kwlist,
1807+
&untracked_files, &ignored))
1808+
goto error;
1809+
1810+
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
1811+
opts.flags = GIT_STATUS_OPT_DEFAULTS;
1812+
1813+
if (!strcmp(untracked_files, "no")) {
1814+
opts.flags &= ~(GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS);
1815+
} else if (!strcmp(untracked_files, "normal")){
1816+
opts.flags &= ~GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
1817+
} else if (strcmp(untracked_files, "all") ){
1818+
return PyErr_Format(
1819+
PyExc_ValueError,
1820+
"untracked_files must be one of \"all\", \"normal\" or \"one\"");
1821+
};
1822+
1823+
if (!PyBool_Check(ignored)) {
1824+
return PyErr_Format(PyExc_TypeError, "ignored must be True or False");
1825+
}
1826+
if (!PyObject_IsTrue(ignored)) {
1827+
opts.flags &= ~GIT_STATUS_OPT_INCLUDE_IGNORED;
1828+
}
1829+
1830+
err = git_status_list_new(&list, self->repo, &opts);
17881831
if (err < 0)
17891832
return Error_set(err);
17901833

@@ -2413,7 +2456,7 @@ PyMethodDef Repository_methods[] = {
24132456
METHOD(Repository, revparse_single, METH_O),
24142457
METHOD(Repository, revparse_ext, METH_O),
24152458
METHOD(Repository, revparse, METH_O),
2416-
METHOD(Repository, status, METH_NOARGS),
2459+
METHOD(Repository, status, METH_VARARGS | METH_KEYWORDS),
24172460
METHOD(Repository, status_file, METH_O),
24182461
METHOD(Repository, notes, METH_VARARGS),
24192462
METHOD(Repository, create_note, METH_VARARGS),

src/repository.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ PyObject* Repository_create_reference_direct(Repository *self, PyObject *args, P
6060
PyObject* Repository_create_reference_symbolic(Repository *self, PyObject *args, PyObject* kw);
6161

6262
PyObject* Repository_compress_references(Repository *self);
63-
PyObject* Repository_status(Repository *self);
63+
PyObject* Repository_status(Repository *self, PyObject *args, PyObject *kw);
6464
PyObject* Repository_status_file(Repository *self, PyObject *value);
6565
PyObject* Repository_TreeBuilder(Repository *self, PyObject *args);
6666

test/data/dirtyrepo.zip

1.24 KB
Binary file not shown.

test/test_diff.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
PATCHID = 'f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7'
6565

6666
DIFF_HEAD_TO_INDEX_EXPECTED = [
67+
'.gitignore',
6768
'staged_changes',
6869
'staged_changes_file_deleted',
6970
'staged_changes_file_modified',
@@ -87,6 +88,7 @@
8788
]
8889

8990
DIFF_INDEX_TO_WORK_EXPECTED = [
91+
'.gitignore',
9092
'file_deleted',
9193
'modified_file',
9294
'staged_changes_file_deleted',

test/test_status.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
# along with this program; see the file COPYING. If not, write to
2323
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
2424
# Boston, MA 02110-1301, USA.
25+
import pygit2
26+
import pytest
2527

2628

2729
def test_status(dirtyrepo):
@@ -32,3 +34,49 @@ def test_status(dirtyrepo):
3234
for filepath, status in git_status.items():
3335
assert filepath in git_status
3436
assert status == git_status[filepath]
37+
38+
39+
def test_status_untracked_no(dirtyrepo):
40+
git_status = dirtyrepo.status(untracked_files="no")
41+
not any(status & pygit2.GIT_STATUS_WT_NEW for status in git_status.values())
42+
43+
44+
@pytest.mark.parametrize(
45+
"untracked_files,expected",
46+
[
47+
("no", set()),
48+
(
49+
"normal",
50+
{
51+
"untracked_dir/",
52+
"staged_delete_file_modified",
53+
"subdir/new_file",
54+
"new_file",
55+
},
56+
),
57+
(
58+
"all",
59+
{
60+
"new_file",
61+
"subdir/new_file",
62+
"staged_delete_file_modified",
63+
"untracked_dir/untracked_file",
64+
},
65+
),
66+
],
67+
)
68+
def test_status_untracked_normal(dirtyrepo, untracked_files, expected):
69+
git_status = dirtyrepo.status(untracked_files=untracked_files)
70+
assert {
71+
file for file, status in git_status.items() if status & pygit2.GIT_STATUS_WT_NEW
72+
} == expected
73+
74+
75+
@pytest.mark.parametrize("ignored,expected", [(True, {"ignored"}), (False, set())])
76+
def test_status_ignored(dirtyrepo, ignored, expected):
77+
git_status = dirtyrepo.status(ignored=ignored)
78+
assert {
79+
file
80+
for file, status in git_status.items()
81+
if status & pygit2.GIT_STATUS_IGNORED
82+
} == expected

0 commit comments

Comments
 (0)