diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index e848c94397dedc93652e8027df1ad081f08d2d03..2ae771f2fb213a478cf8ed5273829c97ad1392a9 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -288,13 +288,13 @@ for paths. *.tex diff=tex ------------------------ -Then, you would define a "diff.tex.funcname" configuration to +Then, you would define a "diff.tex.xfuncname" configuration to specify a regular expression that matches a line that you would want to appear as the hunk header "TEXT", like this: ------------------------ [diff "tex"] - funcname = "^\\(\\\\\\(sub\\)*section{.*\\)$" + xfuncname = "^(\\\\(sub)*section\\{.*)$" ------------------------ Note. A single level of backslashes are eaten by the diff --git a/builtin-apply.c b/builtin-apply.c index 2ab4aba5a07727fa6368c6acc9b851830a9c24f3..e2c611bf964d8cd589171f74e5ccce5aeb392643 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -13,6 +13,7 @@ #include "delta.h" #include "builtin.h" #include "string-list.h" +#include "dir.h" /* * --check turns on checking that the working tree matches the @@ -2735,15 +2736,7 @@ static void remove_file(struct patch *patch, int rmdir_empty) warning("unable to remove submodule %s", patch->old_name); } else if (!unlink(patch->old_name) && rmdir_empty) { - char *name = xstrdup(patch->old_name); - char *end = strrchr(name, '/'); - while (end) { - *end = 0; - if (rmdir(name)) - break; - end = strrchr(name, '/'); - } - free(name); + remove_path(patch->old_name); } } } diff --git a/builtin-commit.c b/builtin-commit.c index 55e1087d2736a2b28c606664a04c7bc31d763fde..b920257524850ea04bc940411d342e9a622e7094 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -639,7 +639,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix) active_cache_tree = cache_tree(); if (cache_tree_update(active_cache_tree, active_cache, active_nr, 0, 0) < 0) { - error("Error building trees"); + error("Error building trees; the index is unmerged?"); return 0; } diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index e59bd8075e44a8de23503a8a59c5fdd2575a9243..fa6c1ed75285649943e0e62ee0b66ef146dda517 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -320,9 +320,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un static const char *copy_line(const char *buf) { - const char *eol = strchr(buf, '\n'); - if (!eol) // simulate strchrnul() - eol = buf + strlen(buf); + const char *eol = strchrnul(buf, '\n'); return xmemdupz(buf, eol - buf); } diff --git a/builtin-remote.c b/builtin-remote.c index 4cb763f989fc987d94141ed763223cb361cec44a..90a4e35828697f349a38ba3486c40813db32cee0 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -650,10 +650,13 @@ static int get_one_entry(struct remote *remote, void *priv) { struct string_list *list = priv; - string_list_append(remote->name, list)->util = remote->url_nr ? - (void *)remote->url[0] : NULL; - if (remote->url_nr > 1) - warning("Remote %s has more than one URL", remote->name); + if (remote->url_nr > 0) { + int i; + + for (i = 0; i < remote->url_nr; i++) + string_list_append(remote->name, list)->util = (void *)remote->url[i]; + } else + string_list_append(remote->name, list)->util = NULL; return 0; } @@ -669,10 +672,14 @@ static int show_all(void) sort_string_list(&list); for (i = 0; i < list.nr; i++) { struct string_list_item *item = list.items + i; - printf("%s%s%s\n", item->string, - verbose ? "\t" : "", - verbose && item->util ? - (const char *)item->util : ""); + if (verbose) + printf("%s\t%s\n", item->string, + item->util ? (const char *)item->util : ""); + else { + if (i && !strcmp((item - 1)->string, item->string)) + continue; + printf("%s\n", item->string); + } } } return result; diff --git a/builtin-rm.c b/builtin-rm.c index fdac34f2423409add48706497fa01010219baf72..50ae6d54011fad89387f0d56ce7a4591d07874f3 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -29,26 +29,6 @@ static void add_list(const char *name) list.name[list.nr++] = name; } -static int remove_file(const char *name) -{ - int ret; - char *slash; - - ret = unlink(name); - if (ret && errno == ENOENT) - /* The user has removed it from the filesystem by hand */ - ret = errno = 0; - - if (!ret && (slash = strrchr(name, '/'))) { - char *n = xstrdup(name); - do { - n[slash - name] = 0; - name = n; - } while (!rmdir(name) && (slash = strrchr(name, '/'))); - } - return ret; -} - static int check_local_mod(unsigned char *head, int index_only) { /* items in list are already sorted in the cache order, @@ -239,7 +219,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) int removed = 0; for (i = 0; i < list.nr; i++) { const char *path = list.name[i]; - if (!remove_file(path)) { + if (!remove_path(path)) { removed = 1; continue; } diff --git a/diff.c b/diff.c index a2f4850d4fdf3758b75cc086e47e18736075c999..b001d7b5072e71616fb36bed938d57b83e027178 100644 --- a/diff.c +++ b/diff.c @@ -96,32 +96,37 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val * to define a customized regexp to find the beginning of a function to * be used for hunk header lines of "diff -p" style output. */ -static struct funcname_pattern { +struct funcname_pattern_entry { char *name; char *pattern; - struct funcname_pattern *next; + int cflags; +}; +static struct funcname_pattern_list { + struct funcname_pattern_list *next; + struct funcname_pattern_entry e; } *funcname_pattern_list; -static int parse_funcname_pattern(const char *var, const char *ep, const char *value) +static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags) { const char *name; int namelen; - struct funcname_pattern *pp; + struct funcname_pattern_list *pp; name = var + 5; /* "diff." */ namelen = ep - name; for (pp = funcname_pattern_list; pp; pp = pp->next) - if (!strncmp(pp->name, name, namelen) && !pp->name[namelen]) + if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen]) break; if (!pp) { pp = xcalloc(1, sizeof(*pp)); - pp->name = xmemdupz(name, namelen); + pp->e.name = xmemdupz(name, namelen); pp->next = funcname_pattern_list; funcname_pattern_list = pp; } - free(pp->pattern); - pp->pattern = xstrdup(value); + free(pp->e.pattern); + pp->e.pattern = xstrdup(value); + pp->e.cflags = cflags; return 0; } @@ -194,7 +199,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) if (!strcmp(ep, ".funcname")) { if (!value) return config_error_nonbool(var); - return parse_funcname_pattern(var, ep, value); + return parse_funcname_pattern(var, ep, value, + 0); + } else if (!strcmp(ep, ".xfuncname")) { + if (!value) + return config_error_nonbool(var); + return parse_funcname_pattern(var, ep, value, + REG_EXTENDED); } } } @@ -1400,42 +1411,45 @@ int diff_filespec_is_binary(struct diff_filespec *one) return one->is_binary; } -static const char *funcname_pattern(const char *ident) +static const struct funcname_pattern_entry *funcname_pattern(const char *ident) { - struct funcname_pattern *pp; + struct funcname_pattern_list *pp; for (pp = funcname_pattern_list; pp; pp = pp->next) - if (!strcmp(ident, pp->name)) - return pp->pattern; + if (!strcmp(ident, pp->e.name)) + return &pp->e; return NULL; } -static struct builtin_funcname_pattern { - const char *name; - const char *pattern; -} builtin_funcname_pattern[] = { - { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, - { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" }, - { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" - "new\\|return\\|switch\\|throw\\|while\\)\n" - "^[ ]*\\(\\([ ]*" - "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*\\)$" }, - { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|" - "destructor\\|interface\\|implementation\\|" - "initialization\\|finalization\\)[ \t]*.*\\)$" - "\\|" - "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" - }, - { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)" }, - { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" }, - { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, - { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, +static const struct funcname_pattern_entry builtin_funcname_pattern[] = { + { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + REG_EXTENDED }, + { "html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", REG_EXTENDED }, + { "java", + "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n" + "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$", + REG_EXTENDED }, + { "pascal", + "^((procedure|function|constructor|destructor|interface|" + "implementation|initialization|finalization)[ \t]*.*)$" + "\n" + "^(.*=[ \t]*(class|record).*)$", + REG_EXTENDED }, + { "php", "^[\t ]*((function|class).*)", REG_EXTENDED }, + { "python", "^[ \t]*((class|def)[ \t].*)$", REG_EXTENDED }, + { "ruby", "^[ \t]*((class|module|def)[ \t].*)$", + REG_EXTENDED }, + { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + REG_EXTENDED }, + { "tex", + "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", + REG_EXTENDED }, }; -static const char *diff_funcname_pattern(struct diff_filespec *one) +static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one) { - const char *ident, *pattern; + const char *ident; + const struct funcname_pattern_entry *pe; int i; diff_filespec_check_attr(one); @@ -1450,9 +1464,9 @@ static const char *diff_funcname_pattern(struct diff_filespec *one) return funcname_pattern("default"); /* Look up custom "funcname.$ident" regexp from config. */ - pattern = funcname_pattern(ident); - if (pattern) - return pattern; + pe = funcname_pattern(ident); + if (pe) + return pe; /* * And define built-in fallback patterns here. Note that @@ -1460,7 +1474,7 @@ static const char *diff_funcname_pattern(struct diff_filespec *one) */ for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++) if (!strcmp(ident, builtin_funcname_pattern[i].name)) - return builtin_funcname_pattern[i].pattern; + return &builtin_funcname_pattern[i]; return NULL; } @@ -1556,11 +1570,11 @@ static void builtin_diff(const char *name_a, xdemitconf_t xecfg; xdemitcb_t ecb; struct emit_callback ecbdata; - const char *funcname_pattern; + const struct funcname_pattern_entry *pe; - funcname_pattern = diff_funcname_pattern(one); - if (!funcname_pattern) - funcname_pattern = diff_funcname_pattern(two); + pe = diff_funcname_pattern(one); + if (!pe) + pe = diff_funcname_pattern(two); memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); @@ -1572,8 +1586,8 @@ static void builtin_diff(const char *name_a, xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; - if (funcname_pattern) - xdiff_set_find_func(&xecfg, funcname_pattern); + if (pe) + xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); if (!diffopts) ; else if (!prefixcmp(diffopts, "--unified=")) diff --git a/dir.c b/dir.c index acf1001c4f9dd51587e3b07e52d4101ff3f1cb66..1c3c5015249e6ac0ed0461b49f6581aebc8701a3 100644 --- a/dir.c +++ b/dir.c @@ -831,3 +831,23 @@ void setup_standard_excludes(struct dir_struct *dir) if (excludes_file && !access(excludes_file, R_OK)) add_excludes_from_file(dir, excludes_file); } + +int remove_path(const char *name) +{ + char *slash; + + if (unlink(name) && errno != ENOENT) + return -1; + + slash = strrchr(name, '/'); + if (slash) { + char *dirs = xstrdup(name); + slash = dirs + (slash - name); + do { + *slash = '\0'; + } while (rmdir(dirs) && (slash = strrchr(dirs, '/'))); + free(dirs); + } + return 0; +} + diff --git a/dir.h b/dir.h index 2df15defb6720a742282f24721233c4816deceb6..278ee42295ed3724801d56eb65c86b29002486aa 100644 --- a/dir.h +++ b/dir.h @@ -81,4 +81,7 @@ extern int is_inside_dir(const char *dir); extern void setup_standard_excludes(struct dir_struct *dir); extern int remove_dir_recursively(struct strbuf *path, int only_empty); +/* tries to remove the path with empty directories along it, ignores ENOENT */ +extern int remove_path(const char *path); + #endif diff --git a/git-stash.sh b/git-stash.sh index 6bd2572f7745cbd35fb59c3449b84f1929d9ab08..42f626f9d555f26f3e50441c0ab27de5cac19bf4 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -161,7 +161,7 @@ show_stash () { apply_stash () { git update-index -q --refresh && git diff-files --quiet --ignore-submodules || - die 'Cannot restore on top of a dirty state' + die 'Cannot apply to a dirty working tree, please stage your changes' unstash_index= case "$1" in diff --git a/git-submodule.sh b/git-submodule.sh index 92be0feb5840c055061f680a45eef6c8f4ebda2d..65178ae8e32bb20c65c42eb9668fb0ffec749007 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -194,7 +194,7 @@ cmd_add() else module_clone "$path" "$realrepo" || exit - (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) || + (unset GIT_DIR; cd "$path" && git checkout -f -q ${branch:+-b "$branch" "origin/$branch"}) || die "Unable to checkout submodule '$path'" fi @@ -340,8 +340,13 @@ cmd_update() if test "$subsha1" != "$sha1" then + force= + if test -z "$subsha1" + then + force="-f" + fi (unset GIT_DIR; cd "$path" && git-fetch && - git-checkout -q "$sha1") || + git-checkout $force -q "$sha1") || die "Unable to checkout '$sha1' in submodule path '$path'" say "Submodule path '$path': checked out '$sha1'" diff --git a/merge-recursive.c b/merge-recursive.c index 08917314fb18a2c5af71cf96c410d2c8363a2650..6bc3eac85c46700b1ed7ab93bce6ecf859fa13f9 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -19,6 +19,7 @@ #include "interpolate.h" #include "attr.h" #include "merge-recursive.h" +#include "dir.h" static struct tree *shift_tree_object(struct tree *one, struct tree *two) { @@ -392,22 +393,6 @@ static int update_stages(const char *path, struct diff_filespec *o, return 0; } -static int remove_path(const char *name) -{ - char *slash, *dirs; - - if (unlink(name)) - return -1; - dirs = xstrdup(name); - while ((slash = strrchr(name, '/'))) { - *slash = '\0'; - if (rmdir(name) != 0) - break; - } - free(dirs); - return 0; -} - static int remove_file(struct merge_options *o, int clean, const char *path, int no_wd) { diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 18bcd9713d4e7a1446639dece4b8bc0173c57b22..520e095c597a61f0f263a274a53ae0aa2781da85 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -32,7 +32,18 @@ EOF sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java +builtin_patterns="bibtex html java pascal php python ruby tex" +for p in $builtin_patterns +do + test_expect_success "builtin $p pattern compiles" ' + echo "*.java diff=$p" > .gitattributes && + ! ( git diff --no-index Beer.java Beer-correct.java 2>&1 | + grep "fatal" > /dev/null ) + ' +done + test_expect_success 'default behaviour' ' + rm -f .gitattributes && git diff --no-index Beer.java Beer-correct.java | grep "^@@.*@@ public class Beer" ' @@ -58,7 +69,7 @@ test_expect_success 'last regexp must not be negated' ' ' test_expect_success 'alternation in pattern' ' - git config diff.java.funcname "^[ ]*\\(\\(public\\|static\\).*\\)$" + git config diff.java.xfuncname "^[ ]*((public|static).*)$" && git diff --no-index Beer.java Beer-correct.java | grep "^@@.*@@ public static void main(" ' diff --git a/xdiff-interface.c b/xdiff-interface.c index 8457601bc417129803c298df57d5d12c6e317096..8bab82ed7fd1fcfea4dd13f6d020476758a18031 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -194,31 +194,34 @@ static long ff_regexp(const char *line, long len, char *line_buffer = xstrndup(line, len); /* make NUL terminated */ struct ff_regs *regs = priv; regmatch_t pmatch[2]; - int result = 0, i; + int i; + int result = -1; for (i = 0; i < regs->nr; i++) { struct ff_reg *reg = regs->array + i; - if (reg->negate ^ !!regexec(®->re, - line_buffer, 2, pmatch, 0)) { - free(line_buffer); - return -1; + if (!regexec(®->re, line_buffer, 2, pmatch, 0)) { + if (reg->negate) + goto fail; + break; } } + if (regs->nr <= i) + goto fail; i = pmatch[1].rm_so >= 0 ? 1 : 0; line += pmatch[i].rm_so; result = pmatch[i].rm_eo - pmatch[i].rm_so; if (result > buffer_size) result = buffer_size; else - while (result > 0 && (isspace(line[result - 1]) || - line[result - 1] == '\n')) + while (result > 0 && (isspace(line[result - 1]))) result--; memcpy(buffer, line, result); + fail: free(line_buffer); return result; } -void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value) +void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags) { int i; struct ff_regs *regs; @@ -243,7 +246,7 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value) expression = buffer = xstrndup(value, ep - value); else expression = value; - if (regcomp(®->re, expression, 0)) + if (regcomp(®->re, expression, cflags)) die("Invalid regexp to look for hunk header: %s", expression); free(buffer); value = ep + 1; diff --git a/xdiff-interface.h b/xdiff-interface.h index b3b5c933bfc3aac2d6c0b59aa72bb18e0b1f7557..eaf9cd34981695fc8d12fd35d01ceae93b22254d 100644 --- a/xdiff-interface.h +++ b/xdiff-interface.h @@ -16,7 +16,7 @@ int parse_hunk_header(char *line, int len, int read_mmfile(mmfile_t *ptr, const char *filename); int buffer_is_binary(const char *ptr, unsigned long size); -extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line); +extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags); extern int git_xmerge_config(const char *var, const char *value, void *cb); extern int git_xmerge_style;