Code

Merge branch 'cw/ws-indent-with-tab'
authorJunio C Hamano <gitster@pobox.com>
Sun, 9 May 2010 05:35:35 +0000 (22:35 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 9 May 2010 05:35:35 +0000 (22:35 -0700)
* cw/ws-indent-with-tab:
  whitespace: tests for git-apply --whitespace=fix with tab-in-indent
  whitespace: add tab-in-indent support for --whitespace=fix
  whitespace: replumb ws_fix_copy to take a strbuf *dst instead of char *dst
  whitespace: tests for git-diff --check with tab-in-indent error class
  whitespace: add tab-in-indent error class
  whitespace: we cannot "catch all errors known to git" anymore

Documentation/config.txt
builtin/apply.c
cache.h
t/t4015-diff-whitespace.sh
t/t4124-apply-ws-rule.sh
ws.c

index 8f86050b05f5643240cfd760492079dbbcb73c76..6308026bf649612cc9d88ee6077b945d3a161d0b 100644 (file)
@@ -481,6 +481,8 @@ core.whitespace::
   error (enabled by default).
 * `indent-with-non-tab` treats a line that is indented with 8 or more
   space characters as an error (not enabled by default).
+* `tab-in-indent` treats a tab character in the initial indent part of
+  the line as an error (not enabled by default).
 * `blank-at-eof` treats blank lines added at the end of file as an error
   (enabled by default).
 * `trailing-space` is a short-hand to cover both `blank-at-eol` and
index 660cf92538ce125a83d566f3ba2bb7f2c24f5671..d56cabf5e2a5fad1cf98d74c38b2623f7150033b 100644 (file)
@@ -1854,6 +1854,8 @@ static int match_fragment(struct image *img,
 {
        int i;
        char *fixed_buf, *buf, *orig, *target;
+       struct strbuf fixed;
+       size_t fixed_len;
        int preimage_limit;
 
        if (preimage->nr + try_lno <= img->nr) {
@@ -1977,12 +1979,12 @@ static int match_fragment(struct image *img,
                 * use the whitespace from the preimage.
                 */
                extra_chars = preimage_end - preimage_eof;
-               fixed_buf = xmalloc(imgoff + extra_chars);
-               memcpy(fixed_buf, img->buf + try, imgoff);
-               memcpy(fixed_buf + imgoff, preimage_eof, extra_chars);
-               imgoff += extra_chars;
+               strbuf_init(&fixed, imgoff + extra_chars);
+               strbuf_add(&fixed, img->buf + try, imgoff);
+               strbuf_add(&fixed, preimage_eof, extra_chars);
+               fixed_buf = strbuf_detach(&fixed, &fixed_len);
                update_pre_post_images(preimage, postimage,
-                               fixed_buf, imgoff, postlen);
+                               fixed_buf, fixed_len, postlen);
                return 1;
        }
 
@@ -1999,27 +2001,22 @@ static int match_fragment(struct image *img,
         * but in this loop we will only handle the part of the
         * preimage that falls within the file.
         */
-       fixed_buf = xmalloc(preimage->len + 1);
-       buf = fixed_buf;
+       strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
        target = img->buf + try;
        for (i = 0; i < preimage_limit; i++) {
-               size_t fixlen; /* length after fixing the preimage */
                size_t oldlen = preimage->line[i].len;
                size_t tgtlen = img->line[try_lno + i].len;
-               size_t tgtfixlen; /* length after fixing the target line */
-               char tgtfixbuf[1024], *tgtfix;
+               size_t fixstart = fixed.len;
+               struct strbuf tgtfix;
                int match;
 
                /* Try fixing the line in the preimage */
-               fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+               ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
 
                /* Try fixing the line in the target */
-               if (sizeof(tgtfixbuf) > tgtlen)
-                       tgtfix = tgtfixbuf;
-               else
-                       tgtfix = xmalloc(tgtlen);
-               tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+               strbuf_init(&tgtfix, tgtlen);
+               ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
 
                /*
                 * If they match, either the preimage was based on
@@ -2031,15 +2028,15 @@ static int match_fragment(struct image *img,
                 * so we might as well take the fix together with their
                 * real change.
                 */
-               match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+               match = (tgtfix.len == fixed.len - fixstart &&
+                        !memcmp(tgtfix.buf, fixed.buf + fixstart,
+                                            fixed.len - fixstart));
 
-               if (tgtfix != tgtfixbuf)
-                       free(tgtfix);
+               strbuf_release(&tgtfix);
                if (!match)
                        goto unmatch_exit;
 
                orig += oldlen;
-               buf += fixlen;
                target += tgtlen;
        }
 
@@ -2051,19 +2048,18 @@ static int match_fragment(struct image *img,
         * false).
         */
        for ( ; i < preimage->nr; i++) {
-               size_t fixlen; /* length after fixing the preimage */
+               size_t fixstart = fixed.len; /* start of the fixed preimage */
                size_t oldlen = preimage->line[i].len;
                int j;
 
                /* Try fixing the line in the preimage */
-               fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+               ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
 
-               for (j = 0; j < fixlen; j++)
-                       if (!isspace(buf[j]))
+               for (j = fixstart; j < fixed.len; j++)
+                       if (!isspace(fixed.buf[j]))
                                goto unmatch_exit;
 
                orig += oldlen;
-               buf += fixlen;
        }
 
        /*
@@ -2071,12 +2067,13 @@ static int match_fragment(struct image *img,
         * has whitespace breakages unfixed, and fixing them makes the
         * hunk match.  Update the context lines in the postimage.
         */
+       fixed_buf = strbuf_detach(&fixed, &fixed_len);
        update_pre_post_images(preimage, postimage,
-                              fixed_buf, buf - fixed_buf, 0);
+                              fixed_buf, fixed_len, 0);
        return 1;
 
  unmatch_exit:
-       free(fixed_buf);
+       strbuf_release(&fixed);
        return 0;
 }
 
@@ -2244,7 +2241,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        int match_beginning, match_end;
        const char *patch = frag->patch;
        int size = frag->size;
-       char *old, *new, *oldlines, *newlines;
+       char *old, *oldlines;
+       struct strbuf newlines;
        int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
        int pos, applied_pos;
@@ -2254,16 +2252,16 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        memset(&preimage, 0, sizeof(preimage));
        memset(&postimage, 0, sizeof(postimage));
        oldlines = xmalloc(size);
-       newlines = xmalloc(size);
+       strbuf_init(&newlines, size);
 
        old = oldlines;
-       new = newlines;
        while (size > 0) {
                char first;
                int len = linelen(patch, size);
-               int plen, added;
+               int plen;
                int added_blank_line = 0;
                int is_blank_context = 0;
+               size_t start;
 
                if (!len)
                        break;
@@ -2293,7 +2291,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                                /* ... followed by '\No newline'; nothing */
                                break;
                        *old++ = '\n';
-                       *new++ = '\n';
+                       strbuf_addch(&newlines, '\n');
                        add_line_info(&preimage, "\n", 1, LINE_COMMON);
                        add_line_info(&postimage, "\n", 1, LINE_COMMON);
                        is_blank_context = 1;
@@ -2315,18 +2313,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        if (first == '+' && no_add)
                                break;
 
+                       start = newlines.len;
                        if (first != '+' ||
                            !whitespace_error ||
                            ws_error_action != correct_ws_error) {
-                               memcpy(new, patch + 1, plen);
-                               added = plen;
+                               strbuf_add(&newlines, patch + 1, plen);
                        }
                        else {
-                               added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+                               ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
                        }
-                       add_line_info(&postimage, new, added,
+                       add_line_info(&postimage, newlines.buf + start, newlines.len - start,
                                      (first == '+' ? 0 : LINE_COMMON));
-                       new += added;
                        if (first == '+' &&
                            (ws_rule & WS_BLANK_AT_EOF) &&
                            ws_blank_line(patch + 1, plen, ws_rule))
@@ -2351,9 +2348,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        }
        if (inaccurate_eof &&
            old > oldlines && old[-1] == '\n' &&
-           new > newlines && new[-1] == '\n') {
+           newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
                old--;
-               new--;
+               strbuf_setlen(&newlines, newlines.len - 1);
        }
 
        leading = frag->leading;
@@ -2385,8 +2382,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        pos = frag->newpos ? (frag->newpos - 1) : 0;
        preimage.buf = oldlines;
        preimage.len = old - oldlines;
-       postimage.buf = newlines;
-       postimage.len = new - newlines;
+       postimage.buf = newlines.buf;
+       postimage.len = newlines.len;
        preimage.line = preimage.line_allocated;
        postimage.line = postimage.line_allocated;
 
@@ -2462,7 +2459,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        }
 
        free(oldlines);
-       free(newlines);
+       strbuf_release(&newlines);
        free(preimage.line_allocated);
        free(postimage.line_allocated);
 
diff --git a/cache.h b/cache.h
index bfc4d82f6c4205e11de6d855459d1afa02bf5cbf..9d97df9db980995f01d8b32f427edc6a1ff6f9c1 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1042,6 +1042,7 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *
 #define WS_INDENT_WITH_NON_TAB 04
 #define WS_CR_AT_EOL           010
 #define WS_BLANK_AT_EOF        020
+#define WS_TAB_IN_INDENT       040
 #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 extern unsigned whitespace_rule_cfg;
@@ -1050,7 +1051,7 @@ extern unsigned parse_whitespace_rule(const char *);
 extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
 extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
 extern char *whitespace_error_string(unsigned ws);
-extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
+extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
 extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 
 /* ls-files */
index 90f33423731a84310caf92780482a44f8d2f32d8..e92eab09cb3155e602027ce4104fe7ceb7af245e 100755 (executable)
@@ -352,6 +352,48 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab:
 
 '
 
+test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
+
+       git config core.whitespace "-tab-in-indent" &&
+       echo "  foo ();" > x &&
+       git diff --check
+
+'
+
+test_expect_success 'check tabs as indentation (tab-in-indent: on)' '
+
+       git config core.whitespace "tab-in-indent" &&
+       echo "  foo ();" > x &&
+       test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
+
+       git config core.whitespace "tab-in-indent" &&
+       echo "                  foo ();" > x &&
+       test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
+
+       git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
+       echo "foo ();" > x &&
+       test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent excluded from wildcard whitespace attribute' '
+
+       git config --unset core.whitespace &&
+       echo "x whitespace" > .gitattributes &&
+       echo "    foo ();" > x &&
+       git diff --check &&
+       rm -f .gitattributes
+
+'
+
 test_expect_success 'line numbers in --check output are correct' '
 
        echo "" > x &&
index fb9ad247bf76c07a8b6dbbb0d6bf1ab830041770..8d46df6c3abd0643031106dac15758fdbe5f2ccd 100755 (executable)
@@ -11,21 +11,22 @@ prepare_test_file () {
        #       !  trailing-space
        #       @  space-before-tab
        #       #  indent-with-non-tab
+       #       %  tab-in-indent
        sed -e "s/_/ /g" -e "s/>/       /" <<-\EOF
                An_SP in an ordinary line>and a HT.
-               >A HT.
-               _>A SP and a HT (@).
-               _>_A SP, a HT and a SP (@).
+               >A HT (%).
+               _>A SP and a HT (@%).
+               _>_A SP, a HT and a SP (@%).
                _______Seven SP.
                ________Eight SP (#).
-               _______>Seven SP and a HT (@).
-               ________>Eight SP and a HT (@#).
-               _______>_Seven SP, a HT and a SP (@).
-               ________>_Eight SP, a HT and a SP (@#).
+               _______>Seven SP and a HT (@%).
+               ________>Eight SP and a HT (@#%).
+               _______>_Seven SP, a HT and a SP (@%).
+               ________>_Eight SP, a HT and a SP (@#%).
                _______________Fifteen SP (#).
-               _______________>Fifteen SP and a HT (@#).
+               _______________>Fifteen SP and a HT (@#%).
                ________________Sixteen SP (#).
-               ________________>Sixteen SP and a HT (@#).
+               ________________>Sixteen SP and a HT (@#%).
                _____a__Five SP, a non WS, two SP.
                A line with a (!) trailing SP_
                A line with a (!) trailing HT>
@@ -39,7 +40,6 @@ apply_patch () {
 }
 
 test_fix () {
-
        # fix should not barf
        apply_patch --whitespace=fix || return 1
 
@@ -130,20 +130,25 @@ do
                for i in - ''
                do
                        case "$i" in '') ti='#' ;; *) ti= ;; esac
-                       rule=${t}trailing,${s}space,${i}indent
-
-                       rm -f .gitattributes
-                       test_expect_success "rule=$rule" '
-                               git config core.whitespace "$rule" &&
-                               test_fix "$tt$ts$ti"
-                       '
-
-                       test_expect_success "rule=$rule (attributes)" '
-                               git config --unset core.whitespace &&
-                               echo "target whitespace=$rule" >.gitattributes &&
-                               test_fix "$tt$ts$ti"
-                       '
-
+                       for h in - ''
+                       do
+                               [ -z "$h$i" ] && continue
+                               case "$h" in '') th='%' ;; *) th= ;; esac
+                               rule=${t}trailing,${s}space,${i}indent,${h}tab
+
+                               rm -f .gitattributes
+                               test_expect_success "rule=$rule" '
+                                       git config core.whitespace "$rule" &&
+                                       test_fix "$tt$ts$ti$th"
+                               '
+
+                               test_expect_success "rule=$rule (attributes)" '
+                                       git config --unset core.whitespace &&
+                                       echo "target whitespace=$rule" >.gitattributes &&
+                                       test_fix "$tt$ts$ti$th"
+                               '
+
+                       done
                done
        done
 done
diff --git a/ws.c b/ws.c
index c0893386e6c8aa3af002e847d228dfc5ef64a9cf..d7b8c33f14195ba7ebe86bdbca2fea8f1b22ad37 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -10,7 +10,8 @@
 static struct whitespace_rule {
        const char *rule_name;
        unsigned rule_bits;
-       unsigned loosens_error;
+       unsigned loosens_error:1,
+               exclude_default:1;
 } whitespace_rule_names[] = {
        { "trailing-space", WS_TRAILING_SPACE, 0 },
        { "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
@@ -18,6 +19,7 @@ static struct whitespace_rule {
        { "cr-at-eol", WS_CR_AT_EOL, 1 },
        { "blank-at-eol", WS_BLANK_AT_EOL, 0 },
        { "blank-at-eof", WS_BLANK_AT_EOF, 0 },
+       { "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 },
 };
 
 unsigned parse_whitespace_rule(const char *string)
@@ -56,6 +58,9 @@ unsigned parse_whitespace_rule(const char *string)
                }
                string = ep;
        }
+
+       if (rule & WS_TAB_IN_INDENT && rule & WS_INDENT_WITH_NON_TAB)
+               die("cannot enforce both tab-in-indent and indent-with-non-tab");
        return rule;
 }
 
@@ -82,7 +87,8 @@ unsigned whitespace_rule(const char *pathname)
                        unsigned all_rule = 0;
                        int i;
                        for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
-                               if (!whitespace_rule_names[i].loosens_error)
+                               if (!whitespace_rule_names[i].loosens_error &&
+                                   !whitespace_rule_names[i].exclude_default)
                                        all_rule |= whitespace_rule_names[i].rule_bits;
                        return all_rule;
                } else if (ATTR_FALSE(value)) {
@@ -125,6 +131,11 @@ char *whitespace_error_string(unsigned ws)
                        strbuf_addstr(&err, ", ");
                strbuf_addstr(&err, "indent with spaces");
        }
+       if (ws & WS_TAB_IN_INDENT) {
+               if (err.len)
+                       strbuf_addstr(&err, ", ");
+               strbuf_addstr(&err, "tab in indent");
+       }
        return strbuf_detach(&err, NULL);
 }
 
@@ -163,7 +174,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
                }
        }
 
-       /* Check for space before tab in initial indent. */
+       /* Check indentation */
        for (i = 0; i < len; i++) {
                if (line[i] == ' ')
                        continue;
@@ -175,11 +186,19 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
                                fputs(ws, stream);
                                fwrite(line + written, i - written, 1, stream);
                                fputs(reset, stream);
+                               fwrite(line + i, 1, 1, stream);
                        }
-               } else if (stream)
-                       fwrite(line + written, i - written, 1, stream);
-               if (stream)
-                       fwrite(line + i, 1, 1, stream);
+               } else if (ws_rule & WS_TAB_IN_INDENT) {
+                       result |= WS_TAB_IN_INDENT;
+                       if (stream) {
+                               fwrite(line + written, i - written, 1, stream);
+                               fputs(ws, stream);
+                               fwrite(line + i, 1, 1, stream);
+                               fputs(reset, stream);
+                       }
+               } else if (stream) {
+                       fwrite(line + written, i - written + 1, 1, stream);
+               }
                written = i + 1;
        }
 
@@ -252,8 +271,8 @@ int ws_blank_line(const char *line, int len, unsigned ws_rule)
        return 1;
 }
 
-/* Copy the line to the buffer while fixing whitespaces */
-int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *error_count)
+/* Copy the line onto the end of the strbuf while fixing whitespaces */
+void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule, int *error_count)
 {
        /*
         * len is number of bytes to be copied from src, starting
@@ -267,7 +286,6 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
        int last_tab_in_indent = -1;
        int last_space_in_indent = -1;
        int need_fix_leading_space = 0;
-       char *buf;
 
        /*
         * Strip trailing whitespace
@@ -307,7 +325,6 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
                        break;
        }
 
-       buf = dst;
        if (need_fix_leading_space) {
                /* Process indent ourselves */
                int consecutive_spaces = 0;
@@ -329,28 +346,41 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
                        char ch = src[i];
                        if (ch != ' ') {
                                consecutive_spaces = 0;
-                               *dst++ = ch;
+                               strbuf_addch(dst, ch);
                        } else {
                                consecutive_spaces++;
                                if (consecutive_spaces == 8) {
-                                       *dst++ = '\t';
+                                       strbuf_addch(dst, '\t');
                                        consecutive_spaces = 0;
                                }
                        }
                }
                while (0 < consecutive_spaces--)
-                       *dst++ = ' ';
+                       strbuf_addch(dst, ' ');
+               len -= last;
+               src += last;
+               fixed = 1;
+       } else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
+               /* Expand tabs into spaces */
+               int last = last_tab_in_indent + 1;
+               for (i = 0; i < last; i++) {
+                       if (src[i] == '\t')
+                               do {
+                                       strbuf_addch(dst, ' ');
+                               } while (dst->len % 8);
+                       else
+                               strbuf_addch(dst, src[i]);
+               }
                len -= last;
                src += last;
                fixed = 1;
        }
 
-       memcpy(dst, src, len);
+       strbuf_add(dst, src, len);
        if (add_cr_to_tail)
-               dst[len++] = '\r';
+               strbuf_addch(dst, '\r');
        if (add_nl_to_tail)
-               dst[len++] = '\n';
+               strbuf_addch(dst, '\n');
        if (fixed && error_count)
                (*error_count)++;
-       return dst + len - buf;
 }