Skip to content

Commit 3e85584

Browse files
committed
Merge branch 'jw/builtin-objectmode-attr'
The builtin_objectmode attribute is populated for each path without adding anything in .gitattributes files, which would be useful in magic pathspec, e.g., ":(attr:builtin_objectmode=100755)" to limit to executables. * jw/builtin-objectmode-attr: attr: add builtin objectmode values support
2 parents 99bb88a + 2232a88 commit 3e85584

File tree

5 files changed

+210
-3
lines changed

5 files changed

+210
-3
lines changed

Documentation/gitattributes.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,21 @@ for a path to `Unspecified` state. This can be done by listing
100100
the name of the attribute prefixed with an exclamation point `!`.
101101

102102

103+
RESERVED BUILTIN_* ATTRIBUTES
104+
-----------------------------
105+
106+
builtin_* is a reserved namespace for builtin attribute values. Any
107+
user defined attributes under this namespace will be ignored and
108+
trigger a warning.
109+
110+
`builtin_objectmode`
111+
~~~~~~~~~~~~~~~~~~~~
112+
This attribute is for filtering files by their file bit modes (40000,
113+
120000, 160000, 100755, 100644). e.g. ':(attr:builtin_objectmode=160000)'.
114+
You may also check these values with `git check-attr builtin_objectmode -- <file>`.
115+
If the object is not in the index `git check-attr --cached` will return unspecified.
116+
117+
103118
EFFECTS
104119
-------
105120

attr.c

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "utf8.h"
1818
#include "quote.h"
1919
#include "read-cache-ll.h"
20+
#include "refs.h"
2021
#include "revision.h"
2122
#include "object-store-ll.h"
2223
#include "setup.h"
@@ -183,6 +184,15 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
183184
}
184185
}
185186

187+
/*
188+
* Atribute name cannot begin with "builtin_" which
189+
* is a reserved namespace for built in attributes values.
190+
*/
191+
static int attr_name_reserved(const char *name)
192+
{
193+
return starts_with(name, "builtin_");
194+
}
195+
186196
static int attr_name_valid(const char *name, size_t namelen)
187197
{
188198
/*
@@ -315,7 +325,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
315325
cp++;
316326
len--;
317327
}
318-
if (!attr_name_valid(cp, len)) {
328+
if (!attr_name_valid(cp, len) || attr_name_reserved(cp)) {
319329
report_invalid_attr(cp, len, src, lineno);
320330
return NULL;
321331
}
@@ -379,7 +389,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
379389
name += strlen(ATTRIBUTE_MACRO_PREFIX);
380390
name += strspn(name, blank);
381391
namelen = strcspn(name, blank);
382-
if (!attr_name_valid(name, namelen)) {
392+
if (!attr_name_valid(name, namelen) || attr_name_reserved(name)) {
383393
report_invalid_attr(name, namelen, src, lineno);
384394
goto fail_return;
385395
}
@@ -1240,6 +1250,85 @@ static struct object_id *default_attr_source(void)
12401250
return &attr_source;
12411251
}
12421252

1253+
static const char *interned_mode_string(unsigned int mode)
1254+
{
1255+
static struct {
1256+
unsigned int val;
1257+
char str[7];
1258+
} mode_string[] = {
1259+
{ .val = 0040000 },
1260+
{ .val = 0100644 },
1261+
{ .val = 0100755 },
1262+
{ .val = 0120000 },
1263+
{ .val = 0160000 },
1264+
};
1265+
int i;
1266+
1267+
for (i = 0; i < ARRAY_SIZE(mode_string); i++) {
1268+
if (mode_string[i].val != mode)
1269+
continue;
1270+
if (!*mode_string[i].str)
1271+
snprintf(mode_string[i].str, sizeof(mode_string[i].str),
1272+
"%06o", mode);
1273+
return mode_string[i].str;
1274+
}
1275+
BUG("Unsupported mode 0%o", mode);
1276+
}
1277+
1278+
static const char *builtin_object_mode_attr(struct index_state *istate, const char *path)
1279+
{
1280+
unsigned int mode;
1281+
1282+
if (direction == GIT_ATTR_CHECKIN) {
1283+
struct object_id oid;
1284+
struct stat st;
1285+
if (lstat(path, &st))
1286+
die_errno(_("unable to stat '%s'"), path);
1287+
mode = canon_mode(st.st_mode);
1288+
if (S_ISDIR(mode)) {
1289+
/*
1290+
*`path` is either a directory or it is a submodule,
1291+
* in which case it is already indexed as submodule
1292+
* or it does not exist in the index yet and we need to
1293+
* check if we can resolve to a ref.
1294+
*/
1295+
int pos = index_name_pos(istate, path, strlen(path));
1296+
if (pos >= 0) {
1297+
if (S_ISGITLINK(istate->cache[pos]->ce_mode))
1298+
mode = istate->cache[pos]->ce_mode;
1299+
} else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
1300+
mode = S_IFGITLINK;
1301+
}
1302+
}
1303+
} else {
1304+
/*
1305+
* For GIT_ATTR_CHECKOUT and GIT_ATTR_INDEX we only check
1306+
* for mode in the index.
1307+
*/
1308+
int pos = index_name_pos(istate, path, strlen(path));
1309+
if (pos >= 0)
1310+
mode = istate->cache[pos]->ce_mode;
1311+
else
1312+
return ATTR__UNSET;
1313+
}
1314+
1315+
return interned_mode_string(mode);
1316+
}
1317+
1318+
1319+
static const char *compute_builtin_attr(struct index_state *istate,
1320+
const char *path,
1321+
const struct git_attr *attr) {
1322+
static const struct git_attr *object_mode_attr;
1323+
1324+
if (!object_mode_attr)
1325+
object_mode_attr = git_attr("builtin_objectmode");
1326+
1327+
if (attr == object_mode_attr)
1328+
return builtin_object_mode_attr(istate, path);
1329+
return ATTR__UNSET;
1330+
}
1331+
12431332
void git_check_attr(struct index_state *istate,
12441333
const char *path,
12451334
struct attr_check *check)
@@ -1253,7 +1342,7 @@ void git_check_attr(struct index_state *istate,
12531342
unsigned int n = check->items[i].attr->attr_nr;
12541343
const char *value = check->all_attrs[n].value;
12551344
if (value == ATTR__UNKNOWN)
1256-
value = ATTR__UNSET;
1345+
value = compute_builtin_attr(istate, path, check->all_attrs[n].attr);
12571346
check->items[i].value = value;
12581347
}
12591348
}

neue

Whitespace-only changes.

t/t0003-attributes.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ attr_check () {
1919
test_must_be_empty err
2020
}
2121

22+
attr_check_object_mode_basic () {
23+
path="$1" &&
24+
expect="$2" &&
25+
check_opts="$3" &&
26+
git check-attr $check_opts builtin_objectmode -- "$path" >actual 2>err &&
27+
echo "$path: builtin_objectmode: $expect" >expect &&
28+
test_cmp expect actual
29+
}
30+
31+
attr_check_object_mode () {
32+
attr_check_object_mode_basic "$@" &&
33+
test_must_be_empty err
34+
}
35+
2236
attr_check_quote () {
2337
path="$1" quoted_path="$2" expect="$3" &&
2438

@@ -558,4 +572,66 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
558572
test_cmp expect err
559573
'
560574

575+
test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
576+
>normal &&
577+
attr_check_object_mode normal 100644 &&
578+
mkdir dir &&
579+
attr_check_object_mode dir 040000
580+
'
581+
582+
test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
583+
>exec &&
584+
chmod +x exec &&
585+
attr_check_object_mode exec 100755
586+
'
587+
588+
test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
589+
ln -s to_sym sym &&
590+
attr_check_object_mode sym 120000
591+
'
592+
593+
test_expect_success 'native object mode attributes work with --cached' '
594+
>normal &&
595+
git add normal &&
596+
empty_blob=$(git rev-parse :normal) &&
597+
git update-index --index-info <<-EOF &&
598+
100755 $empty_blob 0 exec
599+
120000 $empty_blob 0 symlink
600+
EOF
601+
attr_check_object_mode normal 100644 --cached &&
602+
attr_check_object_mode exec 100755 --cached &&
603+
attr_check_object_mode symlink 120000 --cached
604+
'
605+
606+
test_expect_success 'check object mode attributes work for submodules' '
607+
mkdir sub &&
608+
(
609+
cd sub &&
610+
git init &&
611+
mv .git .real &&
612+
echo "gitdir: .real" >.git &&
613+
test_commit first
614+
) &&
615+
attr_check_object_mode sub 160000 &&
616+
attr_check_object_mode sub unspecified --cached &&
617+
git add sub &&
618+
attr_check_object_mode sub 160000 --cached
619+
'
620+
621+
test_expect_success 'we do not allow user defined builtin_* attributes' '
622+
echo "foo* builtin_foo" >.gitattributes &&
623+
git add .gitattributes 2>actual &&
624+
echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
625+
test_cmp expect actual
626+
'
627+
628+
test_expect_success 'user defined builtin_objectmode values are ignored' '
629+
echo "foo* builtin_objectmode=12345" >.gitattributes &&
630+
git add .gitattributes &&
631+
>foo_1 &&
632+
attr_check_object_mode_basic foo_1 100644 &&
633+
echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
634+
test_cmp expect err
635+
'
636+
561637
test_done

t/t6135-pathspec-with-attrs.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,31 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
393393
test_cmp expect actual
394394
'
395395

396+
test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
397+
>mode_exec_file_1 &&
398+
399+
git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
400+
echo ?? mode_exec_file_1 >expect &&
401+
test_cmp expect actual &&
402+
403+
git add mode_exec_file_1 &&
404+
chmod +x mode_exec_file_1 &&
405+
git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
406+
echo AM mode_exec_file_1 >expect &&
407+
test_cmp expect actual
408+
'
409+
410+
test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
411+
>mode_1_regular &&
412+
>mode_1_exec &&
413+
chmod +x mode_1_exec &&
414+
git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
415+
echo ?? mode_1_exec >expect &&
416+
test_cmp expect actual &&
417+
418+
git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
419+
echo ?? mode_1_regular >expect &&
420+
test_cmp expect actual
421+
'
422+
396423
test_done

0 commit comments

Comments
 (0)