Merge branch 'bc/master-diff-hunk-header-fix'
authorShawn O. Pearce <spearce@spearce.org>
Mon, 29 Sep 2008 18:04:20 +0000 (11:04 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Mon, 29 Sep 2008 18:04:20 +0000 (11:04 -0700)
* bc/master-diff-hunk-header-fix:
  Clarify commit error message for unmerged files
  Use strchrnul() instead of strchr() plus manual workaround
  Use remove_path from dir.c instead of own implementation
  Add remove_path: a function to remove as much as possible of a path
  git-submodule: Fix "Unable to checkout" for the initial 'update'
  Clarify how the user can satisfy stash's 'dirty state' check.
  t4018-diff-funcname: test syntax of builtin xfuncname patterns
  t4018-diff-funcname: test syntax of builtin xfuncname patterns
  make "git remote" report multiple URLs
  diff hunk pattern: fix misconverted "\{" tex macro introducers
  diff: fix "multiple regexp" semantics to find hunk header comment
  diff: use extended regexp to find hunk headers
  diff: use extended regexp to find hunk headers
  diff.*.xfuncname which uses "extended" regex's for hunk header selection
  diff.c: associate a flag with each pattern and use it for compiling regex
  diff.c: return pattern entry pointer rather than just the hunk header pattern

Conflicts:
builtin-merge-recursive.c
t/t7201-co.sh
xdiff-interface.h

15 files changed:
Documentation/gitattributes.txt
builtin-apply.c
builtin-commit.c
builtin-for-each-ref.c
builtin-remote.c
builtin-rm.c
diff.c
dir.c
dir.h
git-stash.sh
git-submodule.sh
merge-recursive.c
t/t4018-diff-funcname.sh
xdiff-interface.c
xdiff-interface.h

index e848c94..2ae771f 100644 (file)
@@ -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
index 2ab4aba..e2c611b 100644 (file)
@@ -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);
                }
        }
 }
index 55e1087..b920257 100644 (file)
@@ -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;
        }
 
index e59bd80..fa6c1ed 100644 (file)
@@ -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);
 }
 
index 4cb763f..90a4e35 100644 (file)
@@ -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;
index fdac34f..50ae6d5 100644 (file)
@@ -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 a2f4850..b001d7b 100644 (file)
--- 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 acf1001..1c3c501 100644 (file)
--- 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 2df15de..278ee42 100644 (file)
--- 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
index 6bd2572..42f626f 100755 (executable)
@@ -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
index 92be0fe..65178ae 100755 (executable)
@@ -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'"
index 0891731..6bc3eac 100644 (file)
@@ -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)
 {
index 18bcd97..520e095 100755 (executable)
@@ -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("
 '
index 8457601..8bab82e 100644 (file)
@@ -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(&reg->re,
-                                       line_buffer, 2, pmatch, 0)) {
-                       free(line_buffer);
-                       return -1;
+               if (!regexec(&reg->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(&reg->re, expression, 0))
+               if (regcomp(&reg->re, expression, cflags))
                        die("Invalid regexp to look for hunk header: %s", expression);
                free(buffer);
                value = ep + 1;
index b3b5c93..eaf9cd3 100644 (file)
@@ -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;