Skip to content

Commit 47cfc9b

Browse files
KarthikNayakgitster
authored andcommitted
attr: add flag --source to work with tree-ish
The contents of the .gitattributes files may evolve over time, but "git check-attr" always checks attributes against them in the working tree and/or in the index. It may be beneficial to optionally allow the users to check attributes taken from a commit other than HEAD against paths. Add a new flag `--source` which will allow users to check the attributes against a commit (actually any tree-ish would do). When the user uses this flag, we go through the stack of .gitattributes files but instead of checking the current working tree and/or in the index, we check the blobs from the provided tree-ish object. This allows the command to also be used in bare repositories. Since we use a tree-ish object, the user can pass "--source HEAD:subdirectory" and all the attributes will be looked up as if subdirectory was the root directory of the repository. We cannot simply use the `<rev>:<path>` syntax without the `--source` flag, similar to how it is used in `git show` because any non-flag parameter before `--` is treated as an attribute and any parameter after `--` is treated as a pathname. The change involves creating a new function `read_attr_from_blob`, which given the path reads the blob for the path against the provided source and parses the attributes line by line. This function is plugged into `read_attr()` function wherein we go through the stack of attributes files. Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Toon Claes <[email protected]> Co-authored-by: [email protected] Signed-off-by: Junio C Hamano <[email protected]>
1 parent c847e8c commit 47cfc9b

12 files changed

+151
-53
lines changed

Documentation/git-check-attr.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ git-check-attr - Display gitattributes information
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git check-attr' [-a | --all | <attr>...] [--] <pathname>...
13-
'git check-attr' --stdin [-z] [-a | --all | <attr>...]
12+
'git check-attr' [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>...
13+
'git check-attr' --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]
1414

1515
DESCRIPTION
1616
-----------
@@ -36,6 +36,11 @@ OPTIONS
3636
If `--stdin` is also given, input paths are separated
3737
with a NUL character instead of a linefeed character.
3838

39+
--source=<tree-ish>::
40+
Check attributes against the specified tree-ish. It is common to
41+
specify the source tree by naming a commit, branch or tag associated
42+
with it.
43+
3944
\--::
4045
Interpret all preceding arguments as attributes and all following
4146
arguments as path names.

archive.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate,
120120
static struct attr_check *check;
121121
if (!check)
122122
check = attr_check_initl("export-ignore", "export-subst", NULL);
123-
git_check_attr(istate, path, check);
123+
git_check_attr(istate, NULL, path, check);
124124
return check;
125125
}
126126

attr.c

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "dir.h"
1414
#include "utf8.h"
1515
#include "quote.h"
16+
#include "revision.h"
17+
#include "object-store.h"
1618
#include "thread-utils.h"
1719

1820
const char git_attr__true[] = "(builtin)true";
@@ -729,14 +731,62 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
729731
return res;
730732
}
731733

732-
static struct attr_stack *read_attr_from_index(struct index_state *istate,
733-
const char *path,
734-
unsigned flags)
734+
static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
735+
unsigned flags)
735736
{
736737
struct attr_stack *res;
737-
char *buf, *sp;
738+
char *sp;
738739
int lineno = 0;
739740

741+
if (!buf)
742+
return NULL;
743+
744+
CALLOC_ARRAY(res, 1);
745+
for (sp = buf; *sp;) {
746+
char *ep;
747+
int more;
748+
749+
ep = strchrnul(sp, '\n');
750+
more = (*ep == '\n');
751+
*ep = '\0';
752+
handle_attr_line(res, sp, path, ++lineno, flags);
753+
sp = ep + more;
754+
}
755+
free(buf);
756+
757+
return res;
758+
}
759+
760+
static struct attr_stack *read_attr_from_blob(struct index_state *istate,
761+
const struct object_id *tree_oid,
762+
const char *path, unsigned flags)
763+
{
764+
struct object_id oid;
765+
unsigned long sz;
766+
enum object_type type;
767+
void *buf;
768+
unsigned short mode;
769+
770+
if (!tree_oid)
771+
return NULL;
772+
773+
if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode))
774+
return NULL;
775+
776+
buf = repo_read_object_file(istate->repo, &oid, &type, &sz);
777+
if (!buf || type != OBJ_BLOB) {
778+
free(buf);
779+
return NULL;
780+
}
781+
782+
return read_attr_from_buf(buf, path, flags);
783+
}
784+
785+
static struct attr_stack *read_attr_from_index(struct index_state *istate,
786+
const char *path, unsigned flags)
787+
{
788+
char *buf;
789+
740790
if (!istate)
741791
return NULL;
742792

@@ -758,28 +808,19 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
758808
if (!buf)
759809
return NULL;
760810

761-
CALLOC_ARRAY(res, 1);
762-
for (sp = buf; *sp; ) {
763-
char *ep;
764-
int more;
765-
766-
ep = strchrnul(sp, '\n');
767-
more = (*ep == '\n');
768-
*ep = '\0';
769-
handle_attr_line(res, sp, path, ++lineno, flags);
770-
sp = ep + more;
771-
}
772-
free(buf);
773-
return res;
811+
return read_attr_from_buf(buf, path, flags);
774812
}
775813

776814
static struct attr_stack *read_attr(struct index_state *istate,
815+
const struct object_id *tree_oid,
777816
const char *path, unsigned flags)
778817
{
779818
struct attr_stack *res = NULL;
780819

781820
if (direction == GIT_ATTR_INDEX) {
782821
res = read_attr_from_index(istate, path, flags);
822+
} else if (tree_oid) {
823+
res = read_attr_from_blob(istate, tree_oid, path, flags);
783824
} else if (!is_bare_repository()) {
784825
if (direction == GIT_ATTR_CHECKOUT) {
785826
res = read_attr_from_index(istate, path, flags);
@@ -839,6 +880,7 @@ static void push_stack(struct attr_stack **attr_stack_p,
839880
}
840881

841882
static void bootstrap_attr_stack(struct index_state *istate,
883+
const struct object_id *tree_oid,
842884
struct attr_stack **stack)
843885
{
844886
struct attr_stack *e;
@@ -864,7 +906,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
864906
}
865907

866908
/* root directory */
867-
e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
909+
e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
868910
push_stack(stack, e, xstrdup(""), 0);
869911

870912
/* info frame */
@@ -878,6 +920,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
878920
}
879921

880922
static void prepare_attr_stack(struct index_state *istate,
923+
const struct object_id *tree_oid,
881924
const char *path, int dirlen,
882925
struct attr_stack **stack)
883926
{
@@ -899,7 +942,7 @@ static void prepare_attr_stack(struct index_state *istate,
899942
* .gitattributes in deeper directories to shallower ones,
900943
* and finally use the built-in set as the default.
901944
*/
902-
bootstrap_attr_stack(istate, stack);
945+
bootstrap_attr_stack(istate, tree_oid, stack);
903946

904947
/*
905948
* Pop the "info" one that is always at the top of the stack.
@@ -954,7 +997,7 @@ static void prepare_attr_stack(struct index_state *istate,
954997
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
955998
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
956999

957-
next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
1000+
next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW);
9581001

9591002
/* reset the pathbuf to not include "/.gitattributes" */
9601003
strbuf_setlen(&pathbuf, len);
@@ -1074,8 +1117,8 @@ static void determine_macros(struct all_attrs_item *all_attrs,
10741117
* Otherwise all attributes are collected.
10751118
*/
10761119
static void collect_some_attrs(struct index_state *istate,
1077-
const char *path,
1078-
struct attr_check *check)
1120+
const struct object_id *tree_oid,
1121+
const char *path, struct attr_check *check)
10791122
{
10801123
int pathlen, rem, dirlen;
10811124
const char *cp, *last_slash = NULL;
@@ -1094,7 +1137,7 @@ static void collect_some_attrs(struct index_state *istate,
10941137
dirlen = 0;
10951138
}
10961139

1097-
prepare_attr_stack(istate, path, dirlen, &check->stack);
1140+
prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack);
10981141
all_attrs_init(&g_attr_hashmap, check);
10991142
determine_macros(check->all_attrs, check->stack);
11001143

@@ -1103,12 +1146,12 @@ static void collect_some_attrs(struct index_state *istate,
11031146
}
11041147

11051148
void git_check_attr(struct index_state *istate,
1106-
const char *path,
1149+
const struct object_id *tree_oid, const char *path,
11071150
struct attr_check *check)
11081151
{
11091152
int i;
11101153

1111-
collect_some_attrs(istate, path, check);
1154+
collect_some_attrs(istate, tree_oid, path, check);
11121155

11131156
for (i = 0; i < check->nr; i++) {
11141157
size_t n = check->items[i].attr->attr_nr;
@@ -1119,13 +1162,13 @@ void git_check_attr(struct index_state *istate,
11191162
}
11201163
}
11211164

1122-
void git_all_attrs(struct index_state *istate,
1165+
void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
11231166
const char *path, struct attr_check *check)
11241167
{
11251168
int i;
11261169

11271170
attr_check_reset(check);
1128-
collect_some_attrs(istate, path, check);
1171+
collect_some_attrs(istate, tree_oid, path, check);
11291172

11301173
for (i = 0; i < check->all_attrs_nr; i++) {
11311174
const char *name = check->all_attrs[i].attr->name;

attr.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
*/
109109

110110
struct index_state;
111+
struct object_id;
111112

112113
/**
113114
* An attribute is an opaque object that is identified by its name. Pass the
@@ -190,13 +191,14 @@ void attr_check_free(struct attr_check *check);
190191
const char *git_attr_name(const struct git_attr *);
191192

192193
void git_check_attr(struct index_state *istate,
193-
const char *path, struct attr_check *check);
194+
const struct object_id *tree_oid, const char *path,
195+
struct attr_check *check);
194196

195197
/*
196198
* Retrieve all attributes that apply to the specified path.
197199
* check holds the attributes and their values.
198200
*/
199-
void git_all_attrs(struct index_state *istate,
201+
void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
200202
const char *path, struct attr_check *check);
201203

202204
enum git_attr_direction {

builtin/check-attr.c

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
static int all_attrs;
1010
static int cached_attrs;
1111
static int stdin_paths;
12+
static char *source;
1213
static const char * const check_attr_usage[] = {
13-
N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
14-
N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
14+
N_("git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>..."),
15+
N_("git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"),
1516
NULL
1617
};
1718

@@ -23,6 +24,7 @@ static const struct option check_attr_options[] = {
2324
OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
2425
OPT_BOOL('z', NULL, &nul_term_line,
2526
N_("terminate input and output records by a NUL character")),
27+
OPT_STRING(0, "source", &source, N_("<tree-ish>"), N_("which tree-ish to check attributes at")),
2628
OPT_END()
2729
};
2830

@@ -55,27 +57,26 @@ static void output_attr(struct attr_check *check, const char *file)
5557
}
5658
}
5759

58-
static void check_attr(const char *prefix,
59-
struct attr_check *check,
60-
int collect_all,
60+
static void check_attr(const char *prefix, struct attr_check *check,
61+
const struct object_id *tree_oid, int collect_all,
6162
const char *file)
63+
6264
{
6365
char *full_path =
6466
prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
6567

6668
if (collect_all) {
67-
git_all_attrs(&the_index, full_path, check);
69+
git_all_attrs(&the_index, tree_oid, full_path, check);
6870
} else {
69-
git_check_attr(&the_index, full_path, check);
71+
git_check_attr(&the_index, tree_oid, full_path, check);
7072
}
7173
output_attr(check, file);
7274

7375
free(full_path);
7476
}
7577

76-
static void check_attr_stdin_paths(const char *prefix,
77-
struct attr_check *check,
78-
int collect_all)
78+
static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
79+
const struct object_id *tree_oid, int collect_all)
7980
{
8081
struct strbuf buf = STRBUF_INIT;
8182
struct strbuf unquoted = STRBUF_INIT;
@@ -89,7 +90,7 @@ static void check_attr_stdin_paths(const char *prefix,
8990
die("line is badly quoted");
9091
strbuf_swap(&buf, &unquoted);
9192
}
92-
check_attr(prefix, check, collect_all, buf.buf);
93+
check_attr(prefix, check, tree_oid, collect_all, buf.buf);
9394
maybe_flush_or_die(stdout, "attribute to stdout");
9495
}
9596
strbuf_release(&buf);
@@ -105,6 +106,8 @@ static NORETURN void error_with_usage(const char *msg)
105106
int cmd_check_attr(int argc, const char **argv, const char *prefix)
106107
{
107108
struct attr_check *check;
109+
struct object_id *tree_oid = NULL;
110+
struct object_id initialized_oid;
108111
int cnt, i, doubledash, filei;
109112

110113
if (!is_bare_repository())
@@ -176,11 +179,17 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
176179
}
177180
}
178181

182+
if (source) {
183+
if (repo_get_oid_tree(the_repository, source, &initialized_oid))
184+
die("%s: not a valid tree-ish source", source);
185+
tree_oid = &initialized_oid;
186+
}
187+
179188
if (stdin_paths)
180-
check_attr_stdin_paths(prefix, check, all_attrs);
189+
check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
181190
else {
182191
for (i = filei; i < argc; i++)
183-
check_attr(prefix, check, all_attrs, argv[i]);
192+
check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
184193
maybe_flush_or_die(stdout, "attribute to stdout");
185194
}
186195

builtin/pack-objects.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,7 @@ static int no_try_delta(const char *path)
13181318

13191319
if (!check)
13201320
check = attr_check_initl("delta", NULL);
1321-
git_check_attr(the_repository->index, path, check);
1321+
git_check_attr(the_repository->index, NULL, path, check);
13221322
if (ATTR_FALSE(check->items[0].value))
13231323
return 1;
13241324
return 0;

convert.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,7 @@ void convert_attrs(struct index_state *istate,
13081308
git_config(read_convert_config, NULL);
13091309
}
13101310

1311-
git_check_attr(istate, path, check);
1311+
git_check_attr(istate, NULL, path, check);
13121312
ccheck = check->items;
13131313
ca->crlf_action = git_path_check_crlf(ccheck + 4);
13141314
if (ca->crlf_action == CRLF_UNDEFINED)

ll-merge.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
391391
normalize_file(theirs, path, istate);
392392
}
393393

394-
git_check_attr(istate, path, check);
394+
git_check_attr(istate, NULL, path, check);
395395
ll_driver_name = check->items[0].value;
396396
if (check->items[1].value) {
397397
marker_size = atoi(check->items[1].value);
@@ -419,7 +419,7 @@ int ll_merge_marker_size(struct index_state *istate, const char *path)
419419

420420
if (!check)
421421
check = attr_check_initl("conflict-marker-size", NULL);
422-
git_check_attr(istate, path, check);
422+
git_check_attr(istate, NULL, path, check);
423423
if (check->items[0].value) {
424424
marker_size = atoi(check->items[0].value);
425425
if (marker_size <= 0)

pathspec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ int match_pathspec_attrs(struct index_state *istate,
732732
if (name[namelen])
733733
name = to_free = xmemdupz(name, namelen);
734734

735-
git_check_attr(istate, name, item->attr_check);
735+
git_check_attr(istate, NULL, name, item->attr_check);
736736

737737
free(to_free);
738738

0 commit comments

Comments
 (0)