Skip to content

Commit 577bff3

Browse files
committed
Merge branch 'kn/attr-from-tree'
"git check-attr" learned to take an optional tree-ish to read the .gitattributes file from. * kn/attr-from-tree: attr: add flag `--source` to work with tree-ish t0003: move setup for `--all` into new block
2 parents 8a40af9 + 47cfc9b commit 577bff3

File tree

12 files changed

+156
-55
lines changed

12 files changed

+156
-55
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";
@@ -744,13 +746,61 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
744746
return res;
745747
}
746748

747-
static struct attr_stack *read_attr_from_index(struct index_state *istate,
748-
const char *path,
749-
unsigned flags)
749+
static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
750+
unsigned flags)
750751
{
751752
struct attr_stack *res;
752-
char *buf, *sp;
753+
char *sp;
753754
int lineno = 0;
755+
756+
if (!buf)
757+
return NULL;
758+
759+
CALLOC_ARRAY(res, 1);
760+
for (sp = buf; *sp;) {
761+
char *ep;
762+
int more;
763+
764+
ep = strchrnul(sp, '\n');
765+
more = (*ep == '\n');
766+
*ep = '\0';
767+
handle_attr_line(res, sp, path, ++lineno, flags);
768+
sp = ep + more;
769+
}
770+
free(buf);
771+
772+
return res;
773+
}
774+
775+
static struct attr_stack *read_attr_from_blob(struct index_state *istate,
776+
const struct object_id *tree_oid,
777+
const char *path, unsigned flags)
778+
{
779+
struct object_id oid;
780+
unsigned long sz;
781+
enum object_type type;
782+
void *buf;
783+
unsigned short mode;
784+
785+
if (!tree_oid)
786+
return NULL;
787+
788+
if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode))
789+
return NULL;
790+
791+
buf = repo_read_object_file(istate->repo, &oid, &type, &sz);
792+
if (!buf || type != OBJ_BLOB) {
793+
free(buf);
794+
return NULL;
795+
}
796+
797+
return read_attr_from_buf(buf, path, flags);
798+
}
799+
800+
static struct attr_stack *read_attr_from_index(struct index_state *istate,
801+
const char *path, unsigned flags)
802+
{
803+
char *buf;
754804
unsigned long size;
755805

756806
if (!istate)
@@ -778,28 +828,19 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
778828
return NULL;
779829
}
780830

781-
CALLOC_ARRAY(res, 1);
782-
for (sp = buf; *sp; ) {
783-
char *ep;
784-
int more;
785-
786-
ep = strchrnul(sp, '\n');
787-
more = (*ep == '\n');
788-
*ep = '\0';
789-
handle_attr_line(res, sp, path, ++lineno, flags);
790-
sp = ep + more;
791-
}
792-
free(buf);
793-
return res;
831+
return read_attr_from_buf(buf, path, flags);
794832
}
795833

796834
static struct attr_stack *read_attr(struct index_state *istate,
835+
const struct object_id *tree_oid,
797836
const char *path, unsigned flags)
798837
{
799838
struct attr_stack *res = NULL;
800839

801840
if (direction == GIT_ATTR_INDEX) {
802841
res = read_attr_from_index(istate, path, flags);
842+
} else if (tree_oid) {
843+
res = read_attr_from_blob(istate, tree_oid, path, flags);
803844
} else if (!is_bare_repository()) {
804845
if (direction == GIT_ATTR_CHECKOUT) {
805846
res = read_attr_from_index(istate, path, flags);
@@ -859,6 +900,7 @@ static void push_stack(struct attr_stack **attr_stack_p,
859900
}
860901

861902
static void bootstrap_attr_stack(struct index_state *istate,
903+
const struct object_id *tree_oid,
862904
struct attr_stack **stack)
863905
{
864906
struct attr_stack *e;
@@ -884,7 +926,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
884926
}
885927

886928
/* root directory */
887-
e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
929+
e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
888930
push_stack(stack, e, xstrdup(""), 0);
889931

890932
/* info frame */
@@ -898,6 +940,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
898940
}
899941

900942
static void prepare_attr_stack(struct index_state *istate,
943+
const struct object_id *tree_oid,
901944
const char *path, int dirlen,
902945
struct attr_stack **stack)
903946
{
@@ -919,7 +962,7 @@ static void prepare_attr_stack(struct index_state *istate,
919962
* .gitattributes in deeper directories to shallower ones,
920963
* and finally use the built-in set as the default.
921964
*/
922-
bootstrap_attr_stack(istate, stack);
965+
bootstrap_attr_stack(istate, tree_oid, stack);
923966

924967
/*
925968
* Pop the "info" one that is always at the top of the stack.
@@ -974,7 +1017,7 @@ static void prepare_attr_stack(struct index_state *istate,
9741017
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
9751018
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
9761019

977-
next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
1020+
next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW);
9781021

9791022
/* reset the pathbuf to not include "/.gitattributes" */
9801023
strbuf_setlen(&pathbuf, len);
@@ -1094,8 +1137,8 @@ static void determine_macros(struct all_attrs_item *all_attrs,
10941137
* Otherwise all attributes are collected.
10951138
*/
10961139
static void collect_some_attrs(struct index_state *istate,
1097-
const char *path,
1098-
struct attr_check *check)
1140+
const struct object_id *tree_oid,
1141+
const char *path, struct attr_check *check)
10991142
{
11001143
int pathlen, rem, dirlen;
11011144
const char *cp, *last_slash = NULL;
@@ -1114,7 +1157,7 @@ static void collect_some_attrs(struct index_state *istate,
11141157
dirlen = 0;
11151158
}
11161159

1117-
prepare_attr_stack(istate, path, dirlen, &check->stack);
1160+
prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack);
11181161
all_attrs_init(&g_attr_hashmap, check);
11191162
determine_macros(check->all_attrs, check->stack);
11201163

@@ -1123,12 +1166,12 @@ static void collect_some_attrs(struct index_state *istate,
11231166
}
11241167

11251168
void git_check_attr(struct index_state *istate,
1126-
const char *path,
1169+
const struct object_id *tree_oid, const char *path,
11271170
struct attr_check *check)
11281171
{
11291172
int i;
11301173

1131-
collect_some_attrs(istate, path, check);
1174+
collect_some_attrs(istate, tree_oid, path, check);
11321175

11331176
for (i = 0; i < check->nr; i++) {
11341177
unsigned int n = check->items[i].attr->attr_nr;
@@ -1139,13 +1182,13 @@ void git_check_attr(struct index_state *istate,
11391182
}
11401183
}
11411184

1142-
void git_all_attrs(struct index_state *istate,
1185+
void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
11431186
const char *path, struct attr_check *check)
11441187
{
11451188
int i;
11461189

11471190
attr_check_reset(check);
1148-
collect_some_attrs(istate, path, check);
1191+
collect_some_attrs(istate, tree_oid, path, check);
11491192

11501193
for (i = 0; i < check->all_attrs_nr; i++) {
11511194
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
@@ -120,6 +120,7 @@
120120
#define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
121121

122122
struct index_state;
123+
struct object_id;
123124

124125
/**
125126
* An attribute is an opaque object that is identified by its name. Pass the
@@ -202,13 +203,14 @@ void attr_check_free(struct attr_check *check);
202203
const char *git_attr_name(const struct git_attr *);
203204

204205
void git_check_attr(struct index_state *istate,
205-
const char *path, struct attr_check *check);
206+
const struct object_id *tree_oid, const char *path,
207+
struct attr_check *check);
206208

207209
/*
208210
* Retrieve all attributes that apply to the specified path.
209211
* check holds the attributes and their values.
210212
*/
211-
void git_all_attrs(struct index_state *istate,
213+
void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
212214
const char *path, struct attr_check *check);
213215

214216
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
@@ -730,7 +730,7 @@ int match_pathspec_attrs(struct index_state *istate,
730730
if (name[namelen])
731731
name = to_free = xmemdupz(name, namelen);
732732

733-
git_check_attr(istate, name, item->attr_check);
733+
git_check_attr(istate, NULL, name, item->attr_check);
734734

735735
free(to_free);
736736

0 commit comments

Comments
 (0)