From d0bfd026a8241d544c339944976927b388d61a5e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 12 Apr 2007 01:07:32 -0700 Subject: Add basic infrastructure to assign attributes to paths This adds the basic infrastructure to assign attributes to paths, in a way similar to what the exclusion mechanism does based on $GIT_DIR/info/exclude and .gitignore files. An attribute is just a simple string that does not contain any whitespace. They can be specified in $GIT_DIR/info/attributes file, and .gitattributes file in each directory. Each line in these files defines a pattern matching rule. Similar to the exclusion mechanism, a later match overrides an earlier match in the same file, and entries from .gitattributes file in the same directory takes precedence over the ones from parent directories. Lines in $GIT_DIR/info/attributes file are used as the lowest precedence default rules. A line is either a comment (an empty line, or a line that begins with a '#'), or a rule, which is a whitespace separated list of tokens. The first token on the line is a shell glob pattern. The rest are names of attributes, each of which can optionally be prefixed with '!'. Such a line means "if a path matches this glob, this attribute is set (or unset -- if the attribute name is prefixed with '!'). For glob matching, the same "if the pattern does not have a slash in it, the basename of the path is matched with fnmatch(3) against the pattern, otherwise, the path is matched with the pattern with FNM_PATHNAME" rule as the exclusion mechanism is used. This does not define what an attribute means. Tying an attribute to various effects it has on git operation for paths that have it will be specified separately. Signed-off-by: Junio C Hamano --- cache.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'cache.h') diff --git a/cache.h b/cache.h index b1bd9e46c2..63af43fe5c 100644 --- a/cache.h +++ b/cache.h @@ -151,6 +151,8 @@ enum object_type { #define CONFIG_ENVIRONMENT "GIT_CONFIG" #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL" #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH" +#define GITATTRIBUTES_FILE ".gitattributes" +#define INFOATTRIBUTES_FILE "info/attributes" extern int is_bare_repository_cfg; extern int is_bare_repository(void); -- cgit v1.3 From f48fd68887a03756658a46486a5dd1301c5a655f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 14 Apr 2007 08:54:37 -0700 Subject: attribute macro support This adds "attribute macros" (for lack of better name). So far, we have low-level attributes such as crlf and diff, which are defined in operational terms --- setting or unsetting them on a particular path directly affects what is done to the path. For example, in order to decline diffs or crlf conversions on a binary blob, no diffs on PostScript files, and treat all other files normally, you would have something like these: * diff crlf *.ps !diff proprietary.o !diff !crlf That is fine as the operation goes, but gets unwieldy rather rapidly, when we start adding more low-level attributes that are defined in operational terms. A near-term example of such an attribute would be 'merge-3way' which would control if git should attempt the usual 3-way file-level merge internally, or leave merging to a specialized external program of user's choice. When it is added, we do _not_ want to force the users to update the above to: * diff crlf merge-3way *.ps !diff proprietary.o !diff !crlf !merge-3way The way this patch solves this issue is to realize that the attributes the user is assigning to paths are not defined in terms of operations but in terms of what they are. All of the three low-level attributes usually make sense for most of the files that sane SCM users have git operate on (these files are typically called "text'). Only a few cases, such as binary blob, need exception to decline the "usual treatment given to text files" -- and people mark them as "binary". So this allows the $GIT_DIR/info/alternates and .gitattributes at the toplevel of the project to also specify attributes that assigns other attributes. The syntax is '[attr]' followed by an attribute name followed by a list of attribute names: [attr] binary !diff !crlf !merge-3way When "binary" attribute is set to a path, if the path has not got diff/crlf/merge-3way attribute set or unset by other rules, this rule unsets the three low-level attributes. It is expected that the user level .gitattributes will be expressed mostly in terms of attributes based on what the files are, and the above sample would become like this: (built-in attribute configuration) [attr] binary !diff !crlf !merge-3way * diff crlf merge-3way (project specific .gitattributes) proprietary.o binary (user preference $GIT_DIR/info/attributes) *.ps !diff There are a few caveats. * As described above, you can define these macros only in $GIT_DIR/info/attributes and toplevel .gitattributes. * There is no attempt to detect circular definition of macro attributes, and definitions are evaluated from bottom to top as usual to fill in other attributes that have not yet got values. The following would work as expected: [attr] text diff crlf [attr] ps text !diff *.ps ps while this would most likely not (I haven't tried): [attr] ps text !diff [attr] text diff crlf *.ps ps * When a macro says "[attr] A B !C", saying that a path does not have attribute A does not let you tell anything about attributes B or C. That is, given this: [attr] text diff crlf [attr] ps text !diff *.txt !ps path hello.txt, which would match "*.txt" pattern, would have "ps" attribute set to zero, but that does not make text attribute of hello.txt set to false (nor diff attribute set to true). Signed-off-by: Junio C Hamano --- attr.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++----------------- cache.h | 1 + 2 files changed, 132 insertions(+), 48 deletions(-) (limited to 'cache.h') diff --git a/attr.c b/attr.c index 7435d927a9..a306144204 100644 --- a/attr.c +++ b/attr.c @@ -16,9 +16,12 @@ struct git_attr { struct git_attr *next; unsigned h; + int attr_nr; char name[FLEX_ARRAY]; }; +static int attr_nr; +static struct git_attr_check *check_all_attr; static struct git_attr *(git_attr_hash[HASHSIZE]); static unsigned hash_name(const char *name, int namelen) @@ -50,7 +53,12 @@ struct git_attr *git_attr(const char *name, int len) a->name[len] = 0; a->h = hval; a->next = git_attr_hash[pos]; + a->attr_nr = attr_nr++; git_attr_hash[pos] = a; + + check_all_attr = xrealloc(check_all_attr, + sizeof(*check_all_attr) * attr_nr); + check_all_attr[a->attr_nr].attr = a; return a; } @@ -69,26 +77,46 @@ struct attr_state { }; struct match_attr { - char *pattern; + union { + char *pattern; + struct git_attr *attr; + } u; + char is_macro; unsigned num_attr; struct attr_state state[FLEX_ARRAY]; }; static const char blank[] = " \t\r\n"; -static struct match_attr *parse_attr_line(const char *line) +static struct match_attr *parse_attr_line(const char *line, const char *src, + int lineno, int macro_ok) { int namelen; int num_attr; const char *cp, *name; struct match_attr *res = res; int pass; + int is_macro; cp = line + strspn(line, blank); if (!*cp || *cp == '#') return NULL; name = cp; namelen = strcspn(name, blank); + if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && + !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) { + if (!macro_ok) { + fprintf(stderr, "%s not allowed: %s:%d\n", + name, src, lineno); + return NULL; + } + is_macro = 1; + name += strlen(ATTRIBUTE_MACRO_PREFIX); + name += strspn(name, blank); + namelen = strcspn(name, blank); + } + else + is_macro = 0; for (pass = 0; pass < 2; pass++) { /* pass 0 counts and allocates, pass 1 fills */ @@ -113,13 +141,19 @@ static struct match_attr *parse_attr_line(const char *line) } if (pass) break; + res = xcalloc(1, sizeof(*res) + sizeof(struct attr_state) * num_attr + - namelen + 1); - res->pattern = (char*)&(res->state[num_attr]); - memcpy(res->pattern, name, namelen); - res->pattern[namelen] = 0; + (is_macro ? 0 : namelen + 1)); + if (is_macro) + res->u.attr = git_attr(name, namelen); + else { + res->u.pattern = (char*)&(res->state[num_attr]); + memcpy(res->u.pattern, name, namelen); + res->u.pattern[namelen] = 0; + } + res->is_macro = is_macro; res->num_attr = num_attr; } return res; @@ -167,10 +201,13 @@ static struct attr_stack *read_attr_from_array(const char **list) { struct attr_stack *res; const char *line; + int lineno = 0; res = xcalloc(1, sizeof(*res)); while ((line = *(list++)) != NULL) { - struct match_attr *a = parse_attr_line(line); + struct match_attr *a; + + a = parse_attr_line(line, "[builtin]", ++lineno, 1); if (!a) continue; res->attrs = xrealloc(res->attrs, res->num_matches + 1); @@ -179,11 +216,12 @@ static struct attr_stack *read_attr_from_array(const char **list) return res; } -static struct attr_stack *read_attr_from_file(const char *path) +static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { FILE *fp; struct attr_stack *res; char buf[2048]; + int lineno = 0; res = xcalloc(1, sizeof(*res)); fp = fopen(path, "r"); @@ -191,7 +229,9 @@ static struct attr_stack *read_attr_from_file(const char *path) return res; while (fgets(buf, sizeof(buf), fp)) { - struct match_attr *a = parse_attr_line(buf); + struct match_attr *a; + + a = parse_attr_line(buf, path, ++lineno, macro_ok); if (!a) continue; res->attrs = xrealloc(res->attrs, res->num_matches + 1); @@ -206,13 +246,42 @@ static void debug_info(const char *what, struct attr_stack *elem) { fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()"); } +static void debug_set(const char *what, const char *match, struct git_attr *attr, int set) +{ + fprintf(stderr, "%s: %s => %d (%s)\n", + what, attr->name, set, match); +} #define debug_push(a) debug_info("push", (a)) #define debug_pop(a) debug_info("pop", (a)) #else #define debug_push(a) do { ; } while (0) #define debug_pop(a) do { ; } while (0) +#define debug_set(a,b,c,d) do { ; } while (0) #endif +static void bootstrap_attr_stack(void) +{ + if (!attr_stack) { + struct attr_stack *elem; + + elem = read_attr_from_array(builtin_attr); + elem->origin = NULL; + elem->prev = attr_stack; + attr_stack = elem; + + elem = read_attr_from_file(GITATTRIBUTES_FILE, 1); + elem->origin = strdup(""); + elem->prev = attr_stack; + attr_stack = elem; + debug_push(elem); + + elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1); + elem->origin = NULL; + elem->prev = attr_stack; + attr_stack = elem; + } +} + static void prepare_attr_stack(const char *path, int dirlen) { struct attr_stack *elem, *info; @@ -232,23 +301,8 @@ static void prepare_attr_stack(const char *path, int dirlen) * .gitattributes in deeper directories to shallower ones, * and finally use the built-in set as the default. */ - if (!attr_stack) { - elem = read_attr_from_array(builtin_attr); - elem->origin = NULL; - elem->prev = attr_stack; - attr_stack = elem; - - elem = read_attr_from_file(GITATTRIBUTES_FILE); - elem->origin = strdup(""); - elem->prev = attr_stack; - attr_stack = elem; - debug_push(elem); - - elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE)); - elem->origin = NULL; - elem->prev = attr_stack; - attr_stack = elem; - } + if (!attr_stack) + bootstrap_attr_stack(); /* * Pop the "info" one that is always at the top of the stack. @@ -286,7 +340,7 @@ static void prepare_attr_stack(const char *path, int dirlen) memcpy(pathbuf + dirlen, "/", 2); cp = strchr(pathbuf + len + 1, '/'); strcpy(cp + 1, GITATTRIBUTES_FILE); - elem = read_attr_from_file(pathbuf); + elem = read_attr_from_file(pathbuf, 0); *cp = '\0'; elem->origin = strdup(pathbuf); elem->prev = attr_stack; @@ -324,31 +378,26 @@ static int path_matches(const char *pathname, int pathlen, return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; } -/* - * I do not like this at all. Only because we allow individual - * attribute to be set or unset incrementally by individual - * lines in .gitattribute files, we need to do this triple - * loop which looks quite wasteful. - */ -static int fill(const char *path, int pathlen, - struct attr_stack *stk, struct git_attr_check *check, - int num, int rem) +static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem) { - int i, j, k; const char *base = stk->origin ? stk->origin : ""; + int i, j; + struct git_attr_check *check = check_all_attr; for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { struct match_attr *a = stk->attrs[i]; + if (a->is_macro) + continue; if (path_matches(path, pathlen, - a->pattern, base, strlen(base))) { - for (j = 0; j < a->num_attr; j++) { + a->u.pattern, base, strlen(base))) { + for (j = 0; 0 < rem && j < a->num_attr; j++) { struct git_attr *attr = a->state[j].attr; int set = !a->state[j].unset; - for (k = 0; k < num; k++) { - if (0 <= check[k].isset || - check[k].attr != attr) - continue; - check[k].isset = set; + int *n = &(check[attr->attr_nr].isset); + + if (*n < 0) { + debug_set("fill", a->u.pattern, attr, set); + *n = set; rem--; } } @@ -357,14 +406,41 @@ static int fill(const char *path, int pathlen, return rem; } +static int macroexpand(struct attr_stack *stk, int rem) +{ + int i, j; + struct git_attr_check *check = check_all_attr; + + for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { + struct match_attr *a = stk->attrs[i]; + if (!a->is_macro) + continue; + if (check[a->u.attr->attr_nr].isset < 0) + continue; + for (j = 0; 0 < rem && j < a->num_attr; j++) { + struct git_attr *attr = a->state[j].attr; + int set = !a->state[j].unset; + int *n = &(check[attr->attr_nr].isset); + + if (*n < 0) { + debug_set("expand", a->u.attr->name, attr, set); + *n = set; + rem--; + } + } + } + return rem; +} + int git_checkattr(const char *path, int num, struct git_attr_check *check) { struct attr_stack *stk; const char *cp; int dirlen, pathlen, i, rem; - for (i = 0; i < num; i++) - check[i].isset = -1; + bootstrap_attr_stack(); + for (i = 0; i < attr_nr; i++) + check_all_attr[i].isset = -1; pathlen = strlen(path); cp = strrchr(path, '/'); @@ -373,8 +449,15 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) else dirlen = cp - path; prepare_attr_stack(path, dirlen); - rem = num; + rem = attr_nr; + for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) + rem = fill(path, pathlen, stk, rem); + for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) - rem = fill(path, pathlen, stk, check, num, rem); + rem = macroexpand(stk, rem); + + for (i = 0; i < num; i++) + check[i].isset = check_all_attr[check[i].attr->attr_nr].isset; + return 0; } diff --git a/cache.h b/cache.h index 63af43fe5c..38ad00661d 100644 --- a/cache.h +++ b/cache.h @@ -153,6 +153,7 @@ enum object_type { #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" +#define ATTRIBUTE_MACRO_PREFIX "[attr]" extern int is_bare_repository_cfg; extern int is_bare_repository(void); -- cgit v1.3 From ac78e548049f4e86b38368d2c4b4dbb546c64ac6 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 19 Apr 2007 02:05:03 +0200 Subject: Simplify calling of CR/LF conversion routines Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-apply.c | 18 ++++++--------- cache.h | 4 ++-- convert.c | 71 ++++++++++++++++++++++++++++----------------------------- diff.c | 4 ++-- entry.c | 7 ++---- sha1_file.c | 7 +++--- 6 files changed, 51 insertions(+), 60 deletions(-) (limited to 'cache.h') diff --git a/builtin-apply.c b/builtin-apply.c index fd92ef7174..ccd342c1c4 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1475,8 +1475,8 @@ static int read_old_data(struct stat *st, const char *path, char **buf_p, unsign } close(fd); nsize = got; - nbuf = buf; - if (convert_to_git(path, &nbuf, &nsize)) { + nbuf = convert_to_git(path, buf, &nsize); + if (nbuf) { free(buf); *buf_p = nbuf; *alloc_p = nsize; @@ -2355,9 +2355,8 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { - int fd, converted; + int fd; char *nbuf; - unsigned long nsize; if (has_symlinks && S_ISLNK(mode)) /* Although buf:size is counted string, it also is NUL @@ -2369,13 +2368,10 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, if (fd < 0) return -1; - nsize = size; - nbuf = (char *) buf; - converted = convert_to_working_tree(path, &nbuf, &nsize); - if (converted) { + nbuf = convert_to_working_tree(path, buf, &size); + if (nbuf) buf = nbuf; - size = nsize; - } + while (size) { int written = xwrite(fd, buf, size); if (written < 0) @@ -2387,7 +2383,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, } if (close(fd) < 0) die("closing file %s: %s", path, strerror(errno)); - if (converted) + if (nbuf) free(nbuf); return 0; } diff --git a/cache.h b/cache.h index 38ad00661d..8c804cb6ee 100644 --- a/cache.h +++ b/cache.h @@ -496,8 +496,8 @@ extern void trace_printf(const char *format, ...); extern void trace_argv_printf(const char **argv, int count, const char *format, ...); /* convert.c */ -extern int convert_to_git(const char *path, char **bufp, unsigned long *sizep); -extern int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep); +extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep); +extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep); /* match-trees.c */ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int); diff --git a/convert.c b/convert.c index da64253a16..742b895cfa 100644 --- a/convert.c +++ b/convert.c @@ -79,25 +79,24 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } -static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int action) +static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action) { - char *buffer, *nbuf; + char *buffer, *dst; unsigned long size, nsize; struct text_stat stats; if ((action == CRLF_BINARY) || (action == CRLF_GUESS && !auto_crlf)) - return 0; + return NULL; size = *sizep; if (!size) - return 0; - buffer = *bufp; + return NULL; - gather_stats(buffer, size, &stats); + gather_stats(src, size, &stats); /* No CR? Nothing to convert, regardless. */ if (!stats.cr) - return 0; + return NULL; if (action == CRLF_GUESS) { /* @@ -106,13 +105,13 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int * stuff? */ if (stats.cr != stats.crlf) - return 0; + return NULL; /* * And add some heuristics for binary vs text, of course... */ if (is_binary(size, &stats)) - return 0; + return NULL; } /* @@ -120,10 +119,10 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int * to let the caller know that we switched buffers on it. */ nsize = size - stats.crlf; - nbuf = xmalloc(nsize); - *bufp = nbuf; + buffer = xmalloc(nsize); *sizep = nsize; + dst = buffer; if (action == CRLF_GUESS) { /* * If we guessed, we already know we rejected a file with @@ -131,54 +130,53 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int * follow it. */ do { - unsigned char c = *buffer++; + unsigned char c = *src++; if (c != '\r') - *nbuf++ = c; + *dst++ = c; } while (--size); } else { do { - unsigned char c = *buffer++; + unsigned char c = *src++; if (! (c == '\r' && (1 < size && *buffer == '\n'))) - *nbuf++ = c; + *dst++ = c; } while (--size); } - return 1; + return buffer; } -static int crlf_to_worktree(const char *path, char **bufp, unsigned long *sizep, int action) +static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action) { - char *buffer, *nbuf; + char *buffer, *dst; unsigned long size, nsize; struct text_stat stats; unsigned char last; if ((action == CRLF_BINARY) || (action == CRLF_INPUT) || (action == CRLF_GUESS && auto_crlf <= 0)) - return 0; + return NULL; size = *sizep; if (!size) - return 0; - buffer = *bufp; + return NULL; - gather_stats(buffer, size, &stats); + gather_stats(src, size, &stats); /* No LF? Nothing to convert, regardless. */ if (!stats.lf) - return 0; + return NULL; /* Was it already in CRLF format? */ if (stats.lf == stats.crlf) - return 0; + return NULL; if (action == CRLF_GUESS) { /* If we have any bare CR characters, we're not going to touch it */ if (stats.cr != stats.crlf) - return 0; + return NULL; if (is_binary(size, &stats)) - return 0; + return NULL; } /* @@ -186,19 +184,20 @@ static int crlf_to_worktree(const char *path, char **bufp, unsigned long *sizep, * to let the caller know that we switched buffers on it. */ nsize = size + stats.lf - stats.crlf; - nbuf = xmalloc(nsize); - *bufp = nbuf; + buffer = xmalloc(nsize); *sizep = nsize; last = 0; + + dst = buffer; do { - unsigned char c = *buffer++; + unsigned char c = *src++; if (c == '\n' && last != '\r') - *nbuf++ = '\r'; - *nbuf++ = c; + *dst++ = '\r'; + *dst++ = c; last = c; } while (--size); - return 1; + return buffer; } static void setup_crlf_check(struct git_attr_check *check) @@ -231,12 +230,12 @@ static int git_path_check_crlf(const char *path) return CRLF_GUESS; } -int convert_to_git(const char *path, char **bufp, unsigned long *sizep) +char *convert_to_git(const char *path, const char *src, unsigned long *sizep) { - return crlf_to_git(path, bufp, sizep, git_path_check_crlf(path)); + return crlf_to_git(path, src, sizep, git_path_check_crlf(path)); } -int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep) +char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep) { - return crlf_to_worktree(path, bufp, sizep, git_path_check_crlf(path)); + return crlf_to_worktree(path, src, sizep, git_path_check_crlf(path)); } diff --git a/diff.c b/diff.c index 5f501864e6..1cb1230a99 100644 --- a/diff.c +++ b/diff.c @@ -1493,9 +1493,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) /* * Convert from working tree format to canonical git format */ - buf = s->data; size = s->size; - if (convert_to_git(s->path, &buf, &size)) { + buf = convert_to_git(s->path, s->data, &size); + if (buf) { munmap(s->data, s->size); s->should_munmap = 0; s->data = buf; diff --git a/entry.c b/entry.c index d72f811580..3771209f19 100644 --- a/entry.c +++ b/entry.c @@ -79,7 +79,6 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat } switch (ntohl(ce->ce_mode) & S_IFMT) { char *buf; - unsigned long nsize; case S_IFREG: if (to_tempfile) { @@ -96,12 +95,10 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat /* * Convert from git internal format to working tree format */ - buf = new; - nsize = size; - if (convert_to_working_tree(ce->name, &buf, &nsize)) { + buf = convert_to_working_tree(ce->name, new, &size); + if (buf) { free(new); new = buf; - size = nsize; } wrote = write_in_full(fd, new, size); diff --git a/sha1_file.c b/sha1_file.c index 4304fe9bbc..1978d5f14e 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2277,10 +2277,9 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, */ if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) { unsigned long nsize = size; - char *nbuf = buf; - if (convert_to_git(path, &nbuf, &nsize)) { - if (size) - munmap(buf, size); + char *nbuf = convert_to_git(path, buf, &nsize); + if (nbuf) { + munmap(buf, size); size = nsize; buf = nbuf; re_allocated = 1; -- cgit v1.3 From 5e635e396020cc08bc21a3e67c20c5294d6d13fd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 21 Apr 2007 03:11:10 -0700 Subject: lockfile: record the primary process. The usual process flow is the main process opens and holds the lock to the index, does its thing, perhaps spawning children during the course, and then writes the resulting index out by releaseing the lock. However, the lockfile interface uses atexit(3) to clean it up, without regard to who actually created the lock. This typically leads to a confusing behaviour of lock being released too early when the child exits, and then the parent process when it calls commit_lockfile() finds that it cannot unlock it. This fixes the problem by recording who created and holds the lock, and upon atexit(3) handler, child simply ignores the lockfile the parent created. Signed-off-by: Junio C Hamano --- cache.h | 1 + lockfile.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'cache.h') diff --git a/cache.h b/cache.h index 8c804cb6ee..faddaf6504 100644 --- a/cache.h +++ b/cache.h @@ -209,6 +209,7 @@ extern int refresh_cache(unsigned int flags); struct lock_file { struct lock_file *next; + pid_t owner; char on_list; char filename[PATH_MAX]; }; diff --git a/lockfile.c b/lockfile.c index bed6b21daf..23db35aff2 100644 --- a/lockfile.c +++ b/lockfile.c @@ -8,8 +8,11 @@ static const char *alternate_index_output; static void remove_lock_file(void) { + pid_t me = getpid(); + while (lock_file_list) { - if (lock_file_list->filename[0]) + if (lock_file_list->owner == me && + lock_file_list->filename[0]) unlink(lock_file_list->filename); lock_file_list = lock_file_list->next; } @@ -28,6 +31,7 @@ static int lock_file(struct lock_file *lk, const char *path) sprintf(lk->filename, "%s.lock", path); fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= fd) { + lk->owner = getpid(); if (!lk->on_list) { lk->next = lock_file_list; lock_file_list = lk; -- cgit v1.3