mirror of
https://github.com/git/git
synced 2024-11-04 16:17:49 +00:00
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
This commit is contained in:
commit
3e8558438d
5 changed files with 210 additions and 3 deletions
|
@ -100,6 +100,21 @@ for a path to `Unspecified` state. This can be done by listing
|
|||
the name of the attribute prefixed with an exclamation point `!`.
|
||||
|
||||
|
||||
RESERVED BUILTIN_* ATTRIBUTES
|
||||
-----------------------------
|
||||
|
||||
builtin_* is a reserved namespace for builtin attribute values. Any
|
||||
user defined attributes under this namespace will be ignored and
|
||||
trigger a warning.
|
||||
|
||||
`builtin_objectmode`
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
This attribute is for filtering files by their file bit modes (40000,
|
||||
120000, 160000, 100755, 100644). e.g. ':(attr:builtin_objectmode=160000)'.
|
||||
You may also check these values with `git check-attr builtin_objectmode -- <file>`.
|
||||
If the object is not in the index `git check-attr --cached` will return unspecified.
|
||||
|
||||
|
||||
EFFECTS
|
||||
-------
|
||||
|
||||
|
|
95
attr.c
95
attr.c
|
@ -17,6 +17,7 @@
|
|||
#include "utf8.h"
|
||||
#include "quote.h"
|
||||
#include "read-cache-ll.h"
|
||||
#include "refs.h"
|
||||
#include "revision.h"
|
||||
#include "object-store-ll.h"
|
||||
#include "setup.h"
|
||||
|
@ -183,6 +184,15 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Atribute name cannot begin with "builtin_" which
|
||||
* is a reserved namespace for built in attributes values.
|
||||
*/
|
||||
static int attr_name_reserved(const char *name)
|
||||
{
|
||||
return starts_with(name, "builtin_");
|
||||
}
|
||||
|
||||
static int attr_name_valid(const char *name, size_t namelen)
|
||||
{
|
||||
/*
|
||||
|
@ -315,7 +325,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
|
|||
cp++;
|
||||
len--;
|
||||
}
|
||||
if (!attr_name_valid(cp, len)) {
|
||||
if (!attr_name_valid(cp, len) || attr_name_reserved(cp)) {
|
||||
report_invalid_attr(cp, len, src, lineno);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -379,7 +389,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
|
|||
name += strlen(ATTRIBUTE_MACRO_PREFIX);
|
||||
name += strspn(name, blank);
|
||||
namelen = strcspn(name, blank);
|
||||
if (!attr_name_valid(name, namelen)) {
|
||||
if (!attr_name_valid(name, namelen) || attr_name_reserved(name)) {
|
||||
report_invalid_attr(name, namelen, src, lineno);
|
||||
goto fail_return;
|
||||
}
|
||||
|
@ -1240,6 +1250,85 @@ static struct object_id *default_attr_source(void)
|
|||
return &attr_source;
|
||||
}
|
||||
|
||||
static const char *interned_mode_string(unsigned int mode)
|
||||
{
|
||||
static struct {
|
||||
unsigned int val;
|
||||
char str[7];
|
||||
} mode_string[] = {
|
||||
{ .val = 0040000 },
|
||||
{ .val = 0100644 },
|
||||
{ .val = 0100755 },
|
||||
{ .val = 0120000 },
|
||||
{ .val = 0160000 },
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mode_string); i++) {
|
||||
if (mode_string[i].val != mode)
|
||||
continue;
|
||||
if (!*mode_string[i].str)
|
||||
snprintf(mode_string[i].str, sizeof(mode_string[i].str),
|
||||
"%06o", mode);
|
||||
return mode_string[i].str;
|
||||
}
|
||||
BUG("Unsupported mode 0%o", mode);
|
||||
}
|
||||
|
||||
static const char *builtin_object_mode_attr(struct index_state *istate, const char *path)
|
||||
{
|
||||
unsigned int mode;
|
||||
|
||||
if (direction == GIT_ATTR_CHECKIN) {
|
||||
struct object_id oid;
|
||||
struct stat st;
|
||||
if (lstat(path, &st))
|
||||
die_errno(_("unable to stat '%s'"), path);
|
||||
mode = canon_mode(st.st_mode);
|
||||
if (S_ISDIR(mode)) {
|
||||
/*
|
||||
*`path` is either a directory or it is a submodule,
|
||||
* in which case it is already indexed as submodule
|
||||
* or it does not exist in the index yet and we need to
|
||||
* check if we can resolve to a ref.
|
||||
*/
|
||||
int pos = index_name_pos(istate, path, strlen(path));
|
||||
if (pos >= 0) {
|
||||
if (S_ISGITLINK(istate->cache[pos]->ce_mode))
|
||||
mode = istate->cache[pos]->ce_mode;
|
||||
} else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
|
||||
mode = S_IFGITLINK;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* For GIT_ATTR_CHECKOUT and GIT_ATTR_INDEX we only check
|
||||
* for mode in the index.
|
||||
*/
|
||||
int pos = index_name_pos(istate, path, strlen(path));
|
||||
if (pos >= 0)
|
||||
mode = istate->cache[pos]->ce_mode;
|
||||
else
|
||||
return ATTR__UNSET;
|
||||
}
|
||||
|
||||
return interned_mode_string(mode);
|
||||
}
|
||||
|
||||
|
||||
static const char *compute_builtin_attr(struct index_state *istate,
|
||||
const char *path,
|
||||
const struct git_attr *attr) {
|
||||
static const struct git_attr *object_mode_attr;
|
||||
|
||||
if (!object_mode_attr)
|
||||
object_mode_attr = git_attr("builtin_objectmode");
|
||||
|
||||
if (attr == object_mode_attr)
|
||||
return builtin_object_mode_attr(istate, path);
|
||||
return ATTR__UNSET;
|
||||
}
|
||||
|
||||
void git_check_attr(struct index_state *istate,
|
||||
const char *path,
|
||||
struct attr_check *check)
|
||||
|
@ -1253,7 +1342,7 @@ void git_check_attr(struct index_state *istate,
|
|||
unsigned int n = check->items[i].attr->attr_nr;
|
||||
const char *value = check->all_attrs[n].value;
|
||||
if (value == ATTR__UNKNOWN)
|
||||
value = ATTR__UNSET;
|
||||
value = compute_builtin_attr(istate, path, check->all_attrs[n].attr);
|
||||
check->items[i].value = value;
|
||||
}
|
||||
}
|
||||
|
|
0
neue
Normal file
0
neue
Normal file
|
@ -19,6 +19,20 @@ attr_check () {
|
|||
test_must_be_empty err
|
||||
}
|
||||
|
||||
attr_check_object_mode_basic () {
|
||||
path="$1" &&
|
||||
expect="$2" &&
|
||||
check_opts="$3" &&
|
||||
git check-attr $check_opts builtin_objectmode -- "$path" >actual 2>err &&
|
||||
echo "$path: builtin_objectmode: $expect" >expect &&
|
||||
test_cmp expect actual
|
||||
}
|
||||
|
||||
attr_check_object_mode () {
|
||||
attr_check_object_mode_basic "$@" &&
|
||||
test_must_be_empty err
|
||||
}
|
||||
|
||||
attr_check_quote () {
|
||||
path="$1" quoted_path="$2" expect="$3" &&
|
||||
|
||||
|
@ -558,4 +572,66 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
|
|||
test_cmp expect err
|
||||
'
|
||||
|
||||
test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
|
||||
>normal &&
|
||||
attr_check_object_mode normal 100644 &&
|
||||
mkdir dir &&
|
||||
attr_check_object_mode dir 040000
|
||||
'
|
||||
|
||||
test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
|
||||
>exec &&
|
||||
chmod +x exec &&
|
||||
attr_check_object_mode exec 100755
|
||||
'
|
||||
|
||||
test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
|
||||
ln -s to_sym sym &&
|
||||
attr_check_object_mode sym 120000
|
||||
'
|
||||
|
||||
test_expect_success 'native object mode attributes work with --cached' '
|
||||
>normal &&
|
||||
git add normal &&
|
||||
empty_blob=$(git rev-parse :normal) &&
|
||||
git update-index --index-info <<-EOF &&
|
||||
100755 $empty_blob 0 exec
|
||||
120000 $empty_blob 0 symlink
|
||||
EOF
|
||||
attr_check_object_mode normal 100644 --cached &&
|
||||
attr_check_object_mode exec 100755 --cached &&
|
||||
attr_check_object_mode symlink 120000 --cached
|
||||
'
|
||||
|
||||
test_expect_success 'check object mode attributes work for submodules' '
|
||||
mkdir sub &&
|
||||
(
|
||||
cd sub &&
|
||||
git init &&
|
||||
mv .git .real &&
|
||||
echo "gitdir: .real" >.git &&
|
||||
test_commit first
|
||||
) &&
|
||||
attr_check_object_mode sub 160000 &&
|
||||
attr_check_object_mode sub unspecified --cached &&
|
||||
git add sub &&
|
||||
attr_check_object_mode sub 160000 --cached
|
||||
'
|
||||
|
||||
test_expect_success 'we do not allow user defined builtin_* attributes' '
|
||||
echo "foo* builtin_foo" >.gitattributes &&
|
||||
git add .gitattributes 2>actual &&
|
||||
echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'user defined builtin_objectmode values are ignored' '
|
||||
echo "foo* builtin_objectmode=12345" >.gitattributes &&
|
||||
git add .gitattributes &&
|
||||
>foo_1 &&
|
||||
attr_check_object_mode_basic foo_1 100644 &&
|
||||
echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
|
||||
test_cmp expect err
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -393,4 +393,31 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
|
||||
>mode_exec_file_1 &&
|
||||
|
||||
git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
|
||||
echo ?? mode_exec_file_1 >expect &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git add mode_exec_file_1 &&
|
||||
chmod +x mode_exec_file_1 &&
|
||||
git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
|
||||
echo AM mode_exec_file_1 >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
|
||||
>mode_1_regular &&
|
||||
>mode_1_exec &&
|
||||
chmod +x mode_1_exec &&
|
||||
git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
|
||||
echo ?? mode_1_exec >expect &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
|
||||
echo ?? mode_1_regular >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Loading…
Reference in a new issue