зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/open-dotgitx-with-nofollow'
It does not make sense to make ".gitattributes", ".gitignore" and ".mailmap" symlinks, as they are supposed to be usable from the object store (think: bare repositories where HEAD:.mailmap etc. are used). When these files are symbolic links, we used to read the contents of the files pointed by them by mistake, which has been corrected. * jk/open-dotgitx-with-nofollow: mailmap: do not respect symlinks for in-tree .mailmap exclude: do not respect symlinks for in-tree .gitignore attr: do not respect symlinks for in-tree .gitattributes exclude: add flags parameter to add_patterns() attr: convert "macro_ok" into a flags field add open_nofollow() helper
This commit is contained in:
Коммит
204333b015
60
attr.c
60
attr.c
|
@ -278,6 +278,10 @@ struct match_attr {
|
||||||
|
|
||||||
static const char blank[] = " \t\r\n";
|
static const char blank[] = " \t\r\n";
|
||||||
|
|
||||||
|
/* Flags usable in read_attr() and parse_attr_line() family of functions. */
|
||||||
|
#define READ_ATTR_MACRO_OK (1<<0)
|
||||||
|
#define READ_ATTR_NOFOLLOW (1<<1)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse a whitespace-delimited attribute state (i.e., "attr",
|
* Parse a whitespace-delimited attribute state (i.e., "attr",
|
||||||
* "-attr", "!attr", or "attr=value") from the string starting at src.
|
* "-attr", "!attr", or "attr=value") from the string starting at src.
|
||||||
|
@ -331,7 +335,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct match_attr *parse_attr_line(const char *line, const char *src,
|
static struct match_attr *parse_attr_line(const char *line, const char *src,
|
||||||
int lineno, int macro_ok)
|
int lineno, unsigned flags)
|
||||||
{
|
{
|
||||||
int namelen;
|
int namelen;
|
||||||
int num_attr, i;
|
int num_attr, i;
|
||||||
|
@ -355,7 +359,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
|
||||||
|
|
||||||
if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
|
if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
|
||||||
starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
|
starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
|
||||||
if (!macro_ok) {
|
if (!(flags & READ_ATTR_MACRO_OK)) {
|
||||||
fprintf_ln(stderr, _("%s not allowed: %s:%d"),
|
fprintf_ln(stderr, _("%s not allowed: %s:%d"),
|
||||||
name, src, lineno);
|
name, src, lineno);
|
||||||
goto fail_return;
|
goto fail_return;
|
||||||
|
@ -653,11 +657,11 @@ static void handle_attr_line(struct attr_stack *res,
|
||||||
const char *line,
|
const char *line,
|
||||||
const char *src,
|
const char *src,
|
||||||
int lineno,
|
int lineno,
|
||||||
int macro_ok)
|
unsigned flags)
|
||||||
{
|
{
|
||||||
struct match_attr *a;
|
struct match_attr *a;
|
||||||
|
|
||||||
a = parse_attr_line(line, src, lineno, macro_ok);
|
a = parse_attr_line(line, src, lineno, flags);
|
||||||
if (!a)
|
if (!a)
|
||||||
return;
|
return;
|
||||||
ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc);
|
ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc);
|
||||||
|
@ -672,7 +676,8 @@ static struct attr_stack *read_attr_from_array(const char **list)
|
||||||
|
|
||||||
CALLOC_ARRAY(res, 1);
|
CALLOC_ARRAY(res, 1);
|
||||||
while ((line = *(list++)) != NULL)
|
while ((line = *(list++)) != NULL)
|
||||||
handle_attr_line(res, line, "[builtin]", ++lineno, 1);
|
handle_attr_line(res, line, "[builtin]", ++lineno,
|
||||||
|
READ_ATTR_MACRO_OK);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,21 +703,31 @@ void git_attr_set_direction(enum git_attr_direction new_direction)
|
||||||
direction = new_direction;
|
direction = new_direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
|
static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
|
||||||
{
|
{
|
||||||
FILE *fp = fopen_or_warn(path, "r");
|
int fd;
|
||||||
|
FILE *fp;
|
||||||
struct attr_stack *res;
|
struct attr_stack *res;
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
int lineno = 0;
|
int lineno = 0;
|
||||||
|
|
||||||
if (!fp)
|
if (flags & READ_ATTR_NOFOLLOW)
|
||||||
|
fd = open_nofollow(path, O_RDONLY);
|
||||||
|
else
|
||||||
|
fd = open(path, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
warn_on_fopen_errors(path);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
fp = xfdopen(fd, "r");
|
||||||
|
|
||||||
CALLOC_ARRAY(res, 1);
|
CALLOC_ARRAY(res, 1);
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
char *bufp = buf;
|
char *bufp = buf;
|
||||||
if (!lineno)
|
if (!lineno)
|
||||||
skip_utf8_bom(&bufp, strlen(bufp));
|
skip_utf8_bom(&bufp, strlen(bufp));
|
||||||
handle_attr_line(res, bufp, path, ++lineno, macro_ok);
|
handle_attr_line(res, bufp, path, ++lineno, flags);
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return res;
|
return res;
|
||||||
|
@ -720,7 +735,7 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
|
||||||
|
|
||||||
static struct attr_stack *read_attr_from_index(const struct index_state *istate,
|
static struct attr_stack *read_attr_from_index(const struct index_state *istate,
|
||||||
const char *path,
|
const char *path,
|
||||||
int macro_ok)
|
unsigned flags)
|
||||||
{
|
{
|
||||||
struct attr_stack *res;
|
struct attr_stack *res;
|
||||||
char *buf, *sp;
|
char *buf, *sp;
|
||||||
|
@ -741,7 +756,7 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate,
|
||||||
ep = strchrnul(sp, '\n');
|
ep = strchrnul(sp, '\n');
|
||||||
more = (*ep == '\n');
|
more = (*ep == '\n');
|
||||||
*ep = '\0';
|
*ep = '\0';
|
||||||
handle_attr_line(res, sp, path, ++lineno, macro_ok);
|
handle_attr_line(res, sp, path, ++lineno, flags);
|
||||||
sp = ep + more;
|
sp = ep + more;
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf);
|
||||||
|
@ -749,19 +764,19 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attr_stack *read_attr(const struct index_state *istate,
|
static struct attr_stack *read_attr(const struct index_state *istate,
|
||||||
const char *path, int macro_ok)
|
const char *path, unsigned flags)
|
||||||
{
|
{
|
||||||
struct attr_stack *res = NULL;
|
struct attr_stack *res = NULL;
|
||||||
|
|
||||||
if (direction == GIT_ATTR_INDEX) {
|
if (direction == GIT_ATTR_INDEX) {
|
||||||
res = read_attr_from_index(istate, path, macro_ok);
|
res = read_attr_from_index(istate, path, flags);
|
||||||
} else if (!is_bare_repository()) {
|
} else if (!is_bare_repository()) {
|
||||||
if (direction == GIT_ATTR_CHECKOUT) {
|
if (direction == GIT_ATTR_CHECKOUT) {
|
||||||
res = read_attr_from_index(istate, path, macro_ok);
|
res = read_attr_from_index(istate, path, flags);
|
||||||
if (!res)
|
if (!res)
|
||||||
res = read_attr_from_file(path, macro_ok);
|
res = read_attr_from_file(path, flags);
|
||||||
} else if (direction == GIT_ATTR_CHECKIN) {
|
} else if (direction == GIT_ATTR_CHECKIN) {
|
||||||
res = read_attr_from_file(path, macro_ok);
|
res = read_attr_from_file(path, flags);
|
||||||
if (!res)
|
if (!res)
|
||||||
/*
|
/*
|
||||||
* There is no checked out .gitattributes file
|
* There is no checked out .gitattributes file
|
||||||
|
@ -769,7 +784,7 @@ static struct attr_stack *read_attr(const struct index_state *istate,
|
||||||
* We allow operation in a sparsely checked out
|
* We allow operation in a sparsely checked out
|
||||||
* work tree, so read from it.
|
* work tree, so read from it.
|
||||||
*/
|
*/
|
||||||
res = read_attr_from_index(istate, path, macro_ok);
|
res = read_attr_from_index(istate, path, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,6 +859,7 @@ static void bootstrap_attr_stack(const struct index_state *istate,
|
||||||
struct attr_stack **stack)
|
struct attr_stack **stack)
|
||||||
{
|
{
|
||||||
struct attr_stack *e;
|
struct attr_stack *e;
|
||||||
|
unsigned flags = READ_ATTR_MACRO_OK;
|
||||||
|
|
||||||
if (*stack)
|
if (*stack)
|
||||||
return;
|
return;
|
||||||
|
@ -854,23 +870,23 @@ static void bootstrap_attr_stack(const struct index_state *istate,
|
||||||
|
|
||||||
/* system-wide frame */
|
/* system-wide frame */
|
||||||
if (git_attr_system()) {
|
if (git_attr_system()) {
|
||||||
e = read_attr_from_file(git_etc_gitattributes(), 1);
|
e = read_attr_from_file(git_etc_gitattributes(), flags);
|
||||||
push_stack(stack, e, NULL, 0);
|
push_stack(stack, e, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* home directory */
|
/* home directory */
|
||||||
if (get_home_gitattributes()) {
|
if (get_home_gitattributes()) {
|
||||||
e = read_attr_from_file(get_home_gitattributes(), 1);
|
e = read_attr_from_file(get_home_gitattributes(), flags);
|
||||||
push_stack(stack, e, NULL, 0);
|
push_stack(stack, e, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* root directory */
|
/* root directory */
|
||||||
e = read_attr(istate, GITATTRIBUTES_FILE, 1);
|
e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
|
||||||
push_stack(stack, e, xstrdup(""), 0);
|
push_stack(stack, e, xstrdup(""), 0);
|
||||||
|
|
||||||
/* info frame */
|
/* info frame */
|
||||||
if (startup_info->have_repository)
|
if (startup_info->have_repository)
|
||||||
e = read_attr_from_file(git_path_info_attributes(), 1);
|
e = read_attr_from_file(git_path_info_attributes(), flags);
|
||||||
else
|
else
|
||||||
e = NULL;
|
e = NULL;
|
||||||
if (!e)
|
if (!e)
|
||||||
|
@ -956,7 +972,7 @@ static void prepare_attr_stack(const struct index_state *istate,
|
||||||
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
|
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
|
||||||
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
|
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
|
||||||
|
|
||||||
next = read_attr(istate, pathbuf.buf, 0);
|
next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
|
||||||
|
|
||||||
/* reset the pathbuf to not include "/.gitattributes" */
|
/* reset the pathbuf to not include "/.gitattributes" */
|
||||||
strbuf_setlen(&pathbuf, len);
|
strbuf_setlen(&pathbuf, len);
|
||||||
|
|
|
@ -64,7 +64,7 @@ static int sparse_checkout_list(int argc, const char **argv)
|
||||||
pl.use_cone_patterns = core_sparse_checkout_cone;
|
pl.use_cone_patterns = core_sparse_checkout_cone;
|
||||||
|
|
||||||
sparse_filename = get_sparse_checkout_filename();
|
sparse_filename = get_sparse_checkout_filename();
|
||||||
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
|
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
|
||||||
free(sparse_filename);
|
free(sparse_filename);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -321,7 +321,7 @@ static int sparse_checkout_init(int argc, const char **argv)
|
||||||
memset(&pl, 0, sizeof(pl));
|
memset(&pl, 0, sizeof(pl));
|
||||||
|
|
||||||
sparse_filename = get_sparse_checkout_filename();
|
sparse_filename = get_sparse_checkout_filename();
|
||||||
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
|
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
|
||||||
|
|
||||||
/* If we already have a sparse-checkout file, use it. */
|
/* If we already have a sparse-checkout file, use it. */
|
||||||
if (res >= 0) {
|
if (res >= 0) {
|
||||||
|
@ -483,7 +483,7 @@ static void add_patterns_cone_mode(int argc, const char **argv,
|
||||||
existing.use_cone_patterns = core_sparse_checkout_cone;
|
existing.use_cone_patterns = core_sparse_checkout_cone;
|
||||||
|
|
||||||
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
|
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
|
||||||
&existing, NULL))
|
&existing, NULL, 0))
|
||||||
die(_("unable to load existing sparse-checkout patterns"));
|
die(_("unable to load existing sparse-checkout patterns"));
|
||||||
free(sparse_filename);
|
free(sparse_filename);
|
||||||
|
|
||||||
|
@ -507,7 +507,7 @@ static void add_patterns_literal(int argc, const char **argv,
|
||||||
{
|
{
|
||||||
char *sparse_filename = get_sparse_checkout_filename();
|
char *sparse_filename = get_sparse_checkout_filename();
|
||||||
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
|
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
|
||||||
pl, NULL))
|
pl, NULL, 0))
|
||||||
die(_("unable to load existing sparse-checkout patterns"));
|
die(_("unable to load existing sparse-checkout patterns"));
|
||||||
free(sparse_filename);
|
free(sparse_filename);
|
||||||
add_patterns_from_input(pl, argc, argv);
|
add_patterns_from_input(pl, argc, argv);
|
||||||
|
|
21
dir.c
21
dir.c
|
@ -1035,6 +1035,9 @@ static int add_patterns_from_buffer(char *buf, size_t size,
|
||||||
const char *base, int baselen,
|
const char *base, int baselen,
|
||||||
struct pattern_list *pl);
|
struct pattern_list *pl);
|
||||||
|
|
||||||
|
/* Flags for add_patterns() */
|
||||||
|
#define PATTERN_NOFOLLOW (1<<0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a file with name "fname", read it (either from disk, or from
|
* Given a file with name "fname", read it (either from disk, or from
|
||||||
* an index if 'istate' is non-null), parse it and store the
|
* an index if 'istate' is non-null), parse it and store the
|
||||||
|
@ -1046,7 +1049,7 @@ static int add_patterns_from_buffer(char *buf, size_t size,
|
||||||
*/
|
*/
|
||||||
static int add_patterns(const char *fname, const char *base, int baselen,
|
static int add_patterns(const char *fname, const char *base, int baselen,
|
||||||
struct pattern_list *pl, struct index_state *istate,
|
struct pattern_list *pl, struct index_state *istate,
|
||||||
struct oid_stat *oid_stat)
|
unsigned flags, struct oid_stat *oid_stat)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int r;
|
int r;
|
||||||
|
@ -1054,7 +1057,11 @@ static int add_patterns(const char *fname, const char *base, int baselen,
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
||||||
fd = open(fname, O_RDONLY);
|
if (flags & PATTERN_NOFOLLOW)
|
||||||
|
fd = open_nofollow(fname, O_RDONLY);
|
||||||
|
else
|
||||||
|
fd = open(fname, O_RDONLY);
|
||||||
|
|
||||||
if (fd < 0 || fstat(fd, &st) < 0) {
|
if (fd < 0 || fstat(fd, &st) < 0) {
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
warn_on_fopen_errors(fname);
|
warn_on_fopen_errors(fname);
|
||||||
|
@ -1143,9 +1150,10 @@ static int add_patterns_from_buffer(char *buf, size_t size,
|
||||||
|
|
||||||
int add_patterns_from_file_to_list(const char *fname, const char *base,
|
int add_patterns_from_file_to_list(const char *fname, const char *base,
|
||||||
int baselen, struct pattern_list *pl,
|
int baselen, struct pattern_list *pl,
|
||||||
struct index_state *istate)
|
struct index_state *istate,
|
||||||
|
unsigned flags)
|
||||||
{
|
{
|
||||||
return add_patterns(fname, base, baselen, pl, istate, NULL);
|
return add_patterns(fname, base, baselen, pl, istate, flags, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int add_patterns_from_blob_to_list(
|
int add_patterns_from_blob_to_list(
|
||||||
|
@ -1194,7 +1202,7 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
|
||||||
if (!dir->untracked)
|
if (!dir->untracked)
|
||||||
dir->unmanaged_exclude_files++;
|
dir->unmanaged_exclude_files++;
|
||||||
pl = add_pattern_list(dir, EXC_FILE, fname);
|
pl = add_pattern_list(dir, EXC_FILE, fname);
|
||||||
if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0)
|
if (add_patterns(fname, "", 0, pl, NULL, 0, oid_stat) < 0)
|
||||||
die(_("cannot use %s as an exclude file"), fname);
|
die(_("cannot use %s as an exclude file"), fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1558,6 +1566,7 @@ static void prep_exclude(struct dir_struct *dir,
|
||||||
strbuf_addstr(&sb, dir->exclude_per_dir);
|
strbuf_addstr(&sb, dir->exclude_per_dir);
|
||||||
pl->src = strbuf_detach(&sb, NULL);
|
pl->src = strbuf_detach(&sb, NULL);
|
||||||
add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
|
add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
|
||||||
|
PATTERN_NOFOLLOW,
|
||||||
untracked ? &oid_stat : NULL);
|
untracked ? &oid_stat : NULL);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -3006,7 +3015,7 @@ int get_sparse_checkout_patterns(struct pattern_list *pl)
|
||||||
char *sparse_filename = get_sparse_checkout_filename();
|
char *sparse_filename = get_sparse_checkout_filename();
|
||||||
|
|
||||||
pl->use_cone_patterns = core_sparse_checkout_cone;
|
pl->use_cone_patterns = core_sparse_checkout_cone;
|
||||||
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL);
|
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL, 0);
|
||||||
|
|
||||||
free(sparse_filename);
|
free(sparse_filename);
|
||||||
return res;
|
return res;
|
||||||
|
|
3
dir.h
3
dir.h
|
@ -420,7 +420,8 @@ int hashmap_contains_parent(struct hashmap *map,
|
||||||
struct pattern_list *add_pattern_list(struct dir_struct *dir,
|
struct pattern_list *add_pattern_list(struct dir_struct *dir,
|
||||||
int group_type, const char *src);
|
int group_type, const char *src);
|
||||||
int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
|
int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
|
||||||
struct pattern_list *pl, struct index_state *istate);
|
struct pattern_list *pl, struct index_state *istate,
|
||||||
|
unsigned flags);
|
||||||
void add_patterns_from_file(struct dir_struct *, const char *fname);
|
void add_patterns_from_file(struct dir_struct *, const char *fname);
|
||||||
int add_patterns_from_blob_to_list(struct object_id *oid,
|
int add_patterns_from_blob_to_list(struct object_id *oid,
|
||||||
const char *base, int baselen,
|
const char *base, int baselen,
|
||||||
|
|
|
@ -1242,6 +1242,13 @@ int access_or_die(const char *path, int mode, unsigned flag);
|
||||||
/* Warn on an inaccessible file if errno indicates this is an error */
|
/* Warn on an inaccessible file if errno indicates this is an error */
|
||||||
int warn_on_fopen_errors(const char *path);
|
int warn_on_fopen_errors(const char *path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
|
||||||
|
* may be racy. Do not use this as protection against an attacker who can
|
||||||
|
* simultaneously create paths.
|
||||||
|
*/
|
||||||
|
int open_nofollow(const char *path, int flags);
|
||||||
|
|
||||||
#if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__)
|
#if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__)
|
||||||
#define USE_PARENS_AROUND_GETTEXT_N 1
|
#define USE_PARENS_AROUND_GETTEXT_N 1
|
||||||
#endif
|
#endif
|
||||||
|
|
22
mailmap.c
22
mailmap.c
|
@ -157,20 +157,30 @@ static void read_mailmap_line(struct string_list *map, char *buffer)
|
||||||
add_mapping(map, name1, email1, name2, email2);
|
add_mapping(map, name1, email1, name2, email2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_mailmap_file(struct string_list *map, const char *filename)
|
/* Flags for read_mailmap_file() */
|
||||||
|
#define MAILMAP_NOFOLLOW (1<<0)
|
||||||
|
|
||||||
|
static int read_mailmap_file(struct string_list *map, const char *filename,
|
||||||
|
unsigned flags)
|
||||||
{
|
{
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
int fd;
|
||||||
|
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
f = fopen(filename, "r");
|
if (flags & MAILMAP_NOFOLLOW)
|
||||||
if (!f) {
|
fd = open_nofollow(filename, O_RDONLY);
|
||||||
|
else
|
||||||
|
fd = open(filename, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
return 0;
|
return 0;
|
||||||
return error_errno("unable to open mailmap at %s", filename);
|
return error_errno("unable to open mailmap at %s", filename);
|
||||||
}
|
}
|
||||||
|
f = xfdopen(fd, "r");
|
||||||
|
|
||||||
while (fgets(buffer, sizeof(buffer), f) != NULL)
|
while (fgets(buffer, sizeof(buffer), f) != NULL)
|
||||||
read_mailmap_line(map, buffer);
|
read_mailmap_line(map, buffer);
|
||||||
|
@ -226,10 +236,12 @@ int read_mailmap(struct string_list *map)
|
||||||
git_mailmap_blob = "HEAD:.mailmap";
|
git_mailmap_blob = "HEAD:.mailmap";
|
||||||
|
|
||||||
if (!startup_info->have_repository || !is_bare_repository())
|
if (!startup_info->have_repository || !is_bare_repository())
|
||||||
err |= read_mailmap_file(map, ".mailmap");
|
err |= read_mailmap_file(map, ".mailmap",
|
||||||
|
startup_info->have_repository ?
|
||||||
|
MAILMAP_NOFOLLOW : 0);
|
||||||
if (startup_info->have_repository)
|
if (startup_info->have_repository)
|
||||||
err |= read_mailmap_blob(map, git_mailmap_blob);
|
err |= read_mailmap_blob(map, git_mailmap_blob);
|
||||||
err |= read_mailmap_file(map, git_mailmap_file);
|
err |= read_mailmap_file(map, git_mailmap_file, 0);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,16 @@ test_description=gitattributes
|
||||||
|
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
attr_check () {
|
attr_check_basic () {
|
||||||
path="$1" expect="$2" git_opts="$3" &&
|
path="$1" expect="$2" git_opts="$3" &&
|
||||||
|
|
||||||
git $git_opts check-attr test -- "$path" >actual 2>err &&
|
git $git_opts check-attr test -- "$path" >actual 2>err &&
|
||||||
echo "$path: test: $expect" >expect &&
|
echo "$path: test: $expect" >expect &&
|
||||||
test_cmp expect actual &&
|
test_cmp expect actual
|
||||||
|
}
|
||||||
|
|
||||||
|
attr_check () {
|
||||||
|
attr_check_basic "$@" &&
|
||||||
test_must_be_empty err
|
test_must_be_empty err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +335,6 @@ test_expect_success 'binary macro expanded by -a' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
||||||
test_expect_success 'query binary macro directly' '
|
test_expect_success 'query binary macro directly' '
|
||||||
echo "file binary" >.gitattributes &&
|
echo "file binary" >.gitattributes &&
|
||||||
echo file: binary: set >expect &&
|
echo file: binary: set >expect &&
|
||||||
|
@ -339,4 +342,31 @@ test_expect_success 'query binary macro directly' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'set up symlink tests' '
|
||||||
|
echo "* test" >attr &&
|
||||||
|
rm -f .gitattributes
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks respected in core.attributesFile' '
|
||||||
|
test_when_finished "rm symlink" &&
|
||||||
|
ln -s attr symlink &&
|
||||||
|
test_config core.attributesFile "$(pwd)/symlink" &&
|
||||||
|
attr_check file set
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks respected in info/attributes' '
|
||||||
|
test_when_finished "rm .git/info/attributes" &&
|
||||||
|
ln -s ../../attr .git/info/attributes &&
|
||||||
|
attr_check file set
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks not respected in-tree' '
|
||||||
|
test_when_finished "rm -rf .gitattributes subdir" &&
|
||||||
|
ln -s attr .gitattributes &&
|
||||||
|
mkdir subdir &&
|
||||||
|
ln -s ../attr subdir/.gitattributes &&
|
||||||
|
attr_check_basic subdir/file unspecified &&
|
||||||
|
test_i18ngrep "unable to access.*gitattributes" err
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -865,4 +865,38 @@ test_expect_success 'info/exclude trumps core.excludesfile' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'set up ignore file for symlink tests' '
|
||||||
|
echo "*" >ignore &&
|
||||||
|
rm -f .gitignore .git/info/exclude
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks respected in core.excludesFile' '
|
||||||
|
test_when_finished "rm symlink" &&
|
||||||
|
ln -s ignore symlink &&
|
||||||
|
test_config core.excludesFile "$(pwd)/symlink" &&
|
||||||
|
echo file >expect &&
|
||||||
|
git check-ignore file >actual 2>err &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test_must_be_empty err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks respected in info/exclude' '
|
||||||
|
test_when_finished "rm .git/info/exclude" &&
|
||||||
|
ln -s ../../ignore .git/info/exclude &&
|
||||||
|
echo file >expect &&
|
||||||
|
git check-ignore file >actual 2>err &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test_must_be_empty err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks not respected in-tree' '
|
||||||
|
test_when_finished "rm .gitignore" &&
|
||||||
|
ln -s ignore .gitignore &&
|
||||||
|
mkdir subdir &&
|
||||||
|
ln -s ignore subdir/.gitignore &&
|
||||||
|
test_must_fail git check-ignore subdir/file >actual 2>err &&
|
||||||
|
test_must_be_empty actual &&
|
||||||
|
test_i18ngrep "unable to access.*gitignore" err
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -932,4 +932,35 @@ test_expect_success 'find top-level mailmap from subdir' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'set up symlink tests' '
|
||||||
|
git commit --allow-empty -m foo --author="Orig <orig@example.com>" &&
|
||||||
|
echo "New <new@example.com> <orig@example.com>" >map &&
|
||||||
|
rm -f .mailmap
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks respected in mailmap.file' '
|
||||||
|
test_when_finished "rm symlink" &&
|
||||||
|
ln -s map symlink &&
|
||||||
|
git -c mailmap.file="$(pwd)/symlink" log -1 --format=%aE >actual &&
|
||||||
|
echo "new@example.com" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks respected in non-repo shortlog' '
|
||||||
|
git log -1 >input &&
|
||||||
|
test_when_finished "nongit rm .mailmap" &&
|
||||||
|
nongit ln -sf "$TRASH_DIRECTORY/map" .mailmap &&
|
||||||
|
nongit git shortlog -s <input >actual &&
|
||||||
|
echo " 1 New" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symlinks not respected in-tree' '
|
||||||
|
test_when_finished "rm .mailmap" &&
|
||||||
|
ln -s map .mailmap &&
|
||||||
|
git log -1 --format=%aE >actual &&
|
||||||
|
echo "orig@example.com" >expect&&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
16
wrapper.c
16
wrapper.c
|
@ -678,3 +678,19 @@ int is_empty_or_missing_file(const char *filename)
|
||||||
|
|
||||||
return !st.st_size;
|
return !st.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int open_nofollow(const char *path, int flags)
|
||||||
|
{
|
||||||
|
#ifdef O_NOFOLLOW
|
||||||
|
return open(path, flags | O_NOFOLLOW);
|
||||||
|
#else
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path, &st) < 0)
|
||||||
|
return -1;
|
||||||
|
if (S_ISLNK(st.st_mode)) {
|
||||||
|
errno = ELOOP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return open(path, flags);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче