Skip to content

Commit 2232a88

Browse files
jowang4105gitster
authored andcommitted
attr: add builtin objectmode values support
Gives all paths builtin objectmode values based on the paths' modes (one of 100644, 100755, 120000, 040000, 160000). Users may use this feature to filter by file types. For example a pathspec such as ':(attr:builtin_objectmode=160000)' could filter for submodules without needing to have `builtin_objectmode=160000` to be set in .gitattributes for every submodule path. These values are also reflected in `git check-attr` results. If the git_attr_direction is set to GIT_ATTR_INDEX or GIT_ATTR_CHECKIN and a path is not found in the index, the value will be unspecified. This patch also reserves the builtin_* attribute namespace for objectmode and any future builtin attributes. Any user defined attributes using this reserved namespace will result in a warning. This is a breaking change for any existing builtin_* attributes. Pathspecs with some builtin_* attribute name (excluding builtin_objectmode) will behave like any attribute where there are no user specified values. Signed-off-by: Joanna Wang <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cfb8a6e commit 2232a88

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
@@ -295,4 +295,31 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
295295
test_cmp expect actual
296296
'
297297

298+
test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
299+
>mode_exec_file_1 &&
300+
301+
git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
302+
echo ?? mode_exec_file_1 >expect &&
303+
test_cmp expect actual &&
304+
305+
git add mode_exec_file_1 &&
306+
chmod +x mode_exec_file_1 &&
307+
git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
308+
echo AM mode_exec_file_1 >expect &&
309+
test_cmp expect actual
310+
'
311+
312+
test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
313+
>mode_1_regular &&
314+
>mode_1_exec &&
315+
chmod +x mode_1_exec &&
316+
git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
317+
echo ?? mode_1_exec >expect &&
318+
test_cmp expect actual &&
319+
320+
git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
321+
echo ?? mode_1_regular >expect &&
322+
test_cmp expect actual
323+
'
324+
298325
test_done

0 commit comments

Comments
 (0)