From 8c701249d2257699c19822b528c101668abc55b9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 12 Apr 2007 23:05:29 -0700 Subject: Teach 'diff' about 'diff' attribute. This makes paths that explicitly unset 'diff' attribute not to produce "textual" diffs from 'git-diff' family. Signed-off-by: Junio C Hamano --- diff.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index fbb79d70a..e4efb657e 100644 --- a/diff.c +++ b/diff.c @@ -8,6 +8,7 @@ #include "delta.h" #include "xdiff-interface.h" #include "color.h" +#include "attr.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1051,13 +1052,34 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two) emit_binary_diff_body(two, one); } +static void setup_diff_attr_check(struct git_attr_check *check) +{ + static struct git_attr *attr_diff; + + if (!attr_diff) + attr_diff = git_attr("diff", 4); + check->attr = attr_diff; +} + #define FIRST_FEW_BYTES 8000 -static int mmfile_is_binary(mmfile_t *mf) +static int file_is_binary(struct diff_filespec *one) { - long sz = mf->size; + unsigned long sz; + struct git_attr_check attr_diff_check; + + setup_diff_attr_check(&attr_diff_check); + if (!git_checkattr(one->path, 1, &attr_diff_check) && + (0 == attr_diff_check.isset)) + return 1; + if (!one->data) { + if (!DIFF_FILE_VALID(one)) + return 0; + diff_populate_filespec(one, 0); + } + sz = one->size; if (FIRST_FEW_BYTES < sz) sz = FIRST_FEW_BYTES; - return !!memchr(mf->ptr, 0, sz); + return !!memchr(one->data, 0, sz); } static void builtin_diff(const char *name_a, @@ -1114,7 +1136,7 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) { + if (!o->text && (file_is_binary(one) || file_is_binary(two))) { /* Quite common confusing case */ if (mf1.size == mf2.size && !memcmp(mf1.ptr, mf2.ptr, mf1.size)) @@ -1190,7 +1212,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) { + if (file_is_binary(one) || file_is_binary(two)) { data->is_binary = 1; data->added = mf2.size; data->deleted = mf1.size; @@ -1228,7 +1250,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (mmfile_is_binary(&mf2)) + if (file_is_binary(two)) return; else { /* Crazy xdl interfaces.. */ @@ -1805,8 +1827,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (o->binary) { mmfile_t mf; - if ((!fill_mmfile(&mf, one) && mmfile_is_binary(&mf)) || - (!fill_mmfile(&mf, two) && mmfile_is_binary(&mf))) + if ((!fill_mmfile(&mf, one) && file_is_binary(one)) || + (!fill_mmfile(&mf, two) && file_is_binary(two))) abbrev = 40; } len += snprintf(msg + len, sizeof(msg) - len, @@ -2701,7 +2723,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) return error("unable to read files to diff"); /* Maybe hash p->two? into the patch id? */ - if (mmfile_is_binary(&mf2)) + if (file_is_binary(p->two)) continue; len1 = remove_space(p->one->path, strlen(p->one->path)); -- cgit v1.2.1 From 04786756f90a734445cc300a5acf9fcfc9fd4c04 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 15 Apr 2007 11:14:28 -0700 Subject: Expose subprojects as special files to "git diff" machinery The same way we generate diffs on symlinks as the the diff of text of the symlink, we can generate subproject diffs (when not recursing into them!) as the diff of the text that describes the subproject. Of course, since what descibes a subproject is just the SHA1, that's what we'll use. Add some pretty-printing to make it a bit more obvious what is going on, and we're done. So with this, we can get both raw diffs and "textual" diffs of subproject changes: - git diff --raw: :160000 160000 2de597b5ad348b7db04bd10cdd38cd81cbc93ab5 0000000... M sub-A - git diff: diff --git a/sub-A b/sub-A index 2de597b..e8f11a4 160000 --- a/sub-A +++ b/sub-A @@ -1 +1 @@ -Subproject commit 2de597b5ad348b7db04bd10cdd38cd81cbc93ab5 +Subproject commit e8f11a45c5c6b9e2fec6d136d3fb5aff75393d42 NOTE! We'll also want to have the ability to recurse into the subproject and actually diff it recursively, but that will involve a new command line option (I'd suggest "--subproject" and "-S", but the latter is in use by pickaxe), and some very different code. But regardless of ay future recursive behaviour, we need the non-recursive version too (and it should be the default, at least in the absense of config options, so that large superprojects don't default to something extremely expensive). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index fbb79d70a..1e8e689be 100644 --- a/diff.c +++ b/diff.c @@ -1397,6 +1397,22 @@ static int populate_from_stdin(struct diff_filespec *s) return 0; } +static int diff_populate_gitlink(struct diff_filespec *s, int size_only) +{ + int len; + char *data = xmalloc(100); + len = snprintf(data, 100, + "Subproject commit %s\n", sha1_to_hex(s->sha1)); + s->data = data; + s->size = len; + s->should_free = 1; + if (size_only) { + s->data = NULL; + free(data); + } + return 0; +} + /* * While doing rename detection and pickaxe operation, we may need to * grab the data for the blob (or file) for our own in-core comparison. @@ -1415,6 +1431,10 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (s->data) return err; + + if (S_ISDIRLNK(s->mode)) + return diff_populate_gitlink(s, size_only); + if (!s->sha1_valid || reuse_worktree_file(s->path, s->sha1, 0)) { struct stat st; -- cgit v1.2.1 From 40250af411f33afa0c39a5d461829b676453ce3b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 15 Apr 2007 14:35:11 -0700 Subject: Fix 'diff' attribute semantics. This is in the same spirit as the previous one. Earlier 'diff' meant 'do the built-in binary heuristics and disable patch text generation based on it' while '!diff' meant 'do not guess, do not generate patch text'. There was no way to say 'do generate patch text even when the heuristics says it has NUL in it'. Signed-off-by: Junio C Hamano --- diff.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e4efb657e..dcea405ed 100644 --- a/diff.c +++ b/diff.c @@ -1069,8 +1069,9 @@ static int file_is_binary(struct diff_filespec *one) setup_diff_attr_check(&attr_diff_check); if (!git_checkattr(one->path, 1, &attr_diff_check) && - (0 == attr_diff_check.isset)) - return 1; + (0 <= attr_diff_check.isset)) + return !attr_diff_check.isset; + if (!one->data) { if (!DIFF_FILE_VALID(one)) return 0; -- cgit v1.2.1 From 515106fa1335462393c08fa8712dddd767dc147a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 16 Apr 2007 21:33:31 -0700 Subject: Allow more than true/false to attributes. This allows you to define three values (and possibly more) to each attribute: true, false, and unset. Typically the handlers that notice and act on attribute values treat "unset" attribute to mean "do your default thing" (e.g. crlf that is unset would trigger "guess from contents"), so being able to override a setting to an unset state is actually useful. - If you want to set the attribute value to true, have an entry in .gitattributes file that mentions the attribute name; e.g. *.o binary - If you want to set the attribute value explicitly to false, use '-'; e.g. *.a -diff - If you want to make the attribute value _unset_, perhaps to override an earlier entry, use '!'; e.g. *.a -diff c.i.a !diff This also allows string values to attributes, with the natural syntax: attrname=attrvalue but you cannot use it, as nobody takes notice and acts on it yet. Signed-off-by: Junio C Hamano --- diff.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dcea405ed..a32078ea0 100644 --- a/diff.c +++ b/diff.c @@ -1068,9 +1068,18 @@ static int file_is_binary(struct diff_filespec *one) struct git_attr_check attr_diff_check; setup_diff_attr_check(&attr_diff_check); - if (!git_checkattr(one->path, 1, &attr_diff_check) && - (0 <= attr_diff_check.isset)) - return !attr_diff_check.isset; + if (!git_checkattr(one->path, 1, &attr_diff_check)) { + void *value = attr_diff_check.value; + if (ATTR_TRUE(value)) + return 0; + else if (ATTR_FALSE(value)) + return 1; + else if (ATTR_UNSET(value)) + ; + else + die("unknown value %s given to 'diff' attribute", + (char *)value); + } if (!one->data) { if (!DIFF_FILE_VALID(one)) -- cgit v1.2.1 From a5e92abde61d59a8612c5b87d0bae681e90f7fdb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 18 Apr 2007 16:16:37 -0700 Subject: Fix funny types used in attribute value representation It was bothering me a lot that I abused small integer values casted to (void *) to represent non string values in gitattributes. This corrects it by making the type of attribute values (const char *), and using the address of a few statically allocated character buffer to denote true/false. Unset attributes are represented as having NULLs as their values. Added in-header documentation to explain how git_checkattr() routine should be called. Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a32078ea0..5f501864e 100644 --- a/diff.c +++ b/diff.c @@ -1069,7 +1069,7 @@ static int file_is_binary(struct diff_filespec *one) setup_diff_attr_check(&attr_diff_check); if (!git_checkattr(one->path, 1, &attr_diff_check)) { - void *value = attr_diff_check.value; + const char *value = attr_diff_check.value; if (ATTR_TRUE(value)) return 0; else if (ATTR_FALSE(value)) @@ -1078,7 +1078,7 @@ static int file_is_binary(struct diff_filespec *one) ; else die("unknown value %s given to 'diff' attribute", - (char *)value); + value); } if (!one->data) { -- cgit v1.2.1 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 --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5f501864e..1cb1230a9 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; -- cgit v1.2.1 From f1af60bdba465779df92090ed370988f202ff043 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 22 Apr 2007 17:52:55 -0700 Subject: Support 'diff=pgm' attribute This enhances the attributes mechanism so that external programs meant for existing GIT_EXTERNAL_DIFF interface can be specifed per path. To configure such a custom diff driver, first define a custom diff driver in the configuration: [diff "my-c-diff"] command = <> Then mark the paths that you want to use this custom driver using the attribute mechanism. *.c diff=my-c-diff The intent of this separation is that the attribute mechanism is used for specifying the type of the contents, while the configuration mechanism is used to define what needs to be done to that type of the contents, which would be specific to both platform and personal taste. Signed-off-by: Junio C Hamano --- diff.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f51666496..9dfded766 100644 --- a/diff.c +++ b/diff.c @@ -52,6 +52,49 @@ static int parse_diff_color_slot(const char *var, int ofs) die("bad config variable '%s'", var); } +static struct ll_diff_driver { + const char *name; + struct ll_diff_driver *next; + char *cmd; +} *user_diff, **user_diff_tail; + +/* + * Currently there is only "diff..command" variable; + * because there are "diff.color." variables, we are parsing + * this in a bit convoluted way to allow low level diff driver + * called "color". + */ +static int parse_lldiff_command(const char *var, const char *ep, const char *value) +{ + const char *name; + int namelen; + struct ll_diff_driver *drv; + + name = var + 5; + namelen = ep - name; + for (drv = user_diff; drv; drv = drv->next) + if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) + break; + if (!drv) { + char *namebuf; + drv = xcalloc(1, sizeof(struct ll_diff_driver)); + namebuf = xmalloc(namelen + 1); + memcpy(namebuf, name, namelen); + namebuf[namelen] = 0; + drv->name = namebuf; + drv->next = NULL; + if (!user_diff_tail) + user_diff_tail = &user_diff; + *user_diff_tail = drv; + user_diff_tail = &(drv->next); + } + + if (!value) + return error("%s: lacks value", var); + drv->cmd = strdup(value); + return 0; +} + /* * These are to give UI layer defaults. * The core-level commands such as git-diff-files should @@ -78,11 +121,18 @@ int git_diff_ui_config(const char *var, const char *value) diff_detect_rename_default = DIFF_DETECT_RENAME; return 0; } + if (!prefixcmp(var, "diff.")) { + const char *ep = strrchr(var, '.'); + + if (ep != var + 4 && !strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); + } if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); color_parse(value, var, diff_colors[slot]); return 0; } + return git_default_config(var, value); } @@ -1074,11 +1124,6 @@ static int file_is_binary(struct diff_filespec *one) return 0; else if (ATTR_FALSE(value)) return 1; - else if (ATTR_UNSET(value)) - ; - else - die("unknown value %s given to 'diff' attribute", - value); } if (!one->data) { @@ -1752,6 +1797,30 @@ static void run_external_diff(const char *pgm, } } +static const char *external_diff_attr(const char *name) +{ + struct git_attr_check attr_diff_check; + + setup_diff_attr_check(&attr_diff_check); + if (!git_checkattr(name, 1, &attr_diff_check)) { + const char *value = attr_diff_check.value; + if (!ATTR_TRUE(value) && + !ATTR_FALSE(value) && + !ATTR_UNSET(value)) { + struct ll_diff_driver *drv; + + if (!user_diff_tail) { + user_diff_tail = &user_diff; + git_config(git_diff_ui_config); + } + for (drv = user_diff; drv; drv = drv->next) + if (!strcmp(drv->name, value)) + return drv->cmd; + } + } + return NULL; +} + static void run_diff_cmd(const char *pgm, const char *name, const char *other, @@ -1761,6 +1830,14 @@ static void run_diff_cmd(const char *pgm, struct diff_options *o, int complete_rewrite) { + if (!o->allow_external) + pgm = NULL; + else { + const char *cmd = external_diff_attr(name); + if (cmd) + pgm = cmd; + } + if (pgm) { run_external_diff(pgm, name, other, one, two, xfrm_msg, complete_rewrite); -- cgit v1.2.1 From cdda666201710dcf50d7ebee804aac65fdec32fd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 3 May 2007 13:00:43 -0700 Subject: diff.c: fix "size cache" handling. We broke the size-cache handling when we changed the function signature of sha1_object_info() in 21666f1a. We obviously wanted to cache the size we obtained when sha1_object_info() succeeded, not when it failed. Signed-off-by: Junio C Hamano --- diff.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d8f9242ea..b28933f87 100644 --- a/diff.c +++ b/diff.c @@ -1468,14 +1468,15 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) enum object_type type; struct sha1_size_cache *e; + if (size_only && use_size_cache && + (e = locate_size_cache(s->sha1, 1, 0)) != NULL) { + s->size = e->size; + return 0; + } + if (size_only) { - e = locate_size_cache(s->sha1, 1, 0); - if (e) { - s->size = e->size; - return 0; - } type = sha1_object_info(s->sha1, &s->size); - if (type < 0) + if (use_size_cache && 0 < type) locate_size_cache(s->sha1, 0, s->size); } else { -- cgit v1.2.1 From fc3abdf5cb16914fb22747d7c3fb44258712a36d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 3 May 2007 13:05:48 -0700 Subject: diff: release blobs after generating textual diff. This reduces the memory pressure when dealing with many paths. An unscientific test of running "diff-tree --stat --summary -M" between v2.6.19 and v2.6.20-rc1 in the linux kernel repository indicates that the number of minor faults are reduced by 2/3. Signed-off-by: Junio C Hamano --- diff.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7bbe7590b..2979bc57e 100644 --- a/diff.c +++ b/diff.c @@ -1236,6 +1236,8 @@ static void builtin_diff(const char *name_a, } free_ab_and_return: + diff_free_filespec_data(one); + diff_free_filespec_data(two); free(a_one); free(b_two); return; @@ -1262,7 +1264,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, diff_populate_filespec(two, 0); data->deleted = count_lines(one->data, one->size); data->added = count_lines(two->data, two->size); - return; + goto free_and_return; } if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1284,6 +1286,10 @@ static void builtin_diffstat(const char *name_a, const char *name_b, ecb.priv = diffstat; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } + + free_and_return: + diff_free_filespec_data(one); + diff_free_filespec_data(two); } static void builtin_checkdiff(const char *name_a, const char *name_b, @@ -1306,7 +1312,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, die("unable to read files to diff"); if (file_is_binary(two)) - return; + goto free_and_return; else { /* Crazy xdl interfaces.. */ xpparam_t xpp; @@ -1320,6 +1326,9 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, ecb.priv = &data; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } + free_and_return: + diff_free_filespec_data(one); + diff_free_filespec_data(two); } struct diff_filespec *alloc_filespec(const char *path) @@ -1507,7 +1516,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) size_only = 0; if (s->data) - return err; + return 0; if (S_ISDIRLNK(s->mode)) return diff_populate_gitlink(s, size_only); @@ -1597,8 +1606,11 @@ void diff_free_filespec_data(struct diff_filespec *s) free(s->data); else if (s->should_munmap) munmap(s->data, s->size); - s->should_free = s->should_munmap = 0; - s->data = NULL; + + if (s->should_free || s->should_munmap) { + s->should_free = s->should_munmap = 0; + s->data = NULL; + } free(s->cnt_data); s->cnt_data = NULL; } -- cgit v1.2.1 From 6e0b8ed6d35d0ed94b22652d6e8545b610cc43ab Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 7 May 2007 01:14:21 -0700 Subject: diff.c: do not use a separate "size cache". diff_filespec has a slot to record the size of the data already, so make use of it instead of a separate size cache. Signed-off-by: Junio C Hamano --- diff.c | 72 ++++-------------------------------------------------------------- 1 file changed, 4 insertions(+), 68 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 2979bc57e..8354e71e0 100644 --- a/diff.c +++ b/diff.c @@ -16,8 +16,6 @@ #define FAST_WORKING_DIRECTORY 1 #endif -static int use_size_cache; - static int diff_detect_rename_default; static int diff_rename_limit_default = -1; static int diff_use_color_default; @@ -1408,55 +1406,6 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int return 1; } -static struct sha1_size_cache { - unsigned char sha1[20]; - unsigned long size; -} **sha1_size_cache; -static int sha1_size_cache_nr, sha1_size_cache_alloc; - -static struct sha1_size_cache *locate_size_cache(unsigned char *sha1, - int find_only, - unsigned long size) -{ - int first, last; - struct sha1_size_cache *e; - - first = 0; - last = sha1_size_cache_nr; - while (last > first) { - int cmp, next = (last + first) >> 1; - e = sha1_size_cache[next]; - cmp = hashcmp(e->sha1, sha1); - if (!cmp) - return e; - if (cmp < 0) { - last = next; - continue; - } - first = next+1; - } - /* not found */ - if (find_only) - return NULL; - /* insert to make it at "first" */ - if (sha1_size_cache_alloc <= sha1_size_cache_nr) { - sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc); - sha1_size_cache = xrealloc(sha1_size_cache, - sha1_size_cache_alloc * - sizeof(*sha1_size_cache)); - } - sha1_size_cache_nr++; - if (first < sha1_size_cache_nr) - memmove(sha1_size_cache + first + 1, sha1_size_cache + first, - (sha1_size_cache_nr - first - 1) * - sizeof(*sha1_size_cache)); - e = xmalloc(sizeof(struct sha1_size_cache)); - sha1_size_cache[first] = e; - hashcpy(e->sha1, sha1); - e->size = size; - return e; -} - static int populate_from_stdin(struct diff_filespec *s) { #define INCREMENT 1024 @@ -1512,12 +1461,12 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (S_ISDIR(s->mode)) return -1; - if (!use_size_cache) - size_only = 0; - if (s->data) return 0; + if (size_only && 0 < s->size) + return 0; + if (S_ISDIRLNK(s->mode)) return diff_populate_gitlink(s, size_only); @@ -1579,19 +1528,8 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) } else { enum object_type type; - struct sha1_size_cache *e; - - if (size_only && use_size_cache && - (e = locate_size_cache(s->sha1, 1, 0)) != NULL) { - s->size = e->size; - return 0; - } - - if (size_only) { + if (size_only) type = sha1_object_info(s->sha1, &s->size); - if (use_size_cache && 0 < type) - locate_size_cache(s->sha1, 0, s->size); - } else { s->data = read_sha1_file(s->sha1, &type, &s->size); s->should_free = 1; @@ -2102,8 +2040,6 @@ int diff_setup_done(struct diff_options *options) */ read_cache(); } - if (options->setup & DIFF_SETUP_USE_SIZE_CACHE) - use_size_cache = 1; if (options->abbrev <= 0 || 40 < options->abbrev) options->abbrev = 40; /* full */ -- cgit v1.2.1 From 8a912bcb250d8bf57b225e1cf02c0d69d54c8920 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 15 May 2007 14:49:22 +0200 Subject: Ensure return value from xread() is always stored into an ssize_t This patch fixes all calls to xread() where the return value is not stored into an ssize_t. The patch should not have any effect whatsoever, other than putting better/more appropriate type names on variables. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 8354e71e0..33297aa8a 100644 --- a/diff.c +++ b/diff.c @@ -1411,7 +1411,7 @@ static int populate_from_stdin(struct diff_filespec *s) #define INCREMENT 1024 char *buf; unsigned long size; - int got; + ssize_t got; size = 0; buf = NULL; -- cgit v1.2.1 From 1472966c04103874096fb786657459ea6628e451 Mon Sep 17 00:00:00 2001 From: "Fernando J. Pereda" Date: Sun, 20 May 2007 15:35:46 +0200 Subject: Use PATH_MAX instead of TEMPFILE_PATH_LEN Signed-off-by: Fernando J. Pereda Signed-off-by: Junio C Hamano --- diff.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b28933f87..3c1555371 100644 --- a/diff.c +++ b/diff.c @@ -137,13 +137,11 @@ static const char *external_diff(void) return external_diff_cmd; } -#define TEMPFILE_PATH_LEN 50 - static struct diff_tempfile { const char *name; /* filename external diff should read from */ char hex[41]; char mode[10]; - char tmp_path[TEMPFILE_PATH_LEN]; + char tmp_path[PATH_MAX]; } diff_temp[2]; static int count_lines(const char *data, int size) @@ -1507,7 +1505,7 @@ static void prep_temp_blob(struct diff_tempfile *temp, { int fd; - fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX"); + fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX"); if (fd < 0) die("unable to create temp-file"); if (write_in_full(fd, blob, size) != size) -- cgit v1.2.1 From 302b9282c9ddfcc704ca759bdc98c1d5f75eba2f Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Mon, 21 May 2007 22:08:28 +0200 Subject: rename dirlink to gitlink. Unify naming of plumbing dirlink/gitlink concept: git ls-files -z '*.[ch]' | xargs -0 perl -pi -e 's/dirlink/gitlink/g;' -e 's/DIRLNK/GITLINK/g;' Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b23e19067..0e260490d 100644 --- a/diff.c +++ b/diff.c @@ -1465,7 +1465,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (size_only && 0 < s->size) return 0; - if (S_ISDIRLNK(s->mode)) + if (S_ISGITLINK(s->mode)) return diff_populate_gitlink(s, size_only); if (!s->sha1_valid || -- cgit v1.2.1 From 5adf317b31729707fad4967c1aef6cdba43d0dd3 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 26 May 2007 00:37:40 +0200 Subject: Replace the last 'dircache's by 'index' Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3c1555371..487168be4 100644 --- a/diff.c +++ b/diff.c @@ -1258,7 +1258,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, } /* - * Given a name and sha1 pair, if the dircache tells us the file in + * Given a name and sha1 pair, if the index tells us the file in * the work tree has that object contents, return true, so that * prepare_temp_file() does not have to inflate and extract. */ -- cgit v1.2.1 From 6bfce93e04d13ecb42008a3cf214cc892f480f0c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 5 Jun 2007 03:36:11 +0100 Subject: Move buffer_is_binary() to xdiff-interface.h We already have two instances where we want to determine if a buffer contains binary data as opposed to text. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 508bc51ed..c57ac3341 100644 --- a/diff.c +++ b/diff.c @@ -1107,10 +1107,8 @@ static void setup_diff_attr_check(struct git_attr_check *check) check->attr = attr_diff; } -#define FIRST_FEW_BYTES 8000 static int file_is_binary(struct diff_filespec *one) { - unsigned long sz; struct git_attr_check attr_diff_check; setup_diff_attr_check(&attr_diff_check); @@ -1127,10 +1125,7 @@ static int file_is_binary(struct diff_filespec *one) return 0; diff_populate_filespec(one, 0); } - sz = one->size; - if (FIRST_FEW_BYTES < sz) - sz = FIRST_FEW_BYTES; - return !!memchr(one->data, 0, sz); + return buffer_is_binary(one->data, one->size); } static void builtin_diff(const char *name_a, -- cgit v1.2.1 From a6080a0a44d5ead84db3dabbbc80e82df838533d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2007 00:04:01 -0700 Subject: War on whitespace This uses "git-apply --whitespace=strip" to fix whitespace errors that have crept in to our source files over time. There are a few files that need to have trailing whitespaces (most notably, test vectors). The results still passes the test, and build result in Documentation/ area is unchanged. Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c57ac3341..cafa7debe 100644 --- a/diff.c +++ b/diff.c @@ -3031,7 +3031,7 @@ void diff_addremove(struct diff_options *options, * entries to the diff-core. They will be prefixed * with something like '=' or '*' (I haven't decided * which but should not make any difference). - * Feeding the same new and old to diff_change() + * Feeding the same new and old to diff_change() * also has the same effect. * Before the final output happens, they are pruned after * merged into rename/copy pairs as appropriate. @@ -3058,7 +3058,7 @@ void diff_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, - const char *base, const char *path) + const char *base, const char *path) { char concatpath[PATH_MAX]; struct diff_filespec *one, *two; -- cgit v1.2.1 From 16befb8b7fbfcc9b2d38931f4081669558300adf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 8 Jun 2007 02:54:57 -0700 Subject: Even more missing static Signed-off-by: Junio C Hamano --- diff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index cafa7debe..1d234d361 100644 --- a/diff.c +++ b/diff.c @@ -2103,6 +2103,8 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va return 1; } +static int diff_scoreopt_parse(const char *opt); + int diff_opt_parse(struct diff_options *options, const char **av, int ac) { const char *arg = av[0]; @@ -2274,7 +2276,7 @@ static int parse_num(const char **cp_p) return (int)((num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale)); } -int diff_scoreopt_parse(const char *opt) +static int diff_scoreopt_parse(const char *opt) { int opt1, opt2, cmd; -- cgit v1.2.1 From ca6c097089a38d447de0190ea61e9746a40a170c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 11 Jun 2007 21:12:19 +0100 Subject: Teach diff to imply --find-copies-harder upon -C -C Earlier, a second "-C" on the command line had no effect. But "--find-copies-harder" is so long to type, let's make doubled -C enable that option. It is in line with how "git blame" handles such doubled options to mean "work harder". Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 1d234d361..4aa9bbc01 100644 --- a/diff.c +++ b/diff.c @@ -2201,6 +2201,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->detect_rename = DIFF_DETECT_RENAME; } else if (!prefixcmp(arg, "-C")) { + if (options->detect_rename == DIFF_DETECT_COPY) + options->find_copies_harder = 1; if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return -1; -- cgit v1.2.1 From 634cd48a8afdd920fa26c8ec3ae43e96c82c81f2 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 5 Jun 2007 03:36:11 +0100 Subject: Move buffer_is_binary() to xdiff-interface.h We already have two instances where we want to determine if a buffer contains binary data as opposed to text. [jc: cherry-picked 6bfce93e from 'master'] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index af282ddd8..dc6da5e25 100644 --- a/diff.c +++ b/diff.c @@ -1107,10 +1107,8 @@ static void setup_diff_attr_check(struct git_attr_check *check) check->attr = attr_diff; } -#define FIRST_FEW_BYTES 8000 static int file_is_binary(struct diff_filespec *one) { - unsigned long sz; struct git_attr_check attr_diff_check; setup_diff_attr_check(&attr_diff_check); @@ -1127,10 +1125,7 @@ static int file_is_binary(struct diff_filespec *one) return 0; diff_populate_filespec(one, 0); } - sz = one->size; - if (FIRST_FEW_BYTES < sz) - sz = FIRST_FEW_BYTES; - return !!memchr(one->data, 0, sz); + return buffer_is_binary(one->data, one->size); } static void builtin_diff(const char *name_a, -- cgit v1.2.1 From 750f7b668f33c9e8decbdd8141115328992d6fea Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 19 Jun 2007 14:22:46 -0700 Subject: Finally implement "git log --follow" Ok, I've really held off doing this too damn long, because I'm lazy, and I was always hoping that somebody else would do it. But no, people keep asking for it, but nobody actually did anything, so I decided I might as well bite the bullet, and instead of telling people they could add a "--follow" flag to "git log" to do what they want to do, I decided that it looks like I just have to do it for them.. The code wasn't actually that complicated, in that the diffstat for this patch literally says "70 insertions(+), 1 deletions(-)", but I will have to admit that in order to get to this fairly simple patch, you did have to know and understand the internal git diff generation machinery pretty well, and had to really be able to follow how commit generation interacts with generating patches and generating the log. So I suspect that while I was right that it wasn't that hard, I might have been expecting too much of random people - this patch does seem to be firmly in the core "Linus or Junio" territory. To make a long story short: I'm sorry for it taking so long until I just did it. I'm not going to guarantee that this works for everybody, but you really can just look at the patch, and after the appropriate appreciative noises ("Ooh, aah") over how clever I am, you can then just notice that the code itself isn't really that complicated. All the real new code is in the new "try_to_follow_renames()" function. It really isn't rocket science: we notice that the pathname we were looking at went away, so we start a full tree diff and try to see if we can instead make that pathname be a rename or a copy from some other previous pathname. And if we can, we just continue, except we show *that* particular diff, and ever after we use the _previous_ pathname. One thing to look out for: the "rename detection" is considered to be a singular event in the _linear_ "git log" output! That's what people want to do, but I just wanted to point out that this patch is *not* carrying around a "commit,pathname" kind of pair and it's *not* going to be able to notice the file coming from multiple *different* files in earlier history. IOW, if you use "git log --follow", then you get the stupid CVS/SVN kind of "files have single identities" kind of semantics, and git log will just pick the identity based on the normal move/copy heuristics _as_if_ the history could be linearized. Put another way: I think the model is broken, but given the broken model, I think this patch does just about as well as you can do. If you have merges with the same "file" having different filenames over the two branches, git will just end up picking _one_ of the pathnames at the point where the newer one goes away. It never looks at multiple pathnames in parallel. And if you understood all that, you probably didn't need it explained, and if you didn't understand the above blathering, it doesn't really mtter to you. What matters to you is that you can now do git log -p --follow builtin-rev-list.c and it will find the point where the old "rev-list.c" got renamed to "builtin-rev-list.c" and show it as such. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 4aa9bbc01..9938969fa 100644 --- a/diff.c +++ b/diff.c @@ -2210,6 +2210,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--find-copies-harder")) options->find_copies_harder = 1; + else if (!strcmp(arg, "--follow")) + options->follow_renames = 1; else if (!strcmp(arg, "--abbrev")) options->abbrev = DEFAULT_ABBREV; else if (!prefixcmp(arg, "--abbrev=")) { -- cgit v1.2.1 From 125b7630521918b75c136bd95309054d3f6d9da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 25 Jun 2007 00:23:34 +0200 Subject: diff: round down similarity index Rounding down the printed (dis)similarity index allows us to use "100%" as a special value that indicates complete rewrites and fully equal file contents, respectively. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- diff.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9938969fa..e0edb9884 100644 --- a/diff.c +++ b/diff.c @@ -1813,6 +1813,11 @@ static void diff_fill_sha1_info(struct diff_filespec *one) hashclr(one->sha1); } +static int similarity_index(struct diff_filepair *p) +{ + return p->score * 100 / MAX_SCORE; +} + static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); @@ -1847,23 +1852,20 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) "similarity index %d%%\n" "copy from %s\n" "copy to %s\n", - (int)(0.5 + p->score * 100.0/MAX_SCORE), - name_munged, other_munged); + similarity_index(p), name_munged, other_munged); break; case DIFF_STATUS_RENAMED: len += snprintf(msg + len, sizeof(msg) - len, "similarity index %d%%\n" "rename from %s\n" "rename to %s\n", - (int)(0.5 + p->score * 100.0/MAX_SCORE), - name_munged, other_munged); + similarity_index(p), name_munged, other_munged); break; case DIFF_STATUS_MODIFIED: if (p->score) { len += snprintf(msg + len, sizeof(msg) - len, "dissimilarity index %d%%\n", - (int)(0.5 + p->score * - 100.0/MAX_SCORE)); + similarity_index(p)); complete_rewrite = 1; break; } @@ -2387,8 +2389,7 @@ static void diff_flush_raw(struct diff_filepair *p, } if (p->score) - sprintf(status, "%c%03d", p->status, - (int)(0.5 + p->score * 100.0/MAX_SCORE)); + sprintf(status, "%c%03d", p->status, similarity_index(p)); else { status[0] = p->status; status[1] = 0; @@ -2670,8 +2671,7 @@ static void show_rename_copy(const char *renamecopy, struct diff_filepair *p) { char *names = pprint_rename(p->one->path, p->two->path); - printf(" %s %s (%d%%)\n", renamecopy, names, - (int)(0.5 + p->score * 100.0/MAX_SCORE)); + printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p)); free(names); show_mode_change(p, 0); } @@ -2695,7 +2695,7 @@ static void diff_summary(struct diff_filepair *p) if (p->score) { char *name = quote_one(p->two->path); printf(" rewrite %s (%d%%)\n", name, - (int)(0.5 + p->score * 100.0/MAX_SCORE)); + similarity_index(p)); free(name); show_mode_change(p, 0); } else show_mode_change(p, 1); -- cgit v1.2.1 From 706098af6b53403b5ea3db9216fb99afbe06f52d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 28 Jun 2007 22:56:07 -0700 Subject: diffcore_filespec: add is_binary diffcore-break and diffcore-rename would want to behave slightly differently depending on the binary-ness of the data, so add one bit to the filespec, as the structure is now passed down to diffcore_count_changes() function. Signed-off-by: Junio C Hamano --- diff.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9938969fa..74c1198e6 100644 --- a/diff.c +++ b/diff.c @@ -3005,6 +3005,22 @@ void diffcore_std(struct diff_options *options) { if (options->quiet) return; + + /* + * break/rename count similarity differently depending on + * the binary-ness. + */ + if ((options->break_opt != -1) || (options->detect_rename)) { + struct diff_queue_struct *q = &diff_queued_diff; + int i; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + p->one->is_binary = file_is_binary(p->one); + p->two->is_binary = file_is_binary(p->two); + } + } + if (options->break_opt != -1) diffcore_break(options->break_opt); if (options->detect_rename) -- cgit v1.2.1 From 3cb567386d5d0349bfb5e3aaf85e973faf685dda Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 3 Jul 2007 16:01:06 +0100 Subject: diff --no-index: fix --name-status with added files Without this patch, an added file would be reported as /dev/null. Noticed by David Kastrup. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dc6da5e25..da992dd48 100644 --- a/diff.c +++ b/diff.c @@ -2407,7 +2407,8 @@ static void diff_flush_raw(struct diff_filepair *p, printf("%s ", diff_unique_abbrev(p->two->sha1, abbrev)); } - printf("%s%c%s", status, inter_name_termination, path_one); + printf("%s%c%s", status, inter_name_termination, + two_paths || p->one->mode ? path_one : path_two); if (two_paths) printf("%c%s", inter_name_termination, path_two); putchar(line_termination); -- cgit v1.2.1 From 72909befaa043fcc975115bb56d25a6e7dc65fb6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 30 Jun 2007 18:47:07 +0100 Subject: Add diff-option --ext-diff To prevent funky games with external diff engines, git-log and friends prevent external diff engines from being called. That makes sense in the context of git-format-patch or git-rebase. However, for "git log -p" it is not so nice to get the message that binary files cannot be compared, while "git diff" has no problems with them, if you provided an external diff driver. With this patch, "git log --ext-diff -p" will do what you expect, and the option "--no-ext-diff" can be used to override that setting. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 93eca79c1..b6eb72be0 100644 --- a/diff.c +++ b/diff.c @@ -2241,6 +2241,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->exit_with_status = 1; else if (!strcmp(arg, "--quiet")) options->quiet = 1; + else if (!strcmp(arg, "--ext-diff")) + options->allow_external = 1; + else if (!strcmp(arg, "--no-ext-diff")) + options->allow_external = 0; else return 0; return 1; -- cgit v1.2.1 From 29a3eefde111f6a24292163c4308f00ab3572627 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 6 Jul 2007 00:18:54 -0700 Subject: Introduce diff_filespec_is_binary() This replaces an explicit initialization of filespec->is_binary field used for rename/break followed by direct access to that field with a wrapper function that lazily iniaitlizes and accesses the field. We would add more attribute accesses for the use of diff routines, and it would be better to make this abstraction earlier. Signed-off-by: Junio C Hamano --- diff.c | 71 +++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 19589707c..16ea7100d 100644 --- a/diff.c +++ b/diff.c @@ -1102,30 +1102,45 @@ static void setup_diff_attr_check(struct git_attr_check *check) { static struct git_attr *attr_diff; - if (!attr_diff) + if (!attr_diff) { attr_diff = git_attr("diff", 4); - check->attr = attr_diff; + } + check[0].attr = attr_diff; } -static int file_is_binary(struct diff_filespec *one) +static void diff_filespec_check_attr(struct diff_filespec *one) { - struct git_attr_check attr_diff_check; + struct git_attr_check attr_diff_check[1]; - setup_diff_attr_check(&attr_diff_check); - if (!git_checkattr(one->path, 1, &attr_diff_check)) { - const char *value = attr_diff_check.value; + if (one->checked_attr) + return; + + setup_diff_attr_check(attr_diff_check); + one->is_binary = 0; + + if (!git_checkattr(one->path, ARRAY_SIZE(attr_diff_check), attr_diff_check)) { + const char *value; + + /* binaryness */ + value = attr_diff_check[0].value; if (ATTR_TRUE(value)) - return 0; + ; else if (ATTR_FALSE(value)) - return 1; + one->is_binary = 1; } - if (!one->data) { - if (!DIFF_FILE_VALID(one)) - return 0; + if (!one->data && DIFF_FILE_VALID(one)) diff_populate_filespec(one, 0); - } - return buffer_is_binary(one->data, one->size); + + if (one->data) + one->is_binary = buffer_is_binary(one->data, one->size); + +} + +int diff_filespec_is_binary(struct diff_filespec *one) +{ + diff_filespec_check_attr(one); + return one->is_binary; } static void builtin_diff(const char *name_a, @@ -1182,7 +1197,8 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (!o->text && (file_is_binary(one) || file_is_binary(two))) { + if (!o->text && + (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) { /* Quite common confusing case */ if (mf1.size == mf2.size && !memcmp(mf1.ptr, mf2.ptr, mf1.size)) @@ -1260,7 +1276,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (file_is_binary(one) || file_is_binary(two)) { + if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) { data->is_binary = 1; data->added = mf2.size; data->deleted = mf1.size; @@ -1302,7 +1318,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (file_is_binary(two)) + if (diff_filespec_is_binary(two)) goto free_and_return; else { /* Crazy xdl interfaces.. */ @@ -1880,8 +1896,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (o->binary) { mmfile_t mf; - if ((!fill_mmfile(&mf, one) && file_is_binary(one)) || - (!fill_mmfile(&mf, two) && file_is_binary(two))) + if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || + (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) abbrev = 40; } len += snprintf(msg + len, sizeof(msg) - len, @@ -2783,7 +2799,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) return error("unable to read files to diff"); /* Maybe hash p->two? into the patch id? */ - if (file_is_binary(p->two)) + if (diff_filespec_is_binary(p->two)) continue; len1 = remove_space(p->one->path, strlen(p->one->path)); @@ -3011,21 +3027,6 @@ void diffcore_std(struct diff_options *options) if (options->quiet) return; - /* - * break/rename count similarity differently depending on - * the binary-ness. - */ - if ((options->break_opt != -1) || (options->detect_rename)) { - struct diff_queue_struct *q = &diff_queued_diff; - int i; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - p->one->is_binary = file_is_binary(p->one); - p->two->is_binary = file_is_binary(p->two); - } - } - if (options->break_opt != -1) diffcore_break(options->break_opt); if (options->detect_rename) -- cgit v1.2.1 From 30b250104d9307e1225031c7fc39b66643265ed1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 4 Jul 2007 19:05:46 +0100 Subject: Future-proof source for changes in xdemitconf_t The instances of xdemitconf_t were initialized member by member. Instead, initialize them to all zero, so we do not have to update those places each time we introduce a new member. [jc: minimally fixed by getting rid of a new global] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 16ea7100d..d10e848c7 100644 --- a/diff.c +++ b/diff.c @@ -390,6 +390,7 @@ static void diff_words_show(struct diff_words_data *diff_words) mmfile_t minus, plus; int i; + memset(&xecfg, 0, sizeof(xecfg)); minus.size = diff_words->minus.text.size; minus.ptr = xmalloc(minus.size); memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size); @@ -408,7 +409,6 @@ static void diff_words_show(struct diff_words_data *diff_words) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; - xecfg.flags = 0; ecb.outf = xdiff_outf; ecb.priv = diff_words; diff_words->xm.consume = fn_out_diff_words_aux; @@ -1218,6 +1218,7 @@ static void builtin_diff(const char *name_a, xdemitcb_t ecb; struct emit_callback ecbdata; + memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.label_path = lbl; ecbdata.color_diff = o->color_diff; @@ -1286,9 +1287,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xdemitconf_t xecfg; xdemitcb_t ecb; + memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; - xecfg.ctxlen = 0; - xecfg.flags = 0; ecb.outf = xdiff_outf; ecb.priv = diffstat; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); @@ -1326,9 +1326,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, xdemitconf_t xecfg; xdemitcb_t ecb; + memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL; - xecfg.ctxlen = 0; - xecfg.flags = 0; ecb.outf = xdiff_outf; ecb.priv = &data; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); @@ -2780,6 +2779,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) struct diff_filepair *p = q->queue[i]; int len1, len2; + memset(&xecfg, 0, sizeof(xecfg)); if (p->status == 0) return error("internal diff status error"); if (p->status == DIFF_STATUS_UNKNOWN) -- cgit v1.2.1 From f258475a6ede3617ae768b69e33f78cbab8312de Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 6 Jul 2007 00:45:10 -0700 Subject: Per-path attribute based hunk header selection. This makes"diff -p" hunk headers customizable via gitattributes mechanism. It is based on Johannes's earlier patch that allowed to define a single regexp to be used for everything. The mechanism to arrive at the regexp that is used to define hunk header is the same as other use of gitattributes. You assign an attribute, funcname (because "diff -p" typically uses the name of the function the patch is about as the hunk header), a simple string value. This can be one of the names of built-in pattern (currently, "java" is defined) or a custom pattern name, to be looked up from the configuration file. (in .gitattributes) *.java funcname=java *.perl funcname=perl (in .git/config) [funcname] java = ... # ugly and complicated regexp to override the built-in one. perl = ... # another ugly and complicated regexp to define a new one. Signed-off-by: Junio C Hamano --- diff.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d10e848c7..04e7e91ad 100644 --- a/diff.c +++ b/diff.c @@ -1101,22 +1101,26 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two) static void setup_diff_attr_check(struct git_attr_check *check) { static struct git_attr *attr_diff; + static struct git_attr *attr_diff_func_name; if (!attr_diff) { attr_diff = git_attr("diff", 4); + attr_diff_func_name = git_attr("funcname", 8); } check[0].attr = attr_diff; + check[1].attr = attr_diff_func_name; } static void diff_filespec_check_attr(struct diff_filespec *one) { - struct git_attr_check attr_diff_check[1]; + struct git_attr_check attr_diff_check[2]; if (one->checked_attr) return; setup_diff_attr_check(attr_diff_check); one->is_binary = 0; + one->hunk_header_ident = NULL; if (!git_checkattr(one->path, ARRAY_SIZE(attr_diff_check), attr_diff_check)) { const char *value; @@ -1127,6 +1131,13 @@ static void diff_filespec_check_attr(struct diff_filespec *one) ; else if (ATTR_FALSE(value)) one->is_binary = 1; + + /* hunk header ident */ + value = attr_diff_check[1].value; + if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value)) + ; + else + one->hunk_header_ident = value; } if (!one->data && DIFF_FILE_VALID(one)) @@ -1143,6 +1154,82 @@ int diff_filespec_is_binary(struct diff_filespec *one) return one->is_binary; } +static struct hunk_header_regexp { + char *name; + char *regexp; + struct hunk_header_regexp *next; +} *hunk_header_regexp_list, **hunk_header_regexp_tail; + +static int hunk_header_config(const char *var, const char *value) +{ + static const char funcname[] = "funcname."; + struct hunk_header_regexp *hh; + + if (prefixcmp(var, funcname)) + return 0; + var += strlen(funcname); + for (hh = hunk_header_regexp_list; hh; hh = hh->next) + if (!strcmp(var, hh->name)) { + free(hh->regexp); + hh->regexp = xstrdup(value); + return 0; + } + hh = xcalloc(1, sizeof(*hh)); + hh->name = xstrdup(var); + hh->regexp = xstrdup(value); + hh->next = NULL; + *hunk_header_regexp_tail = hh; + return 0; +} + +static const char *hunk_header_regexp(const char *ident) +{ + struct hunk_header_regexp *hh; + + if (!hunk_header_regexp_tail) { + hunk_header_regexp_tail = &hunk_header_regexp_list; + git_config(hunk_header_config); + } + for (hh = hunk_header_regexp_list; hh; hh = hh->next) + if (!strcmp(ident, hh->name)) + return hh->regexp; + return NULL; +} + +static const char *diff_hunk_header_regexp(struct diff_filespec *one) +{ + const char *ident, *regexp; + + diff_filespec_check_attr(one); + ident = one->hunk_header_ident; + + if (!ident) + /* + * If the config file has "funcname.default" defined, that + * regexp is used; otherwise NULL is returned and xemit uses + * the built-in default. + */ + return hunk_header_regexp("default"); + + /* Look up custom "funcname.$ident" regexp from config. */ + regexp = hunk_header_regexp(ident); + if (regexp) + return regexp; + + /* + * And define built-in fallback patterns here. Note that + * these can be overriden by the user's config settings. + */ + if (!strcmp(ident, "java")) + return "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" + "new\\|return\\|switch\\|throw\\|while\\)\n" + "^[ ]*\\(\\([ ]*" + "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" + "[ ]*([^;]*$\\)"; + + return NULL; +} + static void builtin_diff(const char *name_a, const char *name_b, struct diff_filespec *one, @@ -1217,6 +1304,11 @@ static void builtin_diff(const char *name_a, xdemitconf_t xecfg; xdemitcb_t ecb; struct emit_callback ecbdata; + const char *hunk_header_regexp; + + hunk_header_regexp = diff_hunk_header_regexp(one); + if (!hunk_header_regexp) + hunk_header_regexp = diff_hunk_header_regexp(two); memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); @@ -1226,6 +1318,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 (hunk_header_regexp) + xdiff_set_find_func(&xecfg, hunk_header_regexp); if (!diffopts) ; else if (!prefixcmp(diffopts, "--unified=")) -- cgit v1.2.1 From e0e324a4dc18a4341e1320a7cfac9733d81f8b0b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 Jul 2007 01:49:58 -0700 Subject: Fix configuration syntax to specify customized hunk header patterns. This updates the hunk header customization syntax. The special case 'funcname' attribute is gone. You assign the name of the type of contents to path's "diff" attribute as a string value in .gitattributes like this: *.java diff=java *.perl diff=perl *.doc diff=doc If you supply "diff..funcname" variable via the configuration mechanism (e.g. in $HOME/.gitconfig), the value is used as the regexp set to find the line to use for the hunk header (the variable is called "funcname" because such a line typically is the one that has the name of the function in programming language source text). If there is no such configuration, built-in default is used, if any. Currently there are two default patterns: default and java. Signed-off-by: Junio C Hamano --- diff.c | 147 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 80 insertions(+), 67 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 04e7e91ad..21e61af26 100644 --- a/diff.c +++ b/diff.c @@ -56,6 +56,14 @@ static struct ll_diff_driver { char *cmd; } *user_diff, **user_diff_tail; +static void read_config_if_needed(void) +{ + if (!user_diff_tail) { + user_diff_tail = &user_diff; + git_config(git_diff_ui_config); + } +} + /* * Currently there is only "diff..command" variable; * because there are "diff.color." variables, we are parsing @@ -93,6 +101,45 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val return 0; } +/* + * 'diff..funcname' attribute can be specified in the configuration + * 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 { + char *name; + char *pattern; + struct funcname_pattern *next; +} *funcname_pattern_list; + +static int parse_funcname_pattern(const char *var, const char *ep, const char *value) +{ + const char *name; + int namelen; + struct funcname_pattern *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]) + break; + if (!pp) { + char *namebuf; + pp = xcalloc(1, sizeof(*pp)); + namebuf = xmalloc(namelen + 1); + memcpy(namebuf, name, namelen); + namebuf[namelen] = 0; + pp->name = namebuf; + pp->next = funcname_pattern_list; + funcname_pattern_list = pp; + } + if (pp->pattern) + free(pp->pattern); + pp->pattern = xstrdup(value); + return 0; +} + /* * These are to give UI layer defaults. * The core-level commands such as git-diff-files should @@ -122,8 +169,12 @@ int git_diff_ui_config(const char *var, const char *value) if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); - if (ep != var + 4 && !strcmp(ep, ".command")) - return parse_lldiff_command(var, ep, value); + if (ep != var + 4) { + if (!strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); + if (!strcmp(ep, ".funcname")) + return parse_funcname_pattern(var, ep, value); + } } if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); @@ -1101,43 +1152,39 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two) static void setup_diff_attr_check(struct git_attr_check *check) { static struct git_attr *attr_diff; - static struct git_attr *attr_diff_func_name; if (!attr_diff) { attr_diff = git_attr("diff", 4); - attr_diff_func_name = git_attr("funcname", 8); } check[0].attr = attr_diff; - check[1].attr = attr_diff_func_name; } static void diff_filespec_check_attr(struct diff_filespec *one) { - struct git_attr_check attr_diff_check[2]; + struct git_attr_check attr_diff_check; if (one->checked_attr) return; - setup_diff_attr_check(attr_diff_check); + setup_diff_attr_check(&attr_diff_check); one->is_binary = 0; - one->hunk_header_ident = NULL; + one->funcname_pattern_ident = NULL; - if (!git_checkattr(one->path, ARRAY_SIZE(attr_diff_check), attr_diff_check)) { + if (!git_checkattr(one->path, 1, &attr_diff_check)) { const char *value; /* binaryness */ - value = attr_diff_check[0].value; + value = attr_diff_check.value; if (ATTR_TRUE(value)) ; else if (ATTR_FALSE(value)) one->is_binary = 1; - /* hunk header ident */ - value = attr_diff_check[1].value; + /* funcname pattern ident */ if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value)) ; else - one->hunk_header_ident = value; + one->funcname_pattern_ident = value; } if (!one->data && DIFF_FILE_VALID(one)) @@ -1154,54 +1201,23 @@ int diff_filespec_is_binary(struct diff_filespec *one) return one->is_binary; } -static struct hunk_header_regexp { - char *name; - char *regexp; - struct hunk_header_regexp *next; -} *hunk_header_regexp_list, **hunk_header_regexp_tail; - -static int hunk_header_config(const char *var, const char *value) +static const char *funcname_pattern(const char *ident) { - static const char funcname[] = "funcname."; - struct hunk_header_regexp *hh; + struct funcname_pattern *pp; - if (prefixcmp(var, funcname)) - return 0; - var += strlen(funcname); - for (hh = hunk_header_regexp_list; hh; hh = hh->next) - if (!strcmp(var, hh->name)) { - free(hh->regexp); - hh->regexp = xstrdup(value); - return 0; - } - hh = xcalloc(1, sizeof(*hh)); - hh->name = xstrdup(var); - hh->regexp = xstrdup(value); - hh->next = NULL; - *hunk_header_regexp_tail = hh; - return 0; -} - -static const char *hunk_header_regexp(const char *ident) -{ - struct hunk_header_regexp *hh; - - if (!hunk_header_regexp_tail) { - hunk_header_regexp_tail = &hunk_header_regexp_list; - git_config(hunk_header_config); - } - for (hh = hunk_header_regexp_list; hh; hh = hh->next) - if (!strcmp(ident, hh->name)) - return hh->regexp; + read_config_if_needed(); + for (pp = funcname_pattern_list; pp; pp = pp->next) + if (!strcmp(ident, pp->name)) + return pp->pattern; return NULL; } -static const char *diff_hunk_header_regexp(struct diff_filespec *one) +static const char *diff_funcname_pattern(struct diff_filespec *one) { - const char *ident, *regexp; + const char *ident, *pattern; diff_filespec_check_attr(one); - ident = one->hunk_header_ident; + ident = one->funcname_pattern_ident; if (!ident) /* @@ -1209,12 +1225,12 @@ static const char *diff_hunk_header_regexp(struct diff_filespec *one) * regexp is used; otherwise NULL is returned and xemit uses * the built-in default. */ - return hunk_header_regexp("default"); + return funcname_pattern("default"); /* Look up custom "funcname.$ident" regexp from config. */ - regexp = hunk_header_regexp(ident); - if (regexp) - return regexp; + pattern = funcname_pattern(ident); + if (pattern) + return pattern; /* * And define built-in fallback patterns here. Note that @@ -1304,11 +1320,11 @@ static void builtin_diff(const char *name_a, xdemitconf_t xecfg; xdemitcb_t ecb; struct emit_callback ecbdata; - const char *hunk_header_regexp; + const char *funcname_pattern; - hunk_header_regexp = diff_hunk_header_regexp(one); - if (!hunk_header_regexp) - hunk_header_regexp = diff_hunk_header_regexp(two); + funcname_pattern = diff_funcname_pattern(one); + if (!funcname_pattern) + funcname_pattern = diff_funcname_pattern(two); memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); @@ -1318,8 +1334,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 (hunk_header_regexp) - xdiff_set_find_func(&xecfg, hunk_header_regexp); + if (funcname_pattern) + xdiff_set_find_func(&xecfg, funcname_pattern); if (!diffopts) ; else if (!prefixcmp(diffopts, "--unified=")) @@ -1862,10 +1878,7 @@ static const char *external_diff_attr(const char *name) !ATTR_UNSET(value)) { struct ll_diff_driver *drv; - if (!user_diff_tail) { - user_diff_tail = &user_diff; - git_config(git_diff_ui_config); - } + read_config_if_needed(); for (drv = user_diff; drv; drv = drv->next) if (!strcmp(drv->name, value)) return drv->cmd; -- cgit v1.2.1 From 2c3fa66f3577d1305fb0fac5a181261fb2597859 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 Jul 2007 12:25:11 -0700 Subject: diff: honor binariness specified in attributes The code shuffling mistakenly lost binariness specified with the attribute mecahnism and made it always guess from the data. Noticed by Johannes, with two test cases to t4020. Signed-off-by: Junio C Hamano --- diff.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 21e61af26..b8473f58f 100644 --- a/diff.c +++ b/diff.c @@ -1162,6 +1162,7 @@ static void setup_diff_attr_check(struct git_attr_check *check) static void diff_filespec_check_attr(struct diff_filespec *one) { struct git_attr_check attr_diff_check; + int check_from_data = 0; if (one->checked_attr) return; @@ -1179,6 +1180,8 @@ static void diff_filespec_check_attr(struct diff_filespec *one) ; else if (ATTR_FALSE(value)) one->is_binary = 1; + else + check_from_data = 1; /* funcname pattern ident */ if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value)) @@ -1187,12 +1190,13 @@ static void diff_filespec_check_attr(struct diff_filespec *one) one->funcname_pattern_ident = value; } - if (!one->data && DIFF_FILE_VALID(one)) - diff_populate_filespec(one, 0); - - if (one->data) - one->is_binary = buffer_is_binary(one->data, one->size); + if (check_from_data) { + if (!one->data && DIFF_FILE_VALID(one)) + diff_populate_filespec(one, 0); + if (one->data) + one->is_binary = buffer_is_binary(one->data, one->size); + } } int diff_filespec_is_binary(struct diff_filespec *one) -- cgit v1.2.1 From d3a93dc96786847f9d148da42ecadee5f027c9a7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Jul 2007 00:25:59 -0700 Subject: diff.c: make built-in hunk header pattern a separate table This would hopefully make it easier to maintain. Initially we would have "java" and "tex" defined, as they are the only ones we already have. Signed-off-by: Junio C Hamano --- diff.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b8473f58f..cd6b0c4e0 100644 --- a/diff.c +++ b/diff.c @@ -1216,9 +1216,22 @@ static const char *funcname_pattern(const char *ident) return NULL; } +static struct builtin_funcname_pattern { + const char *name; + const char *pattern; +} builtin_funcname_pattern[] = { + { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" + "new\\|return\\|switch\\|throw\\|while\\)\n" + "^[ ]*\\(\\([ ]*" + "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" + "[ ]*([^;]*$\\)" }, + { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, +}; + static const char *diff_funcname_pattern(struct diff_filespec *one) { const char *ident, *pattern; + int i; diff_filespec_check_attr(one); ident = one->funcname_pattern_ident; @@ -1240,12 +1253,9 @@ static const char *diff_funcname_pattern(struct diff_filespec *one) * And define built-in fallback patterns here. Note that * these can be overriden by the user's config settings. */ - if (!strcmp(ident, "java")) - return "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" - "new\\|return\\|switch\\|throw\\|while\\)\n" - "^[ ]*\\(\\([ ]*" - "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*$\\)"; + 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 NULL; } -- cgit v1.2.1 From e7a7be8831b159b9a7331b34c3ec6915d4a72190 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 25 Jul 2007 21:34:53 -0700 Subject: git_mkstemp(): be careful not to overflow the path buffer. If user's TMPDIR is insanely long, return negative after setting errno to ENAMETOOLONG, pretending that the underlying mkstemp() choked on a temporary file path that is too long. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index cd6b0c4e0..a5fc56bda 100644 --- a/diff.c +++ b/diff.c @@ -1695,7 +1695,7 @@ static void prep_temp_blob(struct diff_tempfile *temp, fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX"); if (fd < 0) - die("unable to create temp-file"); + die("unable to create temp-file: %s", strerror(errno)); if (write_in_full(fd, blob, size) != size) die("unable to write temp-file"); close(fd); -- cgit v1.2.1 From fb13227e089f22dc31a3b1624559153821056848 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 13:33:31 -0700 Subject: git-diff: squelch "empty" diffs After starting to edit a working tree file but later when your edit ends up identical to the original (this can also happen when you ran a wholesale regexp replace with something like "perl -i" that does not actually modify many of the paths), "git diff" between the index and the working tree outputs many "empty" diffs that show "diff --git" headers and nothing else, because these paths are stat-dirty. While it was a way to warn the user that the earlier action of the user made the index ineffective as an optimization mechanism, it was felt too loud for the purpose of warning even to experienced users, and also resulted in confusing people new to git. This replaces the "empty" diffs with a single warning message at the end. Having many such paths hurts performance, and you can run "git-update-index --refresh" to update the lstat(2) information recorded in the index in such a case. "git-status" does so as a side effect, and that is more familiar to the end-user, so we recommend it to them. The change affects only "git diff" that outputs patch text, because that is where the annoyance of too many "empty" diff is most strongly felt, and because the warning message can be safely ignored by downstream tools without getting mistaken as part of the patch. For the low-level "git diff-files" and "git diff-index", the traditional behaviour is retained. Signed-off-by: Junio C Hamano --- diff.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a5fc56bda..f884de77a 100644 --- a/diff.c +++ b/diff.c @@ -3143,11 +3143,63 @@ static void diffcore_apply_filter(const char *filter) *q = outq; } +static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) +{ + int i; + struct diff_queue_struct *q = &diff_queued_diff; + struct diff_queue_struct outq; + outq.queue = NULL; + outq.nr = outq.alloc = 0; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + + /* + * 1. Entries that come from stat info dirtyness + * always have both sides (iow, not create/delete), + * one side of the object name is unknown, with + * the same mode and size. Keep the ones that + * do not match these criteria. They have real + * differences. + * + * 2. At this point, the file is known to be modified, + * with the same mode and size, and the object + * name of one side is unknown. Need to inspect + * the identical contents. + */ + if (!DIFF_FILE_VALID(p->one) || /* (1) */ + !DIFF_FILE_VALID(p->two) || + (p->one->sha1_valid && p->two->sha1_valid) || + (p->one->mode != p->two->mode) || + diff_populate_filespec(p->one, 1) || + diff_populate_filespec(p->two, 1) || + (p->one->size != p->two->size) || + + diff_populate_filespec(p->one, 0) || /* (2) */ + diff_populate_filespec(p->two, 0) || + memcmp(p->one->data, p->two->data, p->one->size)) + diff_q(&outq, p); + else { + /* + * The caller can subtract 1 from skip_stat_unmatch + * to determine how many paths were dirty only + * due to stat info mismatch. + */ + diffopt->skip_stat_unmatch++; + diff_free_filepair(p); + } + } + free(q->queue); + *q = outq; +} + void diffcore_std(struct diff_options *options) { if (options->quiet) return; + if (options->skip_stat_unmatch && !options->find_copies_harder) + diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) diffcore_break(options->break_opt); if (options->detect_rename) -- cgit v1.2.1 From 6d2d9e8666ef72c8878af4822e243ca33b6752a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Wed, 15 Aug 2007 00:41:00 +0200 Subject: diff: squelch empty diffs even more When we compare two non-tracked files, or explicitly specify --no-index, the suggestion to run git-status is not helpful. The patch adds a new diff_options bitfield member, no_index, that is used instead of the special value of -2 of the rev_info field max_count to indicate that the index is not to be used. This makes it possible to pass that flag down to diffcore_skip_stat_unmatch(), which only has one diff_options parameter. This could even become a cleanup if we removed all assignments of max_count to a value of -2 (viz. replacement of a magic value with a self-documenting field name) but I didn't dare to do that so late in the rc game.. The no_index bit, if set, then tells diffcore_skip_stat_unmatch() to not account for any skipped stat-mismatches, which avoids the suggestion to run git-status. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f884de77a..97cc5bc08 100644 --- a/diff.c +++ b/diff.c @@ -3185,7 +3185,8 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) * to determine how many paths were dirty only * due to stat info mismatch. */ - diffopt->skip_stat_unmatch++; + if (!diffopt->no_index) + diffopt->skip_stat_unmatch++; diff_free_filepair(p); } } -- cgit v1.2.1 From 2f82f760e1b2630073a05c44fe70bdce9706d9e5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 18 Aug 2007 15:52:55 -0700 Subject: Take binary diffs into account for "git rebase" We used to not generate a patch ID for binary diffs, but that means that some commits may be skipped as being identical to already-applied diffs when doing a rebase. So just delete the code that skips the binary diff. At the very least, we'd want the filenames to be part of the patch ID, but we might also want to generate some hash for the binary diff itself too. This fixes an issue noticed by Torgil Svensson. Tested-by: Torgil Svensson Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 97cc5bc08..a7e76719d 100644 --- a/diff.c +++ b/diff.c @@ -2919,10 +2919,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) fill_mmfile(&mf2, p->two) < 0) return error("unable to read files to diff"); - /* Maybe hash p->two? into the patch id? */ - if (diff_filespec_is_binary(p->two)) - continue; - len1 = remove_space(p->one->path, strlen(p->one->path)); len2 = remove_space(p->two->path, strlen(p->two->path)); if (p->one->mode == 0) -- cgit v1.2.1 From aecbf914c43ab76e055fa8a25b12456ae74d91bc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 31 Aug 2007 13:13:42 -0700 Subject: git-diff: resurrect the traditional empty "diff --git" behaviour The warning message to suggest "Consider running git-status" from "git-diff" that we experimented with during the 1.5.3 cycle turns out to be a bad idea. It robbed cache-dirty information from people who valued it, while still asking users to run "update-index --refresh". It was hoped that the new behaviour would at least have some educational value, but not showing the cache-dirty paths like before meant that the user would not even know easily which paths were cache-dirty, and it made the need to refresh the index look like even more unnecessary chore. This commit reinstates the traditional behaviour, but with a twist. By default, the empty "diff --git" output is totally squelched out from "git diff" output. At the end of the command, it automatically runs "update-index --refresh" as needed, without even bothering the user. In other words, people who do not care about the cache-dirtyness do not even have to see the warning. The traditional behaviour to see the stat-dirty output and to bypassing the overhead of content comparison can be specified by setting the configuration variable diff.autorefreshindex to false. Signed-off-by: Junio C Hamano --- diff.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a7e76719d..0d30d0526 100644 --- a/diff.c +++ b/diff.c @@ -19,6 +19,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = -1; static int diff_use_color_default; +int diff_auto_refresh_index = 1; static char diff_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ @@ -166,6 +167,10 @@ int git_diff_ui_config(const char *var, const char *value) diff_detect_rename_default = DIFF_DETECT_RENAME; return 0; } + if (!strcmp(var, "diff.autorefreshindex")) { + diff_auto_refresh_index = git_config_bool(var, value); + return 0; + } if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); -- cgit v1.2.1 From af6eb82262e35687aa8f00d688e327cb845973fa Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:09 +0200 Subject: Use strbuf API in apply, blame, commit-tree and diff Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- diff.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0d30d0526..c054b234b 100644 --- a/diff.c +++ b/diff.c @@ -9,6 +9,7 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" +#include "strbuf.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1545,26 +1546,16 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { -#define INCREMENT 1024 - char *buf; - unsigned long size; - ssize_t got; - - size = 0; - buf = NULL; - while (1) { - buf = xrealloc(buf, size + INCREMENT); - got = xread(0, buf + size, INCREMENT); - if (!got) - break; /* EOF */ - if (got < 0) - return error("error while reading from stdin %s", + struct strbuf buf; + + strbuf_init(&buf); + if (strbuf_read(&buf, 0) < 0) + return error("error while reading from stdin %s", strerror(errno)); - size += got; - } + s->should_munmap = 0; - s->data = buf; - s->size = size; + s->size = buf.len; + s->data = strbuf_detach(&buf); s->should_free = 1; return 0; } -- cgit v1.2.1 From 5701115aa7cfe7edd57c2483085456a37e27a5ba Mon Sep 17 00:00:00 2001 From: Sven Verdoolaege Date: Sat, 8 Sep 2007 12:30:22 +0200 Subject: git-diff: don't squelch the new SHA1 in submodule diffs The code to squelch empty diffs introduced by commit fb13227e089f22dc31a3b1624559153821056848 would inadvertently populate filespec "two" of a submodule change using the uninitialized (null) SHA1, thereby replacing the submodule SHA1 by 0{40} in the output. This change teaches diffcore_skip_stat_unmatch to handle submodule changes correctly. Signed-off-by: Sven Verdoolaege Signed-off-by: Junio C Hamano --- diff.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0d30d0526..1aca5df52 100644 --- a/diff.c +++ b/diff.c @@ -3144,6 +3144,22 @@ static void diffcore_apply_filter(const char *filter) *q = outq; } +/* Check whether two filespecs with the same mode and size are identical */ +static int diff_filespec_is_identical(struct diff_filespec *one, + struct diff_filespec *two) +{ + if (S_ISGITLINK(one->mode)) { + diff_fill_sha1_info(one); + diff_fill_sha1_info(two); + return !hashcmp(one->sha1, two->sha1); + } + if (diff_populate_filespec(one, 0)) + return 0; + if (diff_populate_filespec(two, 0)) + return 0; + return !memcmp(one->data, two->data, one->size); +} + static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) { int i; @@ -3175,10 +3191,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) diff_populate_filespec(p->one, 1) || diff_populate_filespec(p->two, 1) || (p->one->size != p->two->size) || - - diff_populate_filespec(p->one, 0) || /* (2) */ - diff_populate_filespec(p->two, 0) || - memcmp(p->one->data, p->two->data, p->one->size)) + !diff_filespec_is_identical(p->one, p->two)) /* (2) */ diff_q(&outq, p); else { /* -- cgit v1.2.1 From f1696ee398e92bcea3cdc7b3da85d8e0f77f6c50 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:04 +0200 Subject: Strbuf API extensions and fixes. * Add strbuf_rtrim to remove trailing spaces. * Add strbuf_insert to insert data at a given position. * Off-by one fix in strbuf_addf: strbuf_avail() does not counts the final \0 so the overflow test for snprintf is the strict comparison. This is not critical as the growth mechanism chosen will always allocate _more_ memory than asked, so the second test will not fail. It's some kind of miracle though. * Add size extension hints for strbuf_init and strbuf_read. If 0, default applies, else: + initial buffer has the given size for strbuf_init. + first growth checks it has at least this size rather than the default 8192. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 26d7bb96b..7290309ea 100644 --- a/diff.c +++ b/diff.c @@ -1548,8 +1548,8 @@ static int populate_from_stdin(struct diff_filespec *s) { struct strbuf buf; - strbuf_init(&buf); - if (strbuf_read(&buf, 0) < 0) + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 0) < 0) return error("error while reading from stdin %s", strerror(errno)); -- cgit v1.2.1 From 0024a54923a12f8c05ce4290f5ebefab0cce4336 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 14 Sep 2007 10:39:48 -0700 Subject: Fix the rename detection limit checking This adds more proper rename detection limits. Instead of just checking the limit against the number of potential rename destinations, we verify that the rename matrix (which is what really matters) doesn't grow ridiculously large, and we also make sure that we don't overflow when doing the matrix size calculation. This also changes the default limits from unlimited, to a rename matrix that is limited to 100 entries on a side. You can raise it with the config entry, or by using the "-l" command line flag, but at least the default is now a sane number that avoids spending lots of time (and memory) in situations that likely don't merit it. The choice of default value is of course very debatable. Limiting the rename matrix to a 100x100 size will mean that even if you have just one obvious rename, but you also create (or delete) 10,000 files, the rename matrix will be so big that we disable the heuristics. Sounds reasonable to me, but let's see if people hit this (and, perhaps more importantly, actually *care*) in real life. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 1aca5df52..0ee9ea1c1 100644 --- a/diff.c +++ b/diff.c @@ -17,7 +17,7 @@ #endif static int diff_detect_rename_default; -static int diff_rename_limit_default = -1; +static int diff_rename_limit_default = 100; static int diff_use_color_default; int diff_auto_refresh_index = 1; -- cgit v1.2.1 From 5ecd293d1420bf641a927a015877950f4d79c067 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 15:51:04 +0200 Subject: Rewrite convert_to_{git,working_tree} to use strbuf's. * Now, those functions take an "out" strbuf argument, where they store their result if any. In that case, it also returns 1, else it returns 0. * those functions support "in place" editing, in the sense that it's OK to call them this way: convert_to_git(path, sb->buf, sb->len, sb); When doable, conversions are done in place for real, else the strbuf content is just replaced with the new one, transparentely for the caller. If you want to create a new filter working this way, being the accumulation of filter1, filter2, ... filtern, then your meta_filter would be: int meta_filter(..., const char *src, size_t len, struct strbuf *sb) { int ret = 0; ret |= filter1(...., src, len, sb); if (ret) { src = sb->buf; len = sb->len; } ret |= filter2(...., src, len, sb); if (ret) { src = sb->buf; len = sb->len; } .... return ret | filtern(..., src, len, sb); } That's why subfilters the convert_to_* functions called were also rewritten to work this way. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7290309ea..f41bcd94a 100644 --- a/diff.c +++ b/diff.c @@ -1600,10 +1600,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (!s->sha1_valid || reuse_worktree_file(s->path, s->sha1, 0)) { + struct strbuf buf; struct stat st; int fd; - char *buf; - unsigned long size; if (!strcmp(s->path, "-")) return populate_from_stdin(s); @@ -1644,13 +1643,12 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) /* * Convert from working tree format to canonical git format */ - size = s->size; - buf = convert_to_git(s->path, s->data, &size); - if (buf) { + strbuf_init(&buf, 0); + if (convert_to_git(s->path, s->data, s->size, &buf)) { munmap(s->data, s->size); s->should_munmap = 0; - s->data = buf; - s->size = size; + s->data = buf.buf; + s->size = buf.len; s->should_free = 1; } } -- cgit v1.2.1 From ba3ed09728cb25e004d3b732de14fca8aeb602f6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 15 Sep 2007 15:56:50 +0200 Subject: Now that cache.h needs strbuf.h, remove useless includes. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 1 - 1 file changed, 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f41bcd94a..56b672c4f 100644 --- a/diff.c +++ b/diff.c @@ -9,7 +9,6 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" -#include "strbuf.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 -- cgit v1.2.1 From 182af8343c307436bb5364309aa6d4d46fa5911d Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 00:32:36 +0200 Subject: Use xmemdupz() in many places. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- diff.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a5b69ed2d..2216d756d 100644 --- a/diff.c +++ b/diff.c @@ -83,13 +83,8 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) break; if (!drv) { - char *namebuf; drv = xcalloc(1, sizeof(struct ll_diff_driver)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - drv->name = namebuf; - drv->next = NULL; + drv->name = xmemdupz(name, namelen); if (!user_diff_tail) user_diff_tail = &user_diff; *user_diff_tail = drv; @@ -126,12 +121,8 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v if (!strncmp(pp->name, name, namelen) && !pp->name[namelen]) break; if (!pp) { - char *namebuf; pp = xcalloc(1, sizeof(*pp)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - pp->name = namebuf; + pp->name = xmemdupz(name, namelen); pp->next = funcname_pattern_list; funcname_pattern_list = pp; } -- cgit v1.2.1 From 663af3422a648e87945e4d8c0cc3e13671f2bbde Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:15 +0200 Subject: Full rework of quote_c_style and write_name_quoted. * quote_c_style works on a strbuf instead of a wild buffer. * quote_c_style is now clever enough to not add double quotes if not needed. * write_name_quoted inherits those advantages, but also take a different set of arguments. Now instead of asking for quotes or not, you pass a "terminator". If it's \0 then we assume you don't want to escape, else C escaping is performed. In any case, the terminator is also appended to the stream. It also no longer takes the prefix/prefix_len arguments, as it's seldomly used, and makes some optimizations harder. * write_name_quotedpfx is created to work like write_name_quoted and take the prefix/prefix_len arguments. Thanks to those API changes, diff.c has somehow lost weight, thanks to the removal of functions that were wrappers around the old write_name_quoted trying to give it a semantics like the new one, but performing a lot of allocations for this goal. Now we always write directly to the stream, no intermediate allocation is performed. As a side effect of the refactor in builtin-apply.c, the length of the bar graphs in diffstats are not affected anymore by the fact that the path was clipped. Signed-off-by: Pierre Habouzit --- diff.c | 303 ++++++++++++++++++++++++----------------------------------------- 1 file changed, 110 insertions(+), 193 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 2216d756d..fb6d077f0 100644 --- a/diff.c +++ b/diff.c @@ -181,44 +181,23 @@ int git_diff_ui_config(const char *var, const char *value) return git_default_config(var, value); } -static char *quote_one(const char *str) -{ - int needlen; - char *xp; - - if (!str) - return NULL; - needlen = quote_c_style(str, NULL, NULL, 0); - if (!needlen) - return xstrdup(str); - xp = xmalloc(needlen + 1); - quote_c_style(str, xp, NULL, 0); - return xp; -} - static char *quote_two(const char *one, const char *two) { int need_one = quote_c_style(one, NULL, NULL, 1); int need_two = quote_c_style(two, NULL, NULL, 1); - char *xp; + struct strbuf res; + strbuf_init(&res, 0); if (need_one + need_two) { - if (!need_one) need_one = strlen(one); - if (!need_two) need_one = strlen(two); - - xp = xmalloc(need_one + need_two + 3); - xp[0] = '"'; - quote_c_style(one, xp + 1, NULL, 1); - quote_c_style(two, xp + need_one + 1, NULL, 1); - strcpy(xp + need_one + need_two + 1, "\""); - return xp; + strbuf_addch(&res, '"'); + quote_c_style(one, &res, NULL, 1); + quote_c_style(two, &res, NULL, 1); + strbuf_addch(&res, '"'); + } else { + strbuf_addstr(&res, one); + strbuf_addstr(&res, two); } - need_one = strlen(one); - need_two = strlen(two); - xp = xmalloc(need_one + need_two + 1); - strcpy(xp, one); - strcpy(xp + need_one, two); - return xp; + return res.buf; } static const char *external_diff(void) @@ -670,27 +649,20 @@ static char *pprint_rename(const char *a, const char *b) { const char *old = a; const char *new = b; - char *name = NULL; + struct strbuf name; int pfx_length, sfx_length; int len_a = strlen(a); int len_b = strlen(b); + int a_midlen, b_midlen; int qlen_a = quote_c_style(a, NULL, NULL, 0); int qlen_b = quote_c_style(b, NULL, NULL, 0); + strbuf_init(&name, 0); if (qlen_a || qlen_b) { - if (qlen_a) len_a = qlen_a; - if (qlen_b) len_b = qlen_b; - name = xmalloc( len_a + len_b + 5 ); - if (qlen_a) - quote_c_style(a, name, NULL, 0); - else - memcpy(name, a, len_a); - memcpy(name + len_a, " => ", 4); - if (qlen_b) - quote_c_style(b, name + len_a + 4, NULL, 0); - else - memcpy(name + len_a + 4, b, len_b + 1); - return name; + quote_c_style(a, &name, NULL, 0); + strbuf_addstr(&name, " => "); + quote_c_style(b, &name, NULL, 0); + return name.buf; } /* Find common prefix */ @@ -719,24 +691,26 @@ static char *pprint_rename(const char *a, const char *b) * pfx{sfx-a => sfx-b} * name-a => name-b */ + a_midlen = len_a - pfx_length - sfx_length; + b_midlen = len_b - pfx_length - sfx_length; + if (a_midlen < 0) + a_midlen = 0; + if (b_midlen < 0) + b_midlen = 0; + + strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7); if (pfx_length + sfx_length) { - int a_midlen = len_a - pfx_length - sfx_length; - int b_midlen = len_b - pfx_length - sfx_length; - if (a_midlen < 0) a_midlen = 0; - if (b_midlen < 0) b_midlen = 0; - - name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7); - sprintf(name, "%.*s{%.*s => %.*s}%s", - pfx_length, a, - a_midlen, a + pfx_length, - b_midlen, b + pfx_length, - a + len_a - sfx_length); + strbuf_add(&name, a, pfx_length); + strbuf_addch(&name, '{'); } - else { - name = xmalloc(len_a + len_b + 5); - sprintf(name, "%s => %s", a, b); + strbuf_add(&name, a + pfx_length, a_midlen); + strbuf_addstr(&name, " => "); + strbuf_add(&name, b + pfx_length, b_midlen); + if (pfx_length + sfx_length) { + strbuf_addch(&name, '}'); + strbuf_add(&name, a + len_a - sfx_length, sfx_length); } - return name; + return name.buf; } struct diffstat_t { @@ -849,12 +823,13 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) int change = file->added + file->deleted; if (!file->is_renamed) { /* renames are already quoted by pprint_rename */ - len = quote_c_style(file->name, NULL, NULL, 0); - if (len) { - char *qname = xmalloc(len + 1); - quote_c_style(file->name, qname, NULL, 0); + struct strbuf buf; + strbuf_init(&buf, 0); + if (quote_c_style(file->name, &buf, NULL, 0)) { free(file->name); - file->name = qname; + file->name = buf.buf; + } else { + strbuf_release(&buf); } } @@ -992,12 +967,12 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) printf("-\t-\t"); else printf("%d\t%d\t", file->added, file->deleted); - if (options->line_termination && !file->is_renamed && - quote_c_style(file->name, NULL, NULL, 0)) - quote_c_style(file->name, NULL, stdout, 0); - else + if (!file->is_renamed) { + write_name_quoted(file->name, stdout, options->line_termination); + } else { fputs(file->name, stdout); - putchar(options->line_termination); + putchar(options->line_termination); + } } } @@ -1941,50 +1916,46 @@ static int similarity_index(struct diff_filepair *p) static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); - char msg[PATH_MAX*2+300], *xfrm_msg; - struct diff_filespec *one; - struct diff_filespec *two; + struct strbuf msg; + char *xfrm_msg; + struct diff_filespec *one = p->one; + struct diff_filespec *two = p->two; const char *name; const char *other; - char *name_munged, *other_munged; int complete_rewrite = 0; - int len; + if (DIFF_PAIR_UNMERGED(p)) { - /* unmerged */ run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0); return; } - name = p->one->path; + name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); - name_munged = quote_one(name); - other_munged = quote_one(other); - one = p->one; two = p->two; - diff_fill_sha1_info(one); diff_fill_sha1_info(two); - len = 0; + strbuf_init(&msg, PATH_MAX * 2 + 300); switch (p->status) { case DIFF_STATUS_COPIED: - len += snprintf(msg + len, sizeof(msg) - len, - "similarity index %d%%\n" - "copy from %s\n" - "copy to %s\n", - similarity_index(p), name_munged, other_munged); + strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(&msg, "\ncopy from "); + quote_c_style(name, &msg, NULL, 0); + strbuf_addstr(&msg, "\ncopy to "); + quote_c_style(other, &msg, NULL, 0); + strbuf_addch(&msg, '\n'); break; case DIFF_STATUS_RENAMED: - len += snprintf(msg + len, sizeof(msg) - len, - "similarity index %d%%\n" - "rename from %s\n" - "rename to %s\n", - similarity_index(p), name_munged, other_munged); + strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(&msg, "\nrename from "); + quote_c_style(name, &msg, NULL, 0); + strbuf_addstr(&msg, "\nrename to "); + quote_c_style(other, &msg, NULL, 0); + strbuf_addch(&msg, '\n'); break; case DIFF_STATUS_MODIFIED: if (p->score) { - len += snprintf(msg + len, sizeof(msg) - len, - "dissimilarity index %d%%\n", + strbuf_addf(&msg, "dissimilarity index %d%%\n", similarity_index(p)); complete_rewrite = 1; break; @@ -2004,19 +1975,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) abbrev = 40; } - len += snprintf(msg + len, sizeof(msg) - len, - "index %.*s..%.*s", + strbuf_addf(&msg, "index %.*s..%.*s", abbrev, sha1_to_hex(one->sha1), abbrev, sha1_to_hex(two->sha1)); if (one->mode == two->mode) - len += snprintf(msg + len, sizeof(msg) - len, - " %06o", one->mode); - len += snprintf(msg + len, sizeof(msg) - len, "\n"); + strbuf_addf(&msg, " %06o", one->mode); + strbuf_addch(&msg, '\n'); } - if (len) - msg[--len] = 0; - xfrm_msg = len ? msg : NULL; + if (msg.len) + strbuf_setlen(&msg, msg.len - 1); + xfrm_msg = msg.len ? msg.buf : NULL; if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && @@ -2035,8 +2004,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o, complete_rewrite); - free(name_munged); - free(other_munged); + strbuf_release(&msg); } static void run_diffstat(struct diff_filepair *p, struct diff_options *o, @@ -2492,72 +2460,30 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len) return sha1_to_hex(sha1); } -static void diff_flush_raw(struct diff_filepair *p, - struct diff_options *options) +static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) { - int two_paths; - char status[10]; - int abbrev = options->abbrev; - const char *path_one, *path_two; - int inter_name_termination = '\t'; - int line_termination = options->line_termination; - - if (!line_termination) - inter_name_termination = 0; + int line_termination = opt->line_termination; + int inter_name_termination = line_termination ? '\t' : '\0'; - path_one = p->one->path; - path_two = p->two->path; - if (line_termination) { - path_one = quote_one(path_one); - path_two = quote_one(path_two); + if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) { + printf(":%06o %06o %s ", p->one->mode, p->two->mode, + diff_unique_abbrev(p->one->sha1, opt->abbrev)); + printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev)); } - - if (p->score) - sprintf(status, "%c%03d", p->status, similarity_index(p)); - else { - status[0] = p->status; - status[1] = 0; - } - switch (p->status) { - case DIFF_STATUS_COPIED: - case DIFF_STATUS_RENAMED: - two_paths = 1; - break; - case DIFF_STATUS_ADDED: - case DIFF_STATUS_DELETED: - two_paths = 0; - break; - default: - two_paths = 0; - break; - } - if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) { - printf(":%06o %06o %s ", - p->one->mode, p->two->mode, - diff_unique_abbrev(p->one->sha1, abbrev)); - printf("%s ", - diff_unique_abbrev(p->two->sha1, abbrev)); + if (p->score) { + printf("%c%03d%c", p->status, similarity_index(p), + inter_name_termination); + } else { + printf("%c%c", p->status, inter_name_termination); } - printf("%s%c%s", status, inter_name_termination, - two_paths || p->one->mode ? path_one : path_two); - if (two_paths) - printf("%c%s", inter_name_termination, path_two); - putchar(line_termination); - if (path_one != p->one->path) - free((void*)path_one); - if (path_two != p->two->path) - free((void*)path_two); -} -static void diff_flush_name(struct diff_filepair *p, struct diff_options *opt) -{ - char *path = p->two->path; - - if (opt->line_termination) - path = quote_one(p->two->path); - printf("%s%c", path, opt->line_termination); - if (p->two->path != path) - free(path); + if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) { + write_name_quoted(p->one->path, stdout, inter_name_termination); + write_name_quoted(p->two->path, stdout, line_termination); + } else { + const char *path = p->one->mode ? p->one->path : p->two->path; + write_name_quoted(path, stdout, line_termination); + } } int diff_unmodified_pair(struct diff_filepair *p) @@ -2567,14 +2493,11 @@ int diff_unmodified_pair(struct diff_filepair *p) * let transformers to produce diff_filepairs any way they want, * and filter and clean them up here before producing the output. */ - struct diff_filespec *one, *two; + struct diff_filespec *one = p->one, *two = p->two; if (DIFF_PAIR_UNMERGED(p)) return 0; /* unmerged is interesting */ - one = p->one; - two = p->two; - /* deletion, addition, mode or type change * and rename are all interesting. */ @@ -2763,32 +2686,27 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) diff_flush_raw(p, opt); else if (fmt & DIFF_FORMAT_NAME) - diff_flush_name(p, opt); + write_name_quoted(p->two->path, stdout, opt->line_termination); } static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) { - char *name = quote_one(fs->path); if (fs->mode) - printf(" %s mode %06o %s\n", newdelete, fs->mode, name); + printf(" %s mode %06o ", newdelete, fs->mode); else - printf(" %s %s\n", newdelete, name); - free(name); + printf(" %s ", newdelete); + write_name_quoted(fs->path, stdout, '\n'); } static void show_mode_change(struct diff_filepair *p, int show_name) { if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) { + printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode, + show_name ? ' ' : '\n'); if (show_name) { - char *name = quote_one(p->two->path); - printf(" mode change %06o => %06o %s\n", - p->one->mode, p->two->mode, name); - free(name); + write_name_quoted(p->two->path, stdout, '\n'); } - else - printf(" mode change %06o => %06o\n", - p->one->mode, p->two->mode); } } @@ -2818,12 +2736,11 @@ static void diff_summary(struct diff_filepair *p) break; default: if (p->score) { - char *name = quote_one(p->two->path); - printf(" rewrite %s (%d%%)\n", name, - similarity_index(p)); - free(name); - show_mode_change(p, 0); - } else show_mode_change(p, 1); + puts(" rewrite "); + write_name_quoted(p->two->path, stdout, ' '); + printf("(%d%%)\n", similarity_index(p)); + } + show_mode_change(p, !p->score); break; } } @@ -2837,14 +2754,14 @@ struct patch_id_t { static int remove_space(char *line, int len) { int i; - char *dst = line; - unsigned char c; + char *dst = line; + unsigned char c; - for (i = 0; i < len; i++) - if (!isspace((c = line[i]))) - *dst++ = c; + for (i = 0; i < len; i++) + if (!isspace((c = line[i]))) + *dst++ = c; - return dst - line; + return dst - line; } static void patch_id_consume(void *priv, char *line, unsigned long len) -- cgit v1.2.1 From b315c5c08139c0d3c1e4867a305334e29da01d07 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 12:58:23 +0200 Subject: strbuf change: be sure ->buf is never ever NULL. For that purpose, the ->buf is always initialized with a char * buf living in the strbuf module. It is made a char * so that we can sloppily accept things that perform: sb->buf[0] = '\0', and because you can't pass "" as an initializer for ->buf without making gcc unhappy for very good reasons. strbuf_init/_detach/_grow have been fixed to trust ->alloc and not ->buf anymore. as a consequence strbuf_detach is _mandatory_ to detach a buffer, copying ->buf isn't an option anymore, if ->buf is going to escape from the scope, and eventually be free'd. API changes: * strbuf_setlen now always works, so just make strbuf_reset a convenience macro. * strbuf_detatch takes a size_t* optional argument (meaning it can be NULL) to copy the buffer's len, as it was needed for this refactor to make the code more readable, and working like the callers. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- diff.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index fb6d077f0..ab575191d 100644 --- a/diff.c +++ b/diff.c @@ -197,7 +197,7 @@ static char *quote_two(const char *one, const char *two) strbuf_addstr(&res, one); strbuf_addstr(&res, two); } - return res.buf; + return strbuf_detach(&res, NULL); } static const char *external_diff(void) @@ -662,7 +662,7 @@ static char *pprint_rename(const char *a, const char *b) quote_c_style(a, &name, NULL, 0); strbuf_addstr(&name, " => "); quote_c_style(b, &name, NULL, 0); - return name.buf; + return strbuf_detach(&name, NULL); } /* Find common prefix */ @@ -710,7 +710,7 @@ static char *pprint_rename(const char *a, const char *b) strbuf_addch(&name, '}'); strbuf_add(&name, a + len_a - sfx_length, sfx_length); } - return name.buf; + return strbuf_detach(&name, NULL); } struct diffstat_t { @@ -827,7 +827,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) strbuf_init(&buf, 0); if (quote_c_style(file->name, &buf, NULL, 0)) { free(file->name); - file->name = buf.buf; + file->name = strbuf_detach(&buf, NULL); } else { strbuf_release(&buf); } @@ -1519,8 +1519,7 @@ static int populate_from_stdin(struct diff_filespec *s) strerror(errno)); s->should_munmap = 0; - s->size = buf.len; - s->data = strbuf_detach(&buf); + s->data = strbuf_detach(&buf, &s->size); s->should_free = 1; return 0; } @@ -1612,8 +1611,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (convert_to_git(s->path, s->data, s->size, &buf)) { munmap(s->data, s->size); s->should_munmap = 0; - s->data = buf.buf; - s->size = buf.len; + s->data = strbuf_detach(&buf, &s->size); s->should_free = 1; } } -- cgit v1.2.1 From eede7b7d110e2c354235d7a3f6c8f1644b5120e5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 25 Sep 2007 15:29:42 -0400 Subject: diffcore-rename: cache file deltas We find rename candidates by computing a fingerprint hash of each file, and then comparing those fingerprints. There are inherently O(n^2) comparisons, so it pays in CPU time to hoist the (rather expensive) computation of the fingerprint out of that loop (or to cache it once we have computed it once). Previously, we didn't keep the filespec information around because then we had the potential to consume a great deal of memory. However, instead of keeping all of the filespec data, we can instead just keep the fingerprint. This patch implements and uses diff_free_filespec_data_large to accomplish that goal. We also have to change estimate_similarity not to needlessly repopulate the filespec data when we already have the hash. Practical tests showed 4.5x speedup for a 10% memory usage increase. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0ee9ea1c1..35e3c6198 100644 --- a/diff.c +++ b/diff.c @@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) return 0; } -void diff_free_filespec_data(struct diff_filespec *s) +void diff_free_filespec_data_large(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1686,6 +1686,11 @@ void diff_free_filespec_data(struct diff_filespec *s) s->should_free = s->should_munmap = 0; s->data = NULL; } +} + +void diff_free_filespec_data(struct diff_filespec *s) +{ + diff_free_filespec_data_large(s); free(s->cnt_data); s->cnt_data = NULL; } -- cgit v1.2.1 From 8ae92e63895000ff9b12046325ae381f3c17d414 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 21:01:03 -0700 Subject: rename diff_free_filespec_data_large() to diff_free_filespec_blob() Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 35e3c6198..71b340c53 100644 --- a/diff.c +++ b/diff.c @@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) return 0; } -void diff_free_filespec_data_large(struct diff_filespec *s) +void diff_free_filespec_blob(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1690,7 +1690,7 @@ void diff_free_filespec_data_large(struct diff_filespec *s) void diff_free_filespec_data(struct diff_filespec *s) { - diff_free_filespec_data_large(s); + diff_free_filespec_blob(s); free(s->cnt_data); s->cnt_data = NULL; } -- cgit v1.2.1 From d5535ec75ce1cdf57ef52b57c58c548121ce99ba Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:56 +0200 Subject: Use run_command() to spawn external diff programs instead of fork/exec. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- diff.c | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6648e0152..30b754417 100644 --- a/diff.c +++ b/diff.c @@ -9,6 +9,7 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" +#include "run-command.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1748,40 +1749,6 @@ static void remove_tempfile_on_signal(int signo) raise(signo); } -static int spawn_prog(const char *pgm, const char **arg) -{ - pid_t pid; - int status; - - fflush(NULL); - pid = fork(); - if (pid < 0) - die("unable to fork"); - if (!pid) { - execvp(pgm, (char *const*) arg); - exit(255); - } - - while (waitpid(pid, &status, 0) < 0) { - if (errno == EINTR) - continue; - return -1; - } - - /* Earlier we did not check the exit status because - * diff exits non-zero if files are different, and - * we are not interested in knowing that. It was a - * mistake which made it harder to quit a diff-* - * session that uses the git-apply-patch-script as - * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF - * should also exit non-zero only when it wants to - * abort the entire diff-* session. - */ - if (WIFEXITED(status) && !WEXITSTATUS(status)) - return 0; - return -1; -} - /* An external diff command takes: * * diff-cmd name infile1 infile1-sha1 infile1-mode \ @@ -1834,7 +1801,8 @@ static void run_external_diff(const char *pgm, *arg++ = name; } *arg = NULL; - retval = spawn_prog(pgm, spawn_arg); + fflush(NULL); + retval = run_command_v_opt(spawn_arg, 0); remove_tempfile(); if (retval) { fprintf(stderr, "external diff died, stopping at %s.\n", name); -- cgit v1.2.1 From c32f749fec69f92ce3b076128e6322f8130bd881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 21 Oct 2007 11:23:49 +0200 Subject: Correct some sizeof(size_t) != sizeof(unsigned long) typing errors Fix size_t vs. unsigned long pointer mismatch warnings introduced with the addition of strbuf_detach(). Signed-off-by: Rene Scharfe Signed-off-by: Shawn O. Pearce --- diff.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6648e0152..dfb8595b7 100644 --- a/diff.c +++ b/diff.c @@ -1512,6 +1512,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { struct strbuf buf; + size_t size = 0; strbuf_init(&buf, 0); if (strbuf_read(&buf, 0, 0) < 0) @@ -1519,7 +1520,8 @@ static int populate_from_stdin(struct diff_filespec *s) strerror(errno)); s->should_munmap = 0; - s->data = strbuf_detach(&buf, &s->size); + s->data = strbuf_detach(&buf, &size); + s->size = size; s->should_free = 1; return 0; } @@ -1609,9 +1611,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) */ strbuf_init(&buf, 0); if (convert_to_git(s->path, s->data, s->size, &buf)) { + size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; - s->data = strbuf_detach(&buf, &s->size); + s->data = strbuf_detach(&buf, &size); + s->size = size; s->should_free = 1; } } -- cgit v1.2.1 From 9fb88419ba85e641006c80db53620423f37f1c93 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:19:10 -0700 Subject: Ref-count the filespecs used by diffcore Rather than copy the filespecs when introducing new versions of them (for rename or copy detection), use a refcount and increment the count when reusing the diff_filespec. This avoids unnecessary allocations, but the real reason behind this is a future enhancement: we will want to track shared data across the copy/rename detection. In order to efficiently notice when a filespec is used by a rename, the rename machinery wants to keep track of a rename usage count which is shared across all different users of the filespec. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dfb8595b7..0b320f6b9 100644 --- a/diff.c +++ b/diff.c @@ -1440,9 +1440,18 @@ struct diff_filespec *alloc_filespec(const char *path) memset(spec, 0, sizeof(*spec)); spec->path = (char *)(spec + 1); memcpy(spec->path, path, namelen+1); + spec->count = 1; return spec; } +void free_filespec(struct diff_filespec *spec) +{ + if (!--spec->count) { + diff_free_filespec_data(spec); + free(spec); + } +} + void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, unsigned short mode) { @@ -2435,10 +2444,8 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue, void diff_free_filepair(struct diff_filepair *p) { - diff_free_filespec_data(p->one); - diff_free_filespec_data(p->two); - free(p->one); - free(p->two); + free_filespec(p->one); + free_filespec(p->two); free(p); } -- cgit v1.2.1 From 644797119d7a3b7a043a51a9cccd8758f8451f91 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:20:56 -0700 Subject: copy vs rename detection: avoid unnecessary O(n*m) loops The core rename detection had some rather stupid code to check if a pathname was used by a later modification or rename, which basically walked the whole pathname space for all renames for each rename, in order to tell whether it was a pure rename (no remaining users) or should be considered a copy (other users of the source file remaining). That's really silly, since we can just keep a count of users around, and replace all those complex and expensive loops with just testing that simple counter (but this all depends on the previous commit that shared the diff_filespec data structure by using a separate reference count). Note that the reference count is not the same as the rename count: they behave otherwise rather similarly, but the reference count is tied to the allocation (and decremented at de-allocation, so that when it turns zero we can get rid of the memory), while the rename count is tied to the renames and is decremented when we find a rename (so that when it turns zero we know that it was a rename, not a copy). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0b320f6b9..af85b94d1 100644 --- a/diff.c +++ b/diff.c @@ -2597,9 +2597,9 @@ void diff_debug_filepair(const struct diff_filepair *p, int i) { diff_debug_filespec(p->one, i, "one"); diff_debug_filespec(p->two, i, "two"); - fprintf(stderr, "score %d, status %c stays %d broken %d\n", + fprintf(stderr, "score %d, status %c rename_used %d broken %d\n", p->score, p->status ? p->status : '?', - p->source_stays, p->broken_pair); + p->one->rename_used, p->broken_pair); } void diff_debug_queue(const char *msg, struct diff_queue_struct *q) @@ -2617,8 +2617,8 @@ void diff_debug_queue(const char *msg, struct diff_queue_struct *q) static void diff_resolve_rename_copy(void) { - int i, j; - struct diff_filepair *p, *pp; + int i; + struct diff_filepair *p; struct diff_queue_struct *q = &diff_queued_diff; diff_debug_queue("resolve-rename-copy", q); @@ -2640,27 +2640,21 @@ static void diff_resolve_rename_copy(void) * either in-place edit or rename/copy edit. */ else if (DIFF_PAIR_RENAME(p)) { - if (p->source_stays) { - p->status = DIFF_STATUS_COPIED; - continue; - } - /* See if there is some other filepair that - * copies from the same source as us. If so - * we are a copy. Otherwise we are either a - * copy if the path stays, or a rename if it - * does not, but we already handled "stays" case. + /* + * A rename might have re-connected a broken + * pair up, causing the pathnames to be the + * same again. If so, that's not a rename at + * all, just a modification.. + * + * Otherwise, see if this source was used for + * multiple renames, in which case we decrement + * the count, and call it a copy. */ - for (j = i + 1; j < q->nr; j++) { - pp = q->queue[j]; - if (strcmp(pp->one->path, p->one->path)) - continue; /* not us */ - if (!DIFF_PAIR_RENAME(pp)) - continue; /* not a rename/copy */ - /* pp is a rename/copy from the same source */ + if (!strcmp(p->one->path, p->two->path)) + p->status = DIFF_STATUS_MODIFIED; + else if (--p->one->rename_used > 0) p->status = DIFF_STATUS_COPIED; - break; - } - if (!p->status) + else p->status = DIFF_STATUS_RENAMED; } else if (hashcmp(p->one->sha1, p->two->sha1) || -- cgit v1.2.1 From a9cc857ada7c57069ff00eed8d0addcf55849f39 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 00:24:27 -0700 Subject: War on whitespace: first, a bit of retreat. This introduces core.whitespace configuration variable that lets you specify the definition of "whitespace error". Currently there are two kinds of whitespace errors defined: * trailing-space: trailing whitespaces at the end of the line. * space-before-tab: a SP appears immediately before HT in the indent part of the line. You can specify the desired types of errors to be detected by listing their names (unique abbreviations are accepted) separated by comma. By default, these two errors are always detected, as that is the traditional behaviour. You can disable detection of a particular type of error by prefixing a '-' in front of the name of the error, like this: [core] whitespace = -trailing-space This patch teaches the code to output colored diff with DIFF_WHITESPACE color to highlight the detected whitespace errors to honor the new configuration. Signed-off-by: Junio C Hamano --- diff.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a6aaaf7e5..211235376 100644 --- a/diff.c +++ b/diff.c @@ -508,7 +508,8 @@ static void emit_line_with_ws(int nparents, for (i = col0; i < len; i++) { if (line[i] == '\t') { last_tab_in_indent = i; - if (0 <= last_space_in_indent) + if ((whitespace_rule & WS_SPACE_BEFORE_TAB) && + 0 <= last_space_in_indent) need_highlight_leading_space = 1; } else if (line[i] == ' ') @@ -540,10 +541,12 @@ static void emit_line_with_ws(int nparents, tail = len - 1; if (line[tail] == '\n' && i < tail) tail--; - while (i < tail) { - if (!isspace(line[tail])) - break; - tail--; + if (whitespace_rule & WS_TRAILING_SPACE) { + while (i < tail) { + if (!isspace(line[tail])) + break; + tail--; + } } if ((i < tail && line[tail + 1] != '\n')) { /* This has whitespace between tail+1..len */ -- cgit v1.2.1 From 459fa6d0fe6a45b8b120463b56a68e943e3a8101 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 18:00:27 -0700 Subject: git-diff: complain about >=8 consecutive spaces in initial indent This introduces a new whitespace error type, "indent-with-non-tab". The error is about starting a line with 8 or more SP, instead of indenting it with a HT. This is not enabled by default, as some projects employ an indenting policy to use only SPs and no HTs. The kernel folks and git contributors may want to enable this detection with: [core] whitespace = indent-with-non-tab Signed-off-by: Junio C Hamano --- diff.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 211235376..6bb902f4a 100644 --- a/diff.c +++ b/diff.c @@ -502,8 +502,11 @@ static void emit_line_with_ws(int nparents, int i; int tail = len; int need_highlight_leading_space = 0; - /* The line is a newly added line. Does it have funny leading - * whitespaces? In indent, SP should never precede a TAB. + /* + * The line is a newly added line. Does it have funny leading + * whitespaces? In indent, SP should never precede a TAB. In + * addition, under "indent with non tab" rule, there should not + * be more than 8 consecutive spaces. */ for (i = col0; i < len; i++) { if (line[i] == '\t') { @@ -517,6 +520,13 @@ static void emit_line_with_ws(int nparents, else break; } + if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && + 0 <= last_space_in_indent && + last_tab_in_indent < 0 && + 8 <= (i - col0)) { + last_tab_in_indent = i; + need_highlight_leading_space = 1; + } fputs(set, stdout); fwrite(line, col0, 1, stdout); fputs(reset, stdout); -- cgit v1.2.1 From 8f67f8aefb1b751073f8b36fae8be8f72eb93f4a Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 10 Nov 2007 20:05:14 +0100 Subject: Make the diff_options bitfields be an unsigned with explicit masks. reverse_diff was a bit-value in disguise, it's merged in the flags now. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- diff.c | 123 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 63 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a6aaaf7e5..67cce8b72 100644 --- a/diff.c +++ b/diff.c @@ -814,10 +814,10 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } /* Find the longest filename and max number of changes */ - reset = diff_get_color(options->color_diff, DIFF_RESET); - set = diff_get_color(options->color_diff, DIFF_PLAIN); - add_c = diff_get_color(options->color_diff, DIFF_FILE_NEW); - del_c = diff_get_color(options->color_diff, DIFF_FILE_OLD); + reset = diff_get_color_opt(options, DIFF_RESET); + set = diff_get_color_opt(options, DIFF_PLAIN); + add_c = diff_get_color_opt(options, DIFF_FILE_NEW); + del_c = diff_get_color_opt(options, DIFF_FILE_OLD); for (i = 0; i < data->nr; i++) { struct diffstat_file *file = data->files[i]; @@ -1243,8 +1243,8 @@ static void builtin_diff(const char *name_a, mmfile_t mf1, mf2; const char *lbl[2]; char *a_one, *b_two; - const char *set = diff_get_color(o->color_diff, DIFF_METAINFO); - const char *reset = diff_get_color(o->color_diff, DIFF_RESET); + const char *set = diff_get_color_opt(o, DIFF_METAINFO); + const char *reset = diff_get_color_opt(o, DIFF_RESET); a_one = quote_two("a/", name_a + (*name_a == '/')); b_two = quote_two("b/", name_b + (*name_b == '/')); @@ -1277,7 +1277,7 @@ static void builtin_diff(const char *name_a, goto free_ab_and_return; if (complete_rewrite) { emit_rewrite_diff(name_a, name_b, one, two, - o->color_diff); + DIFF_OPT_TST(o, COLOR_DIFF)); o->found_changes = 1; goto free_ab_and_return; } @@ -1286,13 +1286,13 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (!o->text && + if (!DIFF_OPT_TST(o, TEXT) && (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) { /* Quite common confusing case */ if (mf1.size == mf2.size && !memcmp(mf1.ptr, mf2.ptr, mf1.size)) goto free_ab_and_return; - if (o->binary) + if (DIFF_OPT_TST(o, BINARY)) emit_binary_diff(&mf1, &mf2); else printf("Binary files %s and %s differ\n", @@ -1315,7 +1315,7 @@ static void builtin_diff(const char *name_a, memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.label_path = lbl; - ecbdata.color_diff = o->color_diff; + ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); ecbdata.found_changesp = &o->found_changes; xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; @@ -1331,11 +1331,11 @@ static void builtin_diff(const char *name_a, ecb.outf = xdiff_outf; ecb.priv = &ecbdata; ecbdata.xm.consume = fn_out_consume; - if (o->color_diff_words) + if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); - if (o->color_diff_words) + if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1409,7 +1409,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; - data.color_diff = o->color_diff; + data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1853,7 +1853,7 @@ static void run_diff_cmd(const char *pgm, struct diff_options *o, int complete_rewrite) { - if (!o->allow_external) + if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { const char *cmd = external_diff_attr(name); @@ -1951,9 +1951,9 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) } if (hashcmp(one->sha1, two->sha1)) { - int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; + int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; - if (o->binary) { + if (DIFF_OPT_TST(o, BINARY)) { mmfile_t mf; if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) @@ -2045,7 +2045,10 @@ void diff_setup(struct diff_options *options) options->change = diff_change; options->add_remove = diff_addremove; - options->color_diff = diff_use_color_default; + if (diff_use_color_default) + DIFF_OPT_SET(options, COLOR_DIFF); + else + DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; } @@ -2064,7 +2067,7 @@ int diff_setup_done(struct diff_options *options) if (count > 1) die("--name-only, --name-status, --check and -s are mutually exclusive"); - if (options->find_copies_harder) + if (DIFF_OPT_TST(options, FIND_COPIES_HARDER)) options->detect_rename = DIFF_DETECT_COPY; if (options->output_format & (DIFF_FORMAT_NAME | @@ -2088,12 +2091,12 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_CHECKDIFF)) - options->recursive = 1; + DIFF_OPT_SET(options, RECURSIVE); /* * Also pickaxe would not work very well if you do not say recursive */ if (options->pickaxe) - options->recursive = 1; + DIFF_OPT_SET(options, RECURSIVE); if (options->detect_rename && options->rename_limit < 0) options->rename_limit = diff_rename_limit_default; @@ -2115,9 +2118,9 @@ int diff_setup_done(struct diff_options *options) * to have found. It does not make sense not to return with * exit code in such a case either. */ - if (options->quiet) { + if (DIFF_OPT_TST(options, QUIET)) { options->output_format = DIFF_FORMAT_NO_OUTPUT; - options->exit_with_status = 1; + DIFF_OPT_SET(options, EXIT_WITH_STATUS); } /* @@ -2125,7 +2128,7 @@ int diff_setup_done(struct diff_options *options) * upon the first hit. We need to run diff as usual. */ if (options->pickaxe || options->filter) - options->quiet = 0; + DIFF_OPT_CLR(options, QUIET); return 0; } @@ -2188,15 +2191,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_PATCH; else if (!strcmp(arg, "--raw")) options->output_format |= DIFF_FORMAT_RAW; - else if (!strcmp(arg, "--patch-with-raw")) { + else if (!strcmp(arg, "--patch-with-raw")) options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW; - } - else if (!strcmp(arg, "--numstat")) { + else if (!strcmp(arg, "--numstat")) options->output_format |= DIFF_FORMAT_NUMSTAT; - } - else if (!strcmp(arg, "--shortstat")) { + else if (!strcmp(arg, "--shortstat")) options->output_format |= DIFF_FORMAT_SHORTSTAT; - } else if (!prefixcmp(arg, "--stat")) { char *end; int width = options->stat_width; @@ -2228,33 +2228,30 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) options->output_format |= DIFF_FORMAT_SUMMARY; - else if (!strcmp(arg, "--patch-with-stat")) { + else if (!strcmp(arg, "--patch-with-stat")) options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; - } else if (!strcmp(arg, "-z")) options->line_termination = 0; else if (!prefixcmp(arg, "-l")) options->rename_limit = strtoul(arg+2, NULL, 10); else if (!strcmp(arg, "--full-index")) - options->full_index = 1; + DIFF_OPT_SET(options, FULL_INDEX); else if (!strcmp(arg, "--binary")) { options->output_format |= DIFF_FORMAT_PATCH; - options->binary = 1; - } - else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) { - options->text = 1; + DIFF_OPT_SET(options, BINARY); } + else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) + DIFF_OPT_SET(options, TEXT); else if (!strcmp(arg, "--name-only")) options->output_format |= DIFF_FORMAT_NAME; else if (!strcmp(arg, "--name-status")) options->output_format |= DIFF_FORMAT_NAME_STATUS; else if (!strcmp(arg, "-R")) - options->reverse_diff = 1; + DIFF_OPT_SET(options, REVERSE_DIFF); else if (!prefixcmp(arg, "-S")) options->pickaxe = arg + 2; - else if (!strcmp(arg, "-s")) { + else if (!strcmp(arg, "-s")) options->output_format |= DIFF_FORMAT_NO_OUTPUT; - } else if (!prefixcmp(arg, "-O")) options->orderfile = arg + 2; else if (!prefixcmp(arg, "--diff-filter=")) @@ -2264,28 +2261,25 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--pickaxe-regex")) options->pickaxe_opts = DIFF_PICKAXE_REGEX; else if (!prefixcmp(arg, "-B")) { - if ((options->break_opt = - diff_scoreopt_parse(arg)) == -1) + if ((options->break_opt = diff_scoreopt_parse(arg)) == -1) return -1; } else if (!prefixcmp(arg, "-M")) { - if ((options->rename_score = - diff_scoreopt_parse(arg)) == -1) + if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return -1; options->detect_rename = DIFF_DETECT_RENAME; } else if (!prefixcmp(arg, "-C")) { if (options->detect_rename == DIFF_DETECT_COPY) - options->find_copies_harder = 1; - if ((options->rename_score = - diff_scoreopt_parse(arg)) == -1) + DIFF_OPT_SET(options, FIND_COPIES_HARDER); + if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return -1; options->detect_rename = DIFF_DETECT_COPY; } else if (!strcmp(arg, "--find-copies-harder")) - options->find_copies_harder = 1; + DIFF_OPT_SET(options, FIND_COPIES_HARDER); else if (!strcmp(arg, "--follow")) - options->follow_renames = 1; + DIFF_OPT_SET(options, FOLLOW_RENAMES); else if (!strcmp(arg, "--abbrev")) options->abbrev = DEFAULT_ABBREV; else if (!prefixcmp(arg, "--abbrev=")) { @@ -2296,9 +2290,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->abbrev = 40; } else if (!strcmp(arg, "--color")) - options->color_diff = 1; + DIFF_OPT_SET(options, COLOR_DIFF); else if (!strcmp(arg, "--no-color")) - options->color_diff = 0; + DIFF_OPT_CLR(options, COLOR_DIFF); else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) options->xdl_opts |= XDF_IGNORE_WHITESPACE; else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) @@ -2306,17 +2300,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--ignore-space-at-eol")) options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; else if (!strcmp(arg, "--color-words")) - options->color_diff = options->color_diff_words = 1; + options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; else if (!strcmp(arg, "--no-renames")) options->detect_rename = 0; else if (!strcmp(arg, "--exit-code")) - options->exit_with_status = 1; + DIFF_OPT_SET(options, EXIT_WITH_STATUS); else if (!strcmp(arg, "--quiet")) - options->quiet = 1; + DIFF_OPT_SET(options, QUIET); else if (!strcmp(arg, "--ext-diff")) - options->allow_external = 1; + DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) - options->allow_external = 0; + DIFF_OPT_CLR(options, ALLOW_EXTERNAL); else return 0; return 1; @@ -3071,7 +3065,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) * to determine how many paths were dirty only * due to stat info mismatch. */ - if (!diffopt->no_index) + if (!DIFF_OPT_TST(diffopt, NO_INDEX)) diffopt->skip_stat_unmatch++; diff_free_filepair(p); } @@ -3082,10 +3076,10 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) void diffcore_std(struct diff_options *options) { - if (options->quiet) + if (DIFF_OPT_TST(options, QUIET)) return; - if (options->skip_stat_unmatch && !options->find_copies_harder) + if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER)) diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) diffcore_break(options->break_opt); @@ -3100,7 +3094,10 @@ void diffcore_std(struct diff_options *options) diff_resolve_rename_copy(); diffcore_apply_filter(options->filter); - options->has_changes = !!diff_queued_diff.nr; + if (diff_queued_diff.nr) + DIFF_OPT_SET(options, HAS_CHANGES); + else + DIFF_OPT_CLR(options, HAS_CHANGES); } @@ -3124,7 +3121,7 @@ void diff_addremove(struct diff_options *options, * Before the final output happens, they are pruned after * merged into rename/copy pairs as appropriate. */ - if (options->reverse_diff) + if (DIFF_OPT_TST(options, REVERSE_DIFF)) addremove = (addremove == '+' ? '-' : addremove == '-' ? '+' : addremove); @@ -3139,7 +3136,7 @@ void diff_addremove(struct diff_options *options, fill_filespec(two, sha1, mode); diff_queue(&diff_queued_diff, one, two); - options->has_changes = 1; + DIFF_OPT_SET(options, HAS_CHANGES); } void diff_change(struct diff_options *options, @@ -3151,7 +3148,7 @@ void diff_change(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; - if (options->reverse_diff) { + if (DIFF_OPT_TST(options, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_c; tmp = old_mode; old_mode = new_mode; new_mode = tmp; @@ -3165,7 +3162,7 @@ void diff_change(struct diff_options *options, fill_filespec(two, new_sha1, new_mode); diff_queue(&diff_queued_diff, one, two); - options->has_changes = 1; + DIFF_OPT_SET(options, HAS_CHANGES); } void diff_unmerge(struct diff_options *options, -- cgit v1.2.1 From d054680c7d245b71ebba0dd030fd13dce213559a Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 7 Nov 2007 11:20:32 +0100 Subject: Reorder diff_opt_parse options more logically per topics. This is a line reordering patch _only_. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- diff.c | 116 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 63 insertions(+), 53 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 67cce8b72..b08d28a57 100644 --- a/diff.c +++ b/diff.c @@ -2185,6 +2185,8 @@ static int diff_scoreopt_parse(const char *opt); int diff_opt_parse(struct diff_options *options, const char **av, int ac) { const char *arg = av[0]; + + /* Output format options */ if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) options->output_format |= DIFF_FORMAT_PATCH; else if (opt_arg(arg, 'U', "unified", &options->context)) @@ -2197,6 +2199,18 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_NUMSTAT; else if (!strcmp(arg, "--shortstat")) options->output_format |= DIFF_FORMAT_SHORTSTAT; + else if (!strcmp(arg, "--check")) + options->output_format |= DIFF_FORMAT_CHECKDIFF; + else if (!strcmp(arg, "--summary")) + options->output_format |= DIFF_FORMAT_SUMMARY; + else if (!strcmp(arg, "--patch-with-stat")) + options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; + else if (!strcmp(arg, "--name-only")) + options->output_format |= DIFF_FORMAT_NAME; + else if (!strcmp(arg, "--name-status")) + options->output_format |= DIFF_FORMAT_NAME_STATUS; + else if (!strcmp(arg, "-s")) + options->output_format |= DIFF_FORMAT_NO_OUTPUT; else if (!prefixcmp(arg, "--stat")) { char *end; int width = options->stat_width; @@ -2224,42 +2238,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->stat_name_width = name_width; options->stat_width = width; } - else if (!strcmp(arg, "--check")) - options->output_format |= DIFF_FORMAT_CHECKDIFF; - else if (!strcmp(arg, "--summary")) - options->output_format |= DIFF_FORMAT_SUMMARY; - else if (!strcmp(arg, "--patch-with-stat")) - options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; - else if (!strcmp(arg, "-z")) - options->line_termination = 0; - else if (!prefixcmp(arg, "-l")) - options->rename_limit = strtoul(arg+2, NULL, 10); - else if (!strcmp(arg, "--full-index")) - DIFF_OPT_SET(options, FULL_INDEX); - else if (!strcmp(arg, "--binary")) { - options->output_format |= DIFF_FORMAT_PATCH; - DIFF_OPT_SET(options, BINARY); - } - else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) - DIFF_OPT_SET(options, TEXT); - else if (!strcmp(arg, "--name-only")) - options->output_format |= DIFF_FORMAT_NAME; - else if (!strcmp(arg, "--name-status")) - options->output_format |= DIFF_FORMAT_NAME_STATUS; - else if (!strcmp(arg, "-R")) - DIFF_OPT_SET(options, REVERSE_DIFF); - else if (!prefixcmp(arg, "-S")) - options->pickaxe = arg + 2; - else if (!strcmp(arg, "-s")) - options->output_format |= DIFF_FORMAT_NO_OUTPUT; - else if (!prefixcmp(arg, "-O")) - options->orderfile = arg + 2; - else if (!prefixcmp(arg, "--diff-filter=")) - options->filter = arg + 14; - else if (!strcmp(arg, "--pickaxe-all")) - options->pickaxe_opts = DIFF_PICKAXE_ALL; - else if (!strcmp(arg, "--pickaxe-regex")) - options->pickaxe_opts = DIFF_PICKAXE_REGEX; + + /* renames options */ else if (!prefixcmp(arg, "-B")) { if ((options->break_opt = diff_scoreopt_parse(arg)) == -1) return -1; @@ -2276,33 +2256,38 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) return -1; options->detect_rename = DIFF_DETECT_COPY; } + else if (!strcmp(arg, "--no-renames")) + options->detect_rename = 0; + + /* xdiff options */ + else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) + options->xdl_opts |= XDF_IGNORE_WHITESPACE; + else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) + options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + else if (!strcmp(arg, "--ignore-space-at-eol")) + options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + + /* flags options */ + else if (!strcmp(arg, "--binary")) { + options->output_format |= DIFF_FORMAT_PATCH; + DIFF_OPT_SET(options, BINARY); + } + else if (!strcmp(arg, "--full-index")) + DIFF_OPT_SET(options, FULL_INDEX); + else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) + DIFF_OPT_SET(options, TEXT); + else if (!strcmp(arg, "-R")) + DIFF_OPT_SET(options, REVERSE_DIFF); else if (!strcmp(arg, "--find-copies-harder")) DIFF_OPT_SET(options, FIND_COPIES_HARDER); else if (!strcmp(arg, "--follow")) DIFF_OPT_SET(options, FOLLOW_RENAMES); - else if (!strcmp(arg, "--abbrev")) - options->abbrev = DEFAULT_ABBREV; - else if (!prefixcmp(arg, "--abbrev=")) { - options->abbrev = strtoul(arg + 9, NULL, 10); - if (options->abbrev < MINIMUM_ABBREV) - options->abbrev = MINIMUM_ABBREV; - else if (40 < options->abbrev) - options->abbrev = 40; - } else if (!strcmp(arg, "--color")) DIFF_OPT_SET(options, COLOR_DIFF); else if (!strcmp(arg, "--no-color")) DIFF_OPT_CLR(options, COLOR_DIFF); - else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE; - else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; - else if (!strcmp(arg, "--ignore-space-at-eol")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; else if (!strcmp(arg, "--color-words")) options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; - else if (!strcmp(arg, "--no-renames")) - options->detect_rename = 0; else if (!strcmp(arg, "--exit-code")) DIFF_OPT_SET(options, EXIT_WITH_STATUS); else if (!strcmp(arg, "--quiet")) @@ -2311,6 +2296,31 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + + /* misc options */ + else if (!strcmp(arg, "-z")) + options->line_termination = 0; + else if (!prefixcmp(arg, "-l")) + options->rename_limit = strtoul(arg+2, NULL, 10); + else if (!prefixcmp(arg, "-S")) + options->pickaxe = arg + 2; + else if (!strcmp(arg, "--pickaxe-all")) + options->pickaxe_opts = DIFF_PICKAXE_ALL; + else if (!strcmp(arg, "--pickaxe-regex")) + options->pickaxe_opts = DIFF_PICKAXE_REGEX; + else if (!prefixcmp(arg, "-O")) + options->orderfile = arg + 2; + else if (!prefixcmp(arg, "--diff-filter=")) + options->filter = arg + 14; + else if (!strcmp(arg, "--abbrev")) + options->abbrev = DEFAULT_ABBREV; + else if (!prefixcmp(arg, "--abbrev=")) { + options->abbrev = strtoul(arg + 9, NULL, 10); + if (options->abbrev < MINIMUM_ABBREV) + options->abbrev = MINIMUM_ABBREV; + else if (40 < options->abbrev) + options->abbrev = 40; + } else return 0; return 1; -- cgit v1.2.1 From f8b6809d52992ad14d035bcf7c8d8cfe00ef45a0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Nov 2007 23:06:44 -0800 Subject: Fix "quote" misconversion for rewrite diff output. 663af3422a648e87945e4d8c0cc3e13671f2bbde (Full rework of quote_c_style and write_name_quoted.) mistakenly used puts() when writing out a fixed string when it did not want to add a terminating LF. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b08d28a57..6b5495961 100644 --- a/diff.c +++ b/diff.c @@ -2716,7 +2716,7 @@ static void diff_summary(struct diff_filepair *p) break; default: if (p->score) { - puts(" rewrite "); + fputs(" rewrite ", stdout); write_name_quoted(p->two->path, stdout, ' '); printf("(%d%%)\n", similarity_index(p)); } -- cgit v1.2.1 From 0f6f5a4022de5904926cd528c681468e3f635256 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Dec 2007 17:26:11 -0800 Subject: git config --get-colorbool This adds an option to help scripts find out color settings from the configuration file. git config --get-colorbool color.diff inspects color.diff variable, and exits with status 0 (i.e. success) if color is to be used. It exits with status 1 otherwise. If a script wants "true"/"false" answer to the standard output of the command, it can pass an additional boolean parameter to its command line, telling if its standard output is a terminal, like this: git config --get-colorbool color.diff true When called like this, the command outputs "true" to its standard output if color is to be used (i.e. "color.diff" says "always", "auto", or "true"), and "false" otherwise. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6b5495961..be6cf687a 100644 --- a/diff.c +++ b/diff.c @@ -146,7 +146,7 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { - diff_use_color_default = git_config_colorbool(var, value); + diff_use_color_default = git_config_colorbool(var, value, -1); return 0; } if (!strcmp(var, "diff.renames")) { -- cgit v1.2.1 From cf1b7869f0c571bbd4f72a4355d9aca558baa0da Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Dec 2007 00:14:14 -0800 Subject: Use gitattributes to define per-path whitespace rule The `core.whitespace` configuration variable allows you to define what `diff` and `apply` should consider whitespace errors for all paths in the project (See gitlink:git-config[1]). This attribute gives you finer control per path. For example, if you have these in the .gitattributes: frotz whitespace nitfol -whitespace xyzzy whitespace=-trailing all types of whitespace problems known to git are noticed in path 'frotz' (i.e. diff shows them in diff.whitespace color, and apply warns about them), no whitespace problem is noticed in path 'nitfol', and the default types of whitespace problems except "trailing whitespace" are noticed for path 'xyzzy'. A project with mixed Python and C might want to have: *.c whitespace *.py whitespace=-indent-with-non-tab in its toplevel .gitattributes file. Signed-off-by: Junio C Hamano --- diff.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6bb902f4a..c3a1942bb 100644 --- a/diff.c +++ b/diff.c @@ -454,6 +454,7 @@ static void diff_words_show(struct diff_words_data *diff_words) struct emit_callback { struct xdiff_emit_state xm; int nparents, color_diff; + unsigned ws_rule; const char **label_path; struct diff_words_data *diff_words; int *found_changesp; @@ -493,8 +494,8 @@ static void emit_line(const char *set, const char *reset, const char *line, int } static void emit_line_with_ws(int nparents, - const char *set, const char *reset, const char *ws, - const char *line, int len) + const char *set, const char *reset, const char *ws, + const char *line, int len, unsigned ws_rule) { int col0 = nparents; int last_tab_in_indent = -1; @@ -511,7 +512,7 @@ static void emit_line_with_ws(int nparents, for (i = col0; i < len; i++) { if (line[i] == '\t') { last_tab_in_indent = i; - if ((whitespace_rule & WS_SPACE_BEFORE_TAB) && + if ((ws_rule & WS_SPACE_BEFORE_TAB) && 0 <= last_space_in_indent) need_highlight_leading_space = 1; } @@ -520,7 +521,7 @@ static void emit_line_with_ws(int nparents, else break; } - if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && + if ((ws_rule & WS_INDENT_WITH_NON_TAB) && 0 <= last_space_in_indent && last_tab_in_indent < 0 && 8 <= (i - col0)) { @@ -551,7 +552,7 @@ static void emit_line_with_ws(int nparents, tail = len - 1; if (line[tail] == '\n' && i < tail) tail--; - if (whitespace_rule & WS_TRAILING_SPACE) { + if (ws_rule & WS_TRAILING_SPACE) { while (i < tail) { if (!isspace(line[tail])) break; @@ -578,7 +579,7 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons emit_line(set, reset, line, len); else emit_line_with_ws(ecbdata->nparents, set, reset, ws, - line, len); + line, len, ecbdata->ws_rule); } static void fn_out_consume(void *priv, char *line, unsigned long len) @@ -994,6 +995,7 @@ struct checkdiff_t { struct xdiff_emit_state xm; const char *filename; int lineno, color_diff; + unsigned ws_rule; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1029,7 +1031,8 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (white_space_at_end) printf("white space at end"); printf(":%s ", reset); - emit_line_with_ws(1, set, reset, ws, line, len); + emit_line_with_ws(1, set, reset, ws, line, len, + data->ws_rule); } data->lineno++; @@ -1330,6 +1333,7 @@ static void builtin_diff(const char *name_a, ecbdata.label_path = lbl; ecbdata.color_diff = o->color_diff; ecbdata.found_changesp = &o->found_changes; + ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; @@ -1423,6 +1427,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.filename = name_b ? name_b : name_a; data.lineno = 0; data.color_diff = o->color_diff; + data.ws_rule = whitespace_rule(data.filename); if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); -- cgit v1.2.1 From 0ac7903ee3c7c93d2426a4aa4f3732bee73e0365 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Wed, 12 Dec 2007 09:12:01 +0100 Subject: Use "whitespace" consistently For consistency, change "white space" and "whitespaces" to "whitespace", fixing a couple of adjacent grammar problems in the docs. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f780e3e8e..e74a30399 100644 --- a/diff.c +++ b/diff.c @@ -1015,7 +1015,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (line[i - 1] == '\t' && spaces) space_before_tab = 1; - /* check white space at line end */ + /* check whitespace at line end */ if (line[len - 1] == '\n') len--; if (isspace(line[len - 1])) @@ -1029,7 +1029,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) putchar(','); } if (white_space_at_end) - printf("white space at end"); + printf("whitespace at end"); printf(":%s ", reset); emit_line_with_ws(1, set, reset, ws, line, len, data->ws_rule); -- cgit v1.2.1 From f604652e05073aaef6d83e83b5d6499b55bb6dfd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 11 Dec 2007 23:46:30 -0800 Subject: git-diff --numstat -z: make it machine readable The "-z" format is all about machine parsability, but showing renamed paths as "common/{a => b}/suffix" makes it impossible. The scripts would never have successfully parsed "--numstat -z -M" in the old format. This fixes the output format in a (hopefully minimally) backward incompatible way. * The output without -z is not changed. This has given a good way for humans to view added and deleted lines separately, and showing the path in combined, shorter way would preserve readability. * The output with -z is unchanged for paths that do not involve renames. Existing scripts that do not pass -M/-C are not affected at all. * The output with -z for a renamed path is shown in a format that can easily be distinguished from an unrenamed path. This is based on Jakub Narebski's patch. Bugs and documentation typos are mine. Signed-off-by: Junio C Hamano --- diff.c | 100 ++++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 32 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e74a30399..d97ebc501 100644 --- a/diff.c +++ b/diff.c @@ -734,7 +734,9 @@ struct diffstat_t { int nr; int alloc; struct diffstat_file { + char *from_name; char *name; + char *print_name; unsigned is_unmerged:1; unsigned is_binary:1; unsigned is_renamed:1; @@ -755,11 +757,14 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, } diffstat->files[diffstat->nr++] = x; if (name_b) { - x->name = pprint_rename(name_a, name_b); + x->from_name = xstrdup(name_a); + x->name = xstrdup(name_b); x->is_renamed = 1; } - else + else { + x->from_name = NULL; x->name = xstrdup(name_a); + } return x; } @@ -803,6 +808,28 @@ static void show_graph(char ch, int cnt, const char *set, const char *reset) printf("%s", reset); } +static void fill_print_name(struct diffstat_file *file) +{ + char *pname; + + if (file->print_name) + return; + + if (!file->is_renamed) { + struct strbuf buf; + strbuf_init(&buf, 0); + if (quote_c_style(file->name, &buf, NULL, 0)) { + pname = strbuf_detach(&buf, NULL); + } else { + pname = file->name; + strbuf_release(&buf); + } + } else { + pname = pprint_rename(file->from_name, file->name); + } + file->print_name = pname; +} + static void show_stats(struct diffstat_t* data, struct diff_options *options) { int i, len, add, del, total, adds = 0, dels = 0; @@ -836,19 +863,8 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) for (i = 0; i < data->nr; i++) { struct diffstat_file *file = data->files[i]; int change = file->added + file->deleted; - - if (!file->is_renamed) { /* renames are already quoted by pprint_rename */ - struct strbuf buf; - strbuf_init(&buf, 0); - if (quote_c_style(file->name, &buf, NULL, 0)) { - free(file->name); - file->name = strbuf_detach(&buf, NULL); - } else { - strbuf_release(&buf); - } - } - - len = strlen(file->name); + fill_print_name(file); + len = strlen(file->print_name); if (max_len < len) max_len = len; @@ -873,7 +889,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) for (i = 0; i < data->nr; i++) { const char *prefix = ""; - char *name = data->files[i]->name; + char *name = data->files[i]->print_name; int added = data->files[i]->added; int deleted = data->files[i]->deleted; int name_len; @@ -901,17 +917,17 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) printf("%s%d%s", add_c, added, reset); printf(" bytes"); printf("\n"); - goto free_diffstat_file; + continue; } else if (data->files[i]->is_unmerged) { show_name(prefix, name, len, reset, set); printf(" Unmerged\n"); - goto free_diffstat_file; + continue; } else if (!data->files[i]->is_renamed && (added + deleted == 0)) { total_files--; - goto free_diffstat_file; + continue; } /* @@ -933,11 +949,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) show_graph('+', add, add_c, reset); show_graph('-', del, del_c, reset); putchar('\n'); - free_diffstat_file: - free(data->files[i]->name); - free(data->files[i]); } - free(data->files); printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n", set, total_files, adds, dels, reset); } @@ -962,11 +974,7 @@ static void show_shortstats(struct diffstat_t* data) dels += deleted; } } - free(data->files[i]->name); - free(data->files[i]); } - free(data->files); - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", total_files, adds, dels); } @@ -975,6 +983,9 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) { int i; + if (data->nr == 0) + return; + for (i = 0; i < data->nr; i++) { struct diffstat_file *file = data->files[i]; @@ -982,15 +993,39 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) printf("-\t-\t"); else printf("%d\t%d\t", file->added, file->deleted); - if (!file->is_renamed) { - write_name_quoted(file->name, stdout, options->line_termination); + if (options->line_termination) { + fill_print_name(file); + if (!file->is_renamed) + write_name_quoted(file->name, stdout, + options->line_termination); + else { + fputs(file->print_name, stdout); + putchar(options->line_termination); + } } else { - fputs(file->name, stdout); - putchar(options->line_termination); + if (file->is_renamed) { + putchar('\0'); + write_name_quoted(file->from_name, stdout, '\0'); + } + write_name_quoted(file->name, stdout, '\0'); } } } +static void free_diffstat_info(struct diffstat_t *diffstat) +{ + int i; + for (i = 0; i < diffstat->nr; i++) { + struct diffstat_file *f = diffstat->files[i]; + if (f->name != f->print_name) + free(f->print_name); + free(f->name); + free(f->from_name); + free(f); + } + free(diffstat->files); +} + struct checkdiff_t { struct xdiff_emit_state xm; const char *filename; @@ -2943,8 +2978,9 @@ void diff_flush(struct diff_options *options) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) show_stats(&diffstat, options); - else if (output_format & DIFF_FORMAT_SHORTSTAT) + if (output_format & DIFF_FORMAT_SHORTSTAT) show_shortstats(&diffstat); + free_diffstat_info(&diffstat); separator++; } -- cgit v1.2.1 From 86f8c2368523db5ee56c8856748b26c31ebeb0aa Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Wed, 12 Dec 2007 17:22:59 +0100 Subject: Fix "diff --check" whitespace detection "diff --check" would only detect spaces before tabs if a tab was the last character in the leading indent. Fix that and add a test case to make sure the bug doesn't regress in the future. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- diff.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d97ebc501..9c79ee289 100644 --- a/diff.c +++ b/diff.c @@ -1044,11 +1044,18 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0; /* check space before tab */ - for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++) + for (i = 1; i < len; i++) { if (line[i] == ' ') spaces++; - if (line[i - 1] == '\t' && spaces) - space_before_tab = 1; + else if (line[i] == '\t') { + if (spaces) { + space_before_tab = 1; + break; + } + } + else + break; + } /* check whitespace at line end */ if (line[len - 1] == '\n') -- cgit v1.2.1 From c279d7e9869740a6d64b0d1d70f7968af68d2071 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 13 Dec 2007 13:25:07 -0800 Subject: xdl_diff: identify call sites. This inserts a new function xdi_diff() that currently does not do anything other than calling the underlying xdl_diff() to the callchain of current callers of xdl_diff() function. Signed-off-by: Junio C Hamano --- diff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9c79ee289..3dd2f35f7 100644 --- a/diff.c +++ b/diff.c @@ -439,7 +439,7 @@ static void diff_words_show(struct diff_words_data *diff_words) ecb.outf = xdiff_outf; ecb.priv = diff_words; diff_words->xm.consume = fn_out_diff_words_aux; - xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb); + xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); @@ -1393,7 +1393,7 @@ static void builtin_diff(const char *name_a, if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1446,7 +1446,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; ecb.outf = xdiff_outf; ecb.priv = diffstat; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } free_and_return: @@ -1486,7 +1486,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, xpp.flags = XDF_NEED_MINIMAL; ecb.outf = xdiff_outf; ecb.priv = &data; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } free_and_return: diff_free_filespec_data(one); @@ -2898,7 +2898,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xecfg.flags = XDL_EMIT_FUNCNAMES; ecb.outf = xdiff_outf; ecb.priv = &data; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); -- cgit v1.2.1 From 62c64895cfcf3bbf34969a69fa96a631f7d5b14e Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 13 Dec 2007 21:24:52 +0100 Subject: "diff --check" should affect exit status "git diff" has a --check option that can be used to check for whitespace problems but it only reported by printing warnings to the console. Now when the --check option is used we give a non-zero exit status, making "git diff --check" nicer to use in scripts and hooks. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- diff.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3dd2f35f7..823707548 100644 --- a/diff.c +++ b/diff.c @@ -1031,6 +1031,7 @@ struct checkdiff_t { const char *filename; int lineno, color_diff; unsigned ws_rule; + unsigned status; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1064,6 +1065,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) white_space_at_end = 1; if (space_before_tab || white_space_at_end) { + data->status = 1; printf("%s:%d: %s", data->filename, data->lineno, ws); if (space_before_tab) { printf("space before tab"); @@ -1491,6 +1493,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, free_and_return: diff_free_filespec_data(one); diff_free_filespec_data(two); + if (data.status) + DIFF_OPT_SET(o, CHECK_FAILED); } struct diff_filespec *alloc_filespec(const char *path) @@ -2121,7 +2125,12 @@ int diff_setup_done(struct diff_options *options) if (options->output_format & DIFF_FORMAT_NAME_STATUS) count++; if (options->output_format & DIFF_FORMAT_CHECKDIFF) + { count++; + if (DIFF_OPT_TST(options, QUIET) || + DIFF_OPT_TST(options, EXIT_WITH_STATUS)) + die("--check may not be used with --quiet or --exit-code"); + } if (options->output_format & DIFF_FORMAT_NO_OUTPUT) count++; if (count > 1) -- cgit v1.2.1 From da31b358fb39b32622c14343ffe157a765f3948b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 13 Dec 2007 23:40:27 -0800 Subject: diff --check: minor fixups There is no reason --exit-code and --check-diff must be mutually exclusive, so assign different bits to different results and allow them to be returned from the command. Introduce diff_result_code() to factor out the common code to decide final status code based on diffopt settings and use it everywhere. Update tests to match the above fix. Turning pager off when "diff --check" is used is a regression. Signed-off-by: Junio C Hamano --- diff.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 823707548..3e46ff8a7 100644 --- a/diff.c +++ b/diff.c @@ -2125,12 +2125,7 @@ int diff_setup_done(struct diff_options *options) if (options->output_format & DIFF_FORMAT_NAME_STATUS) count++; if (options->output_format & DIFF_FORMAT_CHECKDIFF) - { count++; - if (DIFF_OPT_TST(options, QUIET) || - DIFF_OPT_TST(options, EXIT_WITH_STATUS)) - die("--check may not be used with --quiet or --exit-code"); - } if (options->output_format & DIFF_FORMAT_NO_OUTPUT) count++; if (count > 1) @@ -3180,6 +3175,20 @@ void diffcore_std(struct diff_options *options) DIFF_OPT_CLR(options, HAS_CHANGES); } +int diff_result_code(struct diff_options *opt, int status) +{ + int result = 0; + if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + !(opt->output_format & DIFF_FORMAT_CHECKDIFF)) + return status; + if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + DIFF_OPT_TST(opt, HAS_CHANGES)) + result |= 01; + if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) && + DIFF_OPT_TST(opt, CHECK_FAILED)) + result |= 02; + return result; +} void diff_addremove(struct diff_options *options, int addremove, unsigned mode, -- cgit v1.2.1 From c1795bb08aae9fb7e4dc1a01e292b85e59b1c640 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 13 Dec 2007 14:32:29 +0100 Subject: Unify whitespace checking This commit unifies three separate places where whitespace checking was performed: - the whitespace checking previously done in builtin-apply.c is extracted into a function in ws.c - the equivalent logic in "git diff" is removed - the emit_line_with_ws() function is also removed because that also rechecks the whitespace, and its functionality is rolled into ws.c The new function is called check_and_emit_line() and it does two things: checks a line for whitespace errors and optionally emits it. The checking is based on lines of content rather than patch lines (in other words, the caller must strip the leading "+" or "-"); this was suggested by Junio on the mailing list to allow for a future extension to "git show" to display whitespace errors in blobs. At the same time we teach it to report all classes of whitespace errors found for a given line rather than reporting only the first found error. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- diff.c | 138 +++++++++-------------------------------------------------------- 1 file changed, 19 insertions(+), 119 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3e46ff8a7..577c773c3 100644 --- a/diff.c +++ b/diff.c @@ -486,88 +486,9 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(const char *set, const char *reset, const char *line, int len) { - if (len > 0 && line[len-1] == '\n') - len--; fputs(set, stdout); fwrite(line, len, 1, stdout); - puts(reset); -} - -static void emit_line_with_ws(int nparents, - const char *set, const char *reset, const char *ws, - const char *line, int len, unsigned ws_rule) -{ - int col0 = nparents; - int last_tab_in_indent = -1; - int last_space_in_indent = -1; - int i; - int tail = len; - int need_highlight_leading_space = 0; - /* - * The line is a newly added line. Does it have funny leading - * whitespaces? In indent, SP should never precede a TAB. In - * addition, under "indent with non tab" rule, there should not - * be more than 8 consecutive spaces. - */ - for (i = col0; i < len; i++) { - if (line[i] == '\t') { - last_tab_in_indent = i; - if ((ws_rule & WS_SPACE_BEFORE_TAB) && - 0 <= last_space_in_indent) - need_highlight_leading_space = 1; - } - else if (line[i] == ' ') - last_space_in_indent = i; - else - break; - } - if ((ws_rule & WS_INDENT_WITH_NON_TAB) && - 0 <= last_space_in_indent && - last_tab_in_indent < 0 && - 8 <= (i - col0)) { - last_tab_in_indent = i; - need_highlight_leading_space = 1; - } - fputs(set, stdout); - fwrite(line, col0, 1, stdout); fputs(reset, stdout); - if (((i == len) || line[i] == '\n') && i != col0) { - /* The whole line was indent */ - emit_line(ws, reset, line + col0, len - col0); - return; - } - i = col0; - if (need_highlight_leading_space) { - while (i < last_tab_in_indent) { - if (line[i] == ' ') { - fputs(ws, stdout); - putchar(' '); - fputs(reset, stdout); - } - else - putchar(line[i]); - i++; - } - } - tail = len - 1; - if (line[tail] == '\n' && i < tail) - tail--; - if (ws_rule & WS_TRAILING_SPACE) { - while (i < tail) { - if (!isspace(line[tail])) - break; - tail--; - } - } - if ((i < tail && line[tail + 1] != '\n')) { - /* This has whitespace between tail+1..len */ - fputs(set, stdout); - fwrite(line + i, tail - i + 1, 1, stdout); - fputs(reset, stdout); - emit_line(ws, reset, line + tail + 1, len - tail - 1); - } - else - emit_line(set, reset, line + i, len - i); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) @@ -577,9 +498,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons if (!*ws) emit_line(set, reset, line, len); - else - emit_line_with_ws(ecbdata->nparents, set, reset, ws, - line, len, ecbdata->ws_rule); + else { + /* Emit just the prefix, then the rest. */ + emit_line(set, reset, line, ecbdata->nparents); + (void)check_and_emit_line(line + ecbdata->nparents, + len - ecbdata->nparents, ecbdata->ws_rule, + stdout, set, reset, ws); + } } static void fn_out_consume(void *priv, char *line, unsigned long len) @@ -1040,45 +965,20 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE); const char *reset = diff_get_color(data->color_diff, DIFF_RESET); const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW); + char *err; if (line[0] == '+') { - int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0; - - /* check space before tab */ - for (i = 1; i < len; i++) { - if (line[i] == ' ') - spaces++; - else if (line[i] == '\t') { - if (spaces) { - space_before_tab = 1; - break; - } - } - else - break; - } - - /* check whitespace at line end */ - if (line[len - 1] == '\n') - len--; - if (isspace(line[len - 1])) - white_space_at_end = 1; - - if (space_before_tab || white_space_at_end) { - data->status = 1; - printf("%s:%d: %s", data->filename, data->lineno, ws); - if (space_before_tab) { - printf("space before tab"); - if (white_space_at_end) - putchar(','); - } - if (white_space_at_end) - printf("whitespace at end"); - printf(":%s ", reset); - emit_line_with_ws(1, set, reset, ws, line, len, - data->ws_rule); - } - + data->status = check_and_emit_line(line + 1, len - 1, + data->ws_rule, NULL, NULL, NULL, NULL); + if (!data->status) + return; + err = whitespace_error_string(data->status); + printf("%s:%d: %s%s:%s ", data->filename, data->lineno, + ws, err, reset); + free(err); + emit_line(set, reset, line, 1); + (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, + stdout, set, reset, ws); data->lineno++; } else if (line[0] == ' ') data->lineno++; -- cgit v1.2.1 From 45e2a4b2b0a07cda05dda08b47906100c4711e4e Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 13 Dec 2007 14:32:30 +0100 Subject: Make "diff --check" output match "git apply" For consistency, make the two tools report whitespace errors in the same way (the output of "diff --check" has been tweaked to match that of "git apply"). Note that although the textual content is basically the same only "git diff --check" provides a colorized version of the problematic lines; making "git apply" do colorization will require more extensive changes (figuring out the diff colorization preferences of the user) and so that will be a subject for another commit. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- diff.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 577c773c3..08ec66c79 100644 --- a/diff.c +++ b/diff.c @@ -973,8 +973,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (!data->status) return; err = whitespace_error_string(data->status); - printf("%s:%d: %s%s:%s ", data->filename, data->lineno, - ws, err, reset); + printf("%s:%d: %s.\n", data->filename, data->lineno, err); free(err); emit_line(set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, -- cgit v1.2.1 From cbe021004fc8762076bf497cc0b259d110b3a753 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 17 Dec 2007 13:42:20 +0000 Subject: Support config variable diff.external We had the diff.external variable in the documentation of the config file since its conception, but failed to respect it. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 08ec66c79..e26584cdf 100644 --- a/diff.c +++ b/diff.c @@ -20,6 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 100; static int diff_use_color_default; +static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; static char diff_colors[][COLOR_MAXLEN] = { @@ -163,6 +164,10 @@ int git_diff_ui_config(const char *var, const char *value) diff_auto_refresh_index = git_config_bool(var, value); return 0; } + if (!strcmp(var, "diff.external")) { + external_diff_cmd_cfg = xstrdup(value); + return 0; + } if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); @@ -209,6 +214,8 @@ static const char *external_diff(void) if (done_preparing) return external_diff_cmd; external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); + if (!external_diff_cmd) + external_diff_cmd = external_diff_cmd_cfg; done_preparing = 1; return external_diff_cmd; } -- cgit v1.2.1 From eab9a40b6dd5c1c571b1deb264133db47bb4794d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 18 Dec 2007 19:32:14 +0000 Subject: Teach diff machinery to display other prefixes than "a/" and "b/" With the new options "--src-prefix=", "--dst-prefix=" and "--no-prefix", you can now control the path prefixes of the diff machinery. These used to by hardwired to "a/" for the source prefix and "b/" for the destination prefix. Initial patch by Pascal Obry. Sane option names suggested by Linus. Signed-off-by: Johannes Schindelin Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e26584cdf..61fd49236 100644 --- a/diff.c +++ b/diff.c @@ -290,9 +290,10 @@ static void emit_rewrite_diff(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, - int color_diff) + struct diff_options *o) { int lc_a, lc_b; + int color_diff = DIFF_OPT_TST(o, COLOR_DIFF); const char *name_a_tab, *name_b_tab; const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO); const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO); @@ -309,9 +310,9 @@ static void emit_rewrite_diff(const char *name_a, diff_populate_filespec(two, 0); lc_a = count_lines(one->data, one->size); lc_b = count_lines(two->data, two->size); - printf("%s--- a/%s%s%s\n%s+++ b/%s%s%s\n%s@@ -", - metainfo, name_a, name_a_tab, reset, - metainfo, name_b, name_b_tab, reset, fraginfo); + printf("%s--- %s%s%s%s\n%s+++ %s%s%s%s\n%s@@ -", + metainfo, o->a_prefix, name_a, name_a_tab, reset, + metainfo, o->b_prefix, name_b, name_b_tab, reset, fraginfo); print_line_count(lc_a); printf(" +"); print_line_count(lc_b); @@ -1212,8 +1213,8 @@ static void builtin_diff(const char *name_a, const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); - a_one = quote_two("a/", name_a + (*name_a == '/')); - b_two = quote_two("b/", name_b + (*name_b == '/')); + a_one = quote_two(o->a_prefix, name_a + (*name_a == '/')); + b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset); @@ -1242,8 +1243,7 @@ static void builtin_diff(const char *name_a, if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; if (complete_rewrite) { - emit_rewrite_diff(name_a, name_b, one, two, - DIFF_OPT_TST(o, COLOR_DIFF)); + emit_rewrite_diff(name_a, name_b, one, two, o); o->found_changes = 1; goto free_ab_and_return; } @@ -2020,6 +2020,9 @@ void diff_setup(struct diff_options *options) else DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; + + options->a_prefix = "a/"; + options->b_prefix = "b/"; } int diff_setup_done(struct diff_options *options) @@ -2291,6 +2294,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (40 < options->abbrev) options->abbrev = 40; } + else if (!prefixcmp(arg, "--src-prefix=")) + options->a_prefix = arg + 13; + else if (!prefixcmp(arg, "--dst-prefix=")) + options->b_prefix = arg + 13; + else if (!strcmp(arg, "--no-prefix")) + options->a_prefix = options->b_prefix = ""; else return 0; return 1; -- cgit v1.2.1 From d56250911f4ec074048f4edb34551eaf384172cc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Dec 2007 17:13:36 -0800 Subject: Fix rewrite_diff() name quoting. This moves the logic to quote two paths (prefix + path) in C-style introduced in the previous commit from the dump_quoted_path() in combine-diff.c to quote.c, and uses it to fix rewrite_diff() that never C-quoted the pathnames correctly. Signed-off-by: Junio C Hamano --- diff.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 61fd49236..5bdc11137 100644 --- a/diff.c +++ b/diff.c @@ -300,19 +300,25 @@ static void emit_rewrite_diff(const char *name_a, const char *old = diff_get_color(color_diff, DIFF_FILE_OLD); const char *new = diff_get_color(color_diff, DIFF_FILE_NEW); const char *reset = diff_get_color(color_diff, DIFF_RESET); + static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT; name_a += (*name_a == '/'); name_b += (*name_b == '/'); name_a_tab = strchr(name_a, ' ') ? "\t" : ""; name_b_tab = strchr(name_b, ' ') ? "\t" : ""; + strbuf_reset(&a_name); + strbuf_reset(&b_name); + quote_two_c_style(&a_name, o->a_prefix, name_a, 0); + quote_two_c_style(&b_name, o->b_prefix, name_b, 0); + diff_populate_filespec(one, 0); diff_populate_filespec(two, 0); lc_a = count_lines(one->data, one->size); lc_b = count_lines(two->data, two->size); - printf("%s--- %s%s%s%s\n%s+++ %s%s%s%s\n%s@@ -", - metainfo, o->a_prefix, name_a, name_a_tab, reset, - metainfo, o->b_prefix, name_b, name_b_tab, reset, fraginfo); + printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", + metainfo, a_name.buf, name_a_tab, reset, + metainfo, b_name.buf, name_b_tab, reset, fraginfo); print_line_count(lc_a); printf(" +"); print_line_count(lc_b); -- cgit v1.2.1 From 9a1805a8726ee41f25be2e0f2d5f38f1150d38e4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 4 Jan 2008 03:59:34 -0500 Subject: add a "basic" diff config callback The diff porcelain uses git_diff_ui_config to set porcelain-ish config options, like automatically turning on color. The plumbing specifically avoids calling this function, since it doesn't want things like automatic color or rename detection. However, some diff options should be set for both plumbing and porcelain. For example, one can still turn on color in git-diff-files using the --color command line option. This means we want the color config from color.diff.* (so that once color is on, we use the user's preferred scheme), but _not_ the color.diff variable. We split the diff config into "ui" and "basic", where "basic" is suitable for use by plumbing (so _most_ things affecting the output should still go into the "ui" part). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5bdc11137..44aa477c4 100644 --- a/diff.c +++ b/diff.c @@ -178,6 +178,12 @@ int git_diff_ui_config(const char *var, const char *value) return parse_funcname_pattern(var, ep, value); } } + + return git_diff_basic_config(var, value); +} + +int git_diff_basic_config(const char *var, const char *value) +{ if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); color_parse(value, var, diff_colors[slot]); -- cgit v1.2.1 From e467193ff3f57171c5c3232b4ba3595970efbfb5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 4 Jan 2008 04:16:14 -0500 Subject: diff: load funcname patterns in "basic" config The funcname patterns influence the "comment" on @@ lines of the diff. They are safe to use with plumbing since they don't fundamentally change the meaning of the diff in any way. Since all diff users call either diff_ui_config or diff_basic_config, we can get rid of the lazy reading of the config. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 44aa477c4..4a908b28a 100644 --- a/diff.c +++ b/diff.c @@ -174,8 +174,6 @@ int git_diff_ui_config(const char *var, const char *value) if (ep != var + 4) { if (!strcmp(ep, ".command")) return parse_lldiff_command(var, ep, value); - if (!strcmp(ep, ".funcname")) - return parse_funcname_pattern(var, ep, value); } } @@ -190,6 +188,14 @@ int git_diff_basic_config(const char *var, const char *value) return 0; } + if (!prefixcmp(var, "diff.")) { + const char *ep = strrchr(var, '.'); + if (ep != var + 4) { + if (!strcmp(ep, ".funcname")) + return parse_funcname_pattern(var, ep, value); + } + } + return git_default_config(var, value); } @@ -1160,7 +1166,6 @@ static const char *funcname_pattern(const char *ident) { struct funcname_pattern *pp; - read_config_if_needed(); for (pp = funcname_pattern_list; pp; pp = pp->next) if (!strcmp(ident, pp->name)) return pp->pattern; -- cgit v1.2.1 From cae6c25a7fdd7a316318c2efdc48e63cce36a68c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 4 Jan 2008 04:45:31 -0500 Subject: diff: remove lazy config loading There is no point to this. Either: 1. The program has already loaded git_diff_ui_config, in which case this is a noop. 2. The program didn't, which means it is plumbing that does not _want_ git_diff_ui_config to be loaded. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 4a908b28a..2c78d74a4 100644 --- a/diff.c +++ b/diff.c @@ -59,14 +59,6 @@ static struct ll_diff_driver { char *cmd; } *user_diff, **user_diff_tail; -static void read_config_if_needed(void) -{ - if (!user_diff_tail) { - user_diff_tail = &user_diff; - git_config(git_diff_ui_config); - } -} - /* * Currently there is only "diff..command" variable; * because there are "diff.color." variables, we are parsing @@ -1822,7 +1814,6 @@ static const char *external_diff_attr(const char *name) !ATTR_UNSET(value)) { struct ll_diff_driver *drv; - read_config_if_needed(); for (drv = user_diff; drv; drv = drv->next) if (!strcmp(drv->name, value)) return drv->cmd; -- cgit v1.2.1 From 23707811c56a7756cbd6188e510f0a486c35c929 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 2 Jan 2008 01:50:11 -0800 Subject: diff: do not chomp hunk-header in the middle of a character We truncate hunk-header line at 80 bytes, but that 80th byte could be in the middle of a character, which is bad. This uses pick_one_utf8_char() function to make sure we do not cut a character in the middle. This assumes that the internal representation of the text is UTF-8. This needs to be extended in the future but the optimal direction has not been decided yet. Signed-off-by: Junio C Hamano --- diff.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 2c78d74a4..b18c140e3 100644 --- a/diff.c +++ b/diff.c @@ -10,6 +10,7 @@ #include "color.h" #include "attr.h" #include "run-command.h" +#include "utf8.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -469,10 +470,13 @@ static void diff_words_show(struct diff_words_data *diff_words) } } +typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); + struct emit_callback { struct xdiff_emit_state xm; int nparents, color_diff; unsigned ws_rule; + sane_truncate_fn truncate; const char **label_path; struct diff_words_data *diff_words; int *found_changesp; @@ -525,6 +529,24 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons } } +static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len) +{ + const char *cp; + unsigned long allot; + size_t l = len; + + if (ecb->truncate) + return ecb->truncate(line, len); + cp = line; + allot = l; + while (0 < l) { + (void) utf8_width(&cp, &l); + if (!cp) + break; /* truncated in the middle? */ + } + return allot - l; +} + static void fn_out_consume(void *priv, char *line, unsigned long len) { int i; @@ -555,8 +577,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) ; if (2 <= i && i < len && line[i] == ' ') { ecbdata->nparents = i - 1; + len = sane_truncate_line(ecbdata, line, len); emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO), reset, line, len); + if (line[len-1] != '\n') + putchar('\n'); return; } -- cgit v1.2.1 From 9f6fe82233fff26abd1a5315ba7709ccee078ec1 Mon Sep 17 00:00:00 2001 From: Bill Lear Date: Wed, 16 Jan 2008 06:46:53 -0600 Subject: Correct spelling in diff.c comment Correct a spelling mistake in a comment. Signed-off-by: Bill Lear Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b18c140e3..9ee0c41ba 100644 --- a/diff.c +++ b/diff.c @@ -1224,7 +1224,7 @@ static const char *diff_funcname_pattern(struct diff_filespec *one) /* * And define built-in fallback patterns here. Note that - * these can be overriden by the user's config settings. + * these can be overridden by the user's config settings. */ for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++) if (!strcmp(ident, builtin_funcname_pattern[i].name)) -- cgit v1.2.1 From 472ca780773ac75fa6af881117ce5360957fae2d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 17 Jan 2008 10:03:06 -0500 Subject: color unchanged lines as "plain" in "diff --color-words" These were mistakenly being colored in "meta" color. Signed-off-by: Junio C Hamano --- diff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9ee0c41ba..5b8afdcb0 100644 --- a/diff.c +++ b/diff.c @@ -552,7 +552,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) int i; int color; struct emit_callback *ecbdata = priv; - const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); + const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); + const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN); const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET); *(ecbdata->found_changesp) = 1; @@ -564,9 +565,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : ""; printf("%s--- %s%s%s\n", - set, ecbdata->label_path[0], reset, name_a_tab); + meta, ecbdata->label_path[0], reset, name_a_tab); printf("%s+++ %s%s%s\n", - set, ecbdata->label_path[1], reset, name_b_tab); + meta, ecbdata->label_path[1], reset, name_b_tab); ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } @@ -586,7 +587,6 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) } if (len < ecbdata->nparents) { - set = reset; emit_line(reset, reset, line, len); return; } @@ -610,7 +610,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) diff_words_show(ecbdata->diff_words); line++; len--; - emit_line(set, reset, line, len); + emit_line(plain, reset, line, len); return; } for (i = 0; i < ecbdata->nparents && len; i++) { -- cgit v1.2.1 From eadb5831342bb2e756fa05c03642c4aa1929d4f5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 18 Jan 2008 23:45:24 -0800 Subject: Avoid running lstat(2) on the same cache entry. Aside from the lstat(2) done for work tree files, there are quite many lstat(2) calls in refname dwimming codepath. This patch is not about reducing them. * It adds a new ce_flag, CE_UPTODATE, that is meant to mark the cache entries that record a regular file blob that is up to date in the work tree. If somebody later walks the index and wants to see if the work tree has changes, they do not have to be checked with lstat(2) again. * fill_stat_cache_info() marks the cache entry it just added with CE_UPTODATE. This has the effect of marking the paths we write out of the index and lstat(2) immediately as "no need to lstat -- we know it is up-to-date", from quite a lot fo callers: - git-apply --index - git-update-index - git-checkout-index - git-add (uses add_file_to_index()) - git-commit (ditto) - git-mv (ditto) * refresh_cache_ent() also marks the cache entry that are clean with CE_UPTODATE. * write_index is changed not to write CE_UPTODATE out to the index file, because CE_UPTODATE is meant to be transient only in core. For the same reason, CE_UPDATE is not written to prevent an accident from happening. Signed-off-by: Junio C Hamano Signed-off-by: Linus Torvalds --- diff.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5b8afdcb0..d464fe3b2 100644 --- a/diff.c +++ b/diff.c @@ -1510,17 +1510,22 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int if (pos < 0) return 0; ce = active_cache[pos]; - if ((lstat(name, &st) < 0) || - !S_ISREG(st.st_mode) || /* careful! */ - ce_match_stat(ce, &st, 0) || - hashcmp(sha1, ce->sha1)) + + /* + * This is not the sha1 we are looking for, or + * unreusable because it is not a regular file. + */ + if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode)) return 0; - /* we return 1 only when we can stat, it is a regular file, - * stat information matches, and sha1 recorded in the cache - * matches. I.e. we know the file in the work tree really is - * the same as the pair. + + /* + * If ce matches the file in the work tree, we can reuse it. */ - return 1; + if (ce_uptodate(ce) || + (!lstat(name, &st) && !ce_match_stat(ce, &st, 0))) + return 1; + + return 0; } static int populate_from_stdin(struct diff_filespec *s) -- cgit v1.2.1 From 21e5ad50fc5e7277c74cfbb3cf6502468e840f86 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Wed, 6 Feb 2008 12:25:58 +0100 Subject: safecrlf: Add mechanism to warn about irreversible crlf conversions CRLF conversion bears a slight chance of corrupting data. autocrlf=true will convert CRLF to LF during commit and LF to CRLF during checkout. A file that contains a mixture of LF and CRLF before the commit cannot be recreated by git. For text files this is the right thing to do: it corrects line endings such that we have only LF line endings in the repository. But for binary files that are accidentally classified as text the conversion can corrupt data. If you recognize such corruption early you can easily fix it by setting the conversion type explicitly in .gitattributes. Right after committing you still have the original file in your work tree and this file is not yet corrupted. You can explicitly tell git that this file is binary and git will handle the file appropriately. Unfortunately, the desired effect of cleaning up text files with mixed line endings and the undesired effect of corrupting binary files cannot be distinguished. In both cases CRLFs are removed in an irreversible way. For text files this is the right thing to do because CRLFs are line endings, while for binary files converting CRLFs corrupts data. This patch adds a mechanism that can either warn the user about an irreversible conversion or can even refuse to convert. The mechanism is controlled by the variable core.safecrlf, with the following values: - false: disable safecrlf mechanism - warn: warn about irreversible conversions - true: refuse irreversible conversions The default is to warn. Users are only affected by this default if core.autocrlf is set. But the current default of git is to leave core.autocrlf unset, so users will not see warnings unless they deliberately chose to activate the autocrlf mechanism. The safecrlf mechanism's details depend on the git command. The general principles when safecrlf is active (not false) are: - we warn/error out if files in the work tree can modified in an irreversible way without giving the user a chance to backup the original file. - for read-only operations that do not modify files in the work tree we do not not print annoying warnings. There are exceptions. Even though... - "git add" itself does not touch the files in the work tree, the next checkout would, so the safety triggers; - "git apply" to update a text file with a patch does touch the files in the work tree, but the operation is about text files and CRLF conversion is about fixing the line ending inconsistencies, so the safety does not trigger; - "git diff" itself does not touch the files in the work tree, it is often run to inspect the changes you intend to next "git add". To catch potential problems early, safety triggers. The concept of a safety check was originally proposed in a similar way by Linus Torvalds. Thanks to Dimitry Potapov for insisting on getting the naked LF/autocrlf=true case right. Signed-off-by: Steffen Prohaska --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5b8afdcb0..562c20ed4 100644 --- a/diff.c +++ b/diff.c @@ -1624,7 +1624,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) * Convert from working tree format to canonical git format */ strbuf_init(&buf, 0); - if (convert_to_git(s->path, s->data, s->size, &buf)) { + if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; -- cgit v1.2.1 From 64f30e948be5fb2cd2f65e91c0eab5ab4719c636 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 11 Feb 2008 10:53:56 -0800 Subject: diff.c: guard config parser from value=NULL diff.external, diff.*.command, diff.color.*, color.diff.* and diff.*.funcname configuration variables expect a string value. Signed-off-by: Junio C Hamano --- diff.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5b8afdcb0..4d2e23ae1 100644 --- a/diff.c +++ b/diff.c @@ -158,6 +158,8 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.external")) { + if (!value) + return config_error_nonbool(var); external_diff_cmd_cfg = xstrdup(value); return 0; } @@ -165,8 +167,11 @@ int git_diff_ui_config(const char *var, const char *value) const char *ep = strrchr(var, '.'); if (ep != var + 4) { - if (!strcmp(ep, ".command")) + if (!strcmp(ep, ".command")) { + if (!value) + return config_error_nonbool(var); return parse_lldiff_command(var, ep, value); + } } } @@ -177,6 +182,8 @@ int git_diff_basic_config(const char *var, const char *value) { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); + if (!value) + return config_error_nonbool(var); color_parse(value, var, diff_colors[slot]); return 0; } @@ -184,8 +191,11 @@ int git_diff_basic_config(const char *var, const char *value) if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { - if (!strcmp(ep, ".funcname")) + if (!strcmp(ep, ".funcname")) { + if (!value) + return config_error_nonbool(var); return parse_funcname_pattern(var, ep, value); + } } } -- cgit v1.2.1 From 7df7c019c2a46672c12a11a45600cdc698e03029 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 12 Feb 2008 13:26:31 -0800 Subject: Add "--dirstat" for some directory statistics This adds a new form of overview diffstat output, doing something that I have occasionally ended up doing manually (and badly, because it's actually pretty nasty to do), and that I think is very useful for an project like the kernel that has a fairly deep and well-separated directory structure with semantic meaning. What I mean by that is that it's often interesting to see exactly which sub-directories are impacted by a patch, and to what degree - even if you don't perhaps care so much about the individual files themselves. What makes the concept more interesting is that the "impact" is often hierarchical: in the kernel, for example, something could either have a very localized impact to "fs/ext3/" and then it's interesting to see that such a patch changes mostly that subdirectory, but you could have another patch that changes some generic VFS-layer issue which affects _many_ subdirectories that are all under "fs/", but none - or perhaps just a couple of them - of the individual filesystems are interesting in themselves. So what commonly happens is that you may have big changes in a specific sub-subdirectory, but still also significant separate changes to the subdirectory leading up to that - maybe you have significant VFS-level changes, but *also* changes under that VFS layer in the NFS-specific directories, for example. In that case, you do want the low-level parts that are significant to show up, but then the insignificant ones should show up as under the more generic top-level directory. This patch shows all of that with "--dirstat". The output can be either something simple like commit 81772fe... Author: Thomas Gleixner Date: Sun Feb 10 23:57:36 2008 +0100 x86: remove over noisy debug printk pageattr-test.c contains a noisy debug printk that people reported. The condition under which it prints (randomly tapping into a mem_map[] hole and not being able to c_p_a() there) is valid behavior and not interesting to report. Remove it. Signed-off-by: Thomas Gleixner Acked-by: Ingo Molnar Signed-off-by: Linus Torvalds 100.0% arch/x86/mm/ or something much more complex like commit e231c2e... Author: David Howells Date: Thu Feb 7 00:15:26 2008 -0800 Convert ERR_PTR(PTR_ERR(p)) instances to ERR_CAST(p) 20.5% crypto/ 7.6% fs/afs/ 7.6% fs/fuse/ 7.6% fs/gfs2/ 5.1% fs/jffs2/ 5.1% fs/nfs/ 5.1% fs/nfsd/ 7.6% fs/reiserfs/ 15.3% fs/ 7.6% net/rxrpc/ 10.2% security/keys/ where that latter example is an example of significant work in some individual fs/*/ subdirectories (like the patches to reiserfs accounting for 7.6% of the whole), but then discounting those individual filesystems, there's also 15.3% other "random" things that weren't worth reporting on their oen left over under fs/ in general (either in that directory itself, or in subdirectories of fs/ that didn't have enough changes to be reported individually). I'd like to stress that the "15.3% fs/" mentioned above is the stuff that is under fs/ but that was _not_ significant enough to report on its own. So the above does _not_ mean that 15.3% of the work was under fs/ per se, because that 15.3% does *not* include the already-reported 7.6% of afs, 7.6% of fuse etc. If you want to enable "cumulative" directory statistics, you can use the "--cumulative" flag, which adds up percentages recursively even when they have been already reported for a sub-directory. That cumulative output is disabled if *all* of the changes in one subdirectory come from a deeper subdirectory, to avoid repeating subdirectories all the way to the root. For an example of the cumulative reporting, the above commit becomes commit e231c2e... Author: David Howells Date: Thu Feb 7 00:15:26 2008 -0800 Convert ERR_PTR(PTR_ERR(p)) instances to ERR_CAST(p) 20.5% crypto/ 7.6% fs/afs/ 7.6% fs/fuse/ 7.6% fs/gfs2/ 5.1% fs/jffs2/ 5.1% fs/nfs/ 5.1% fs/nfsd/ 7.6% fs/reiserfs/ 61.5% fs/ 7.6% net/rxrpc/ 10.2% security/keys/ in which the commit percentages now obviously add up to much more than 100%: now the changes that were already reported for the sub-directories under fs/ are then cumulatively included in the whole percentage of fs/ (ie now shows 61.5% as opposed to the 15.3% without the cumulative reporting). The default reporting limit has been arbitrarily set at 3%, which seems to be a pretty good cut-off, but you can specify the cut-off manually by giving it as an option parameter (eg "--dirstat=5" makes the cut-off be at 5% instead) NOTE! The percentages are purely about the total lines added and removed, not anything smarter (or dumber) than that. Also note that you should not generally expect things to add up to 100%: not only does it round down, we don't report leftover scraps (they add up to the top-level change count, but we don't even bother reporting that, it only reports subdirectories). Quite frankly, as a top-level manager this is really convenient for me, but it's going to be very boring for git itself since there are few subdirectories. Also, don't expect things to make tons of sense if you combine this with "-M" and there are cross-directory renames etc. But even for git itself, you can get some fun statistics. Try out git log --dirstat and see the occasional mentions of things like Documentation/, git-gui/, gitweb/ and gitk-git/. Or try out something like git diff --dirstat v1.5.0..v1.5.4 which does kind of git an overview that shows *something*. But in general, the output is more exciting for big projects with deeper structure, and doing a git diff --dirstat v2.6.24..v2.6.25-rc1 on the kernel is what I actually wrote this for! Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index cd8bc4dcc..dd374d436 100644 --- a/diff.c +++ b/diff.c @@ -990,6 +990,85 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) } } +struct diffstat_dir { + struct diffstat_file **files; + int nr, percent, cumulative; +}; + +static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) +{ + unsigned long this_dir = 0; + unsigned int sources = 0; + + while (dir->nr) { + struct diffstat_file *f = *dir->files; + int namelen = strlen(f->name); + unsigned long this; + char *slash; + + if (namelen < baselen) + break; + if (memcmp(f->name, base, baselen)) + break; + slash = strchr(f->name + baselen, '/'); + if (slash) { + int newbaselen = slash + 1 - f->name; + this = gather_dirstat(dir, changed, f->name, newbaselen); + sources++; + } else { + this = f->added + f->deleted; + dir->files++; + dir->nr--; + sources += 2; + } + this_dir += this; + } + + /* + * We don't report dirstat's for + * - the top level + * - or cases where everything came from a single directory + * under this directory (sources == 1). + */ + if (baselen && sources != 1) { + int permille = this_dir * 1000 / changed; + if (permille) { + int percent = permille / 10; + if (percent >= dir->percent) { + printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base); + if (!dir->cumulative) + return 0; + } + } + } + return this_dir; +} + +static void show_dirstat(struct diffstat_t *data, struct diff_options *options) +{ + int i; + unsigned long changed; + struct diffstat_dir dir; + + /* Calculate total changes */ + changed = 0; + for (i = 0; i < data->nr; i++) { + changed += data->files[i]->added; + changed += data->files[i]->deleted; + } + + /* This can happen even with many files, if everything was renames */ + if (!changed) + return; + + /* Show all directories with more than x% of the changes */ + dir.files = data->files; + dir.nr = data->nr; + dir.percent = options->dirstat_percent; + dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; + gather_dirstat(&dir, changed, "", 0); +} + static void free_diffstat_info(struct diffstat_t *diffstat) { int i; @@ -2058,6 +2137,7 @@ void diff_setup(struct diff_options *options) options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; + options->dirstat_percent = 3; options->context = 3; options->msg_sep = ""; @@ -2099,6 +2179,7 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_NUMSTAT | DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SHORTSTAT | + DIFF_FORMAT_DIRSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH); @@ -2110,6 +2191,7 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_NUMSTAT | DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SHORTSTAT | + DIFF_FORMAT_DIRSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_CHECKDIFF)) DIFF_OPT_SET(options, RECURSIVE); @@ -2220,6 +2302,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_NUMSTAT; else if (!strcmp(arg, "--shortstat")) options->output_format |= DIFF_FORMAT_SHORTSTAT; + else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent)) + options->output_format |= DIFF_FORMAT_DIRSTAT; + else if (!strcmp(arg, "--cumulative")) + options->output_format |= DIFF_FORMAT_CUMULATIVE; else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) @@ -2938,7 +3024,7 @@ void diff_flush(struct diff_options *options) separator++; } - if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) { + if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) { struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); @@ -2948,6 +3034,8 @@ void diff_flush(struct diff_options *options) if (check_pair_status(p)) diff_flush_stat(p, options, &diffstat); } + if (output_format & DIFF_FORMAT_DIRSTAT) + show_dirstat(&diffstat, options); if (output_format & DIFF_FORMAT_NUMSTAT) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) -- cgit v1.2.1 From cd676a513672eeb9663c6d4de276a1c860a4b879 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Feb 2008 14:26:02 -0800 Subject: diff --relative: output paths as relative to the current subdirectory This adds --relative option to the diff family. When you start from a subdirectory: $ git diff --relative shows only the diff that is inside your current subdirectory, and without $prefix part. People who usually live in subdirectories may like it. There are a few things I should also mention about the change: - This works not just with diff but also works with the log family of commands, but the history pruning is not affected. In other words, if you go to a subdirectory, you can say: $ git log --relative -p but it will show the log message even for commits that do not touch the current directory. You can limit it by giving pathspec yourself: $ git log --relative -p . This originally was not a conscious design choice, but we have a way to affect diff pathspec and pruning pathspec independently. IOW "git log --full-diff -p ." tells it to prune history to commits that affect the current subdirectory but show the changes with full context. I think it makes more sense to leave pruning independent from --relative than the obvious alternative of always pruning with the current subdirectory, which would break the symmetry. - Because this works also with the log family, you could format-patch a single change, limiting the effect to your subdirectory, like so: $ cd gitk-git $ git format-patch -1 --relative 911f1eb But because that is a special purpose usage, this option will never become the default, with or without repository or user preference configuration. The risk of producing a partial patch and sending it out by mistake is too great if we did so. - This is inherently incompatible with --no-index, which is a bolted-on hack that does not have much to do with git itself. I didn't bother checking and erroring out on the combined use of the options, but probably I should. Signed-off-by: Junio C Hamano --- diff.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 17 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5b8afdcb0..db4bd557b 100644 --- a/diff.c +++ b/diff.c @@ -1397,6 +1397,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, } static void builtin_checkdiff(const char *name_a, const char *name_b, + const char *attr_path, struct diff_filespec *one, struct diff_filespec *two, struct diff_options *o) { @@ -1411,7 +1412,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.filename = name_b ? name_b : name_a; data.lineno = 0; data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); - data.ws_rule = whitespace_rule(data.filename); + data.ws_rule = whitespace_rule(attr_path); if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1831,6 +1832,9 @@ static const char *external_diff_attr(const char *name) { struct git_attr_check attr_diff_check; + if (!name) + return NULL; + setup_diff_attr_check(&attr_diff_check); if (!git_checkattr(name, 1, &attr_diff_check)) { const char *value = attr_diff_check.value; @@ -1850,6 +1854,7 @@ static const char *external_diff_attr(const char *name) static void run_diff_cmd(const char *pgm, const char *name, const char *other, + const char *attr_path, struct diff_filespec *one, struct diff_filespec *two, const char *xfrm_msg, @@ -1859,7 +1864,7 @@ static void run_diff_cmd(const char *pgm, if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { - const char *cmd = external_diff_attr(name); + const char *cmd = external_diff_attr(attr_path); if (cmd) pgm = cmd; } @@ -1900,6 +1905,15 @@ static int similarity_index(struct diff_filepair *p) return p->score * 100 / MAX_SCORE; } +static void strip_prefix(int prefix_length, const char **namep, const char **otherp) +{ + /* Strip the prefix but do not molest /dev/null and absolute paths */ + if (*namep && **namep != '/') + *namep += prefix_length; + if (*otherp && **otherp != '/') + *otherp += prefix_length; +} + static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); @@ -1909,16 +1923,21 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) struct diff_filespec *two = p->two; const char *name; const char *other; + const char *attr_path; int complete_rewrite = 0; + name = p->one->path; + other = (strcmp(name, p->two->path) ? p->two->path : NULL); + attr_path = name; + if (o->prefix_length) + strip_prefix(o->prefix_length, &name, &other); if (DIFF_PAIR_UNMERGED(p)) { - run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0); + run_diff_cmd(pgm, name, NULL, attr_path, + NULL, NULL, NULL, o, 0); return; } - name = p->one->path; - other = (strcmp(name, p->two->path) ? p->two->path : NULL); diff_fill_sha1_info(one); diff_fill_sha1_info(two); @@ -1981,15 +2000,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) * needs to be split into deletion and creation. */ struct diff_filespec *null = alloc_filespec(two->path); - run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0); + run_diff_cmd(NULL, name, other, attr_path, + one, null, xfrm_msg, o, 0); free(null); null = alloc_filespec(one->path); - run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0); + run_diff_cmd(NULL, name, other, attr_path, + null, two, xfrm_msg, o, 0); free(null); } else - run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o, - complete_rewrite); + run_diff_cmd(pgm, name, other, attr_path, + one, two, xfrm_msg, o, complete_rewrite); strbuf_release(&msg); } @@ -2010,6 +2031,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); + if (o->prefix_length) + strip_prefix(o->prefix_length, &name, &other); + diff_fill_sha1_info(p->one); diff_fill_sha1_info(p->two); @@ -2022,6 +2046,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) { const char *name; const char *other; + const char *attr_path; if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ @@ -2030,11 +2055,15 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); + attr_path = other ? other : name; + + if (o->prefix_length) + strip_prefix(o->prefix_length, &name, &other); diff_fill_sha1_info(p->one); diff_fill_sha1_info(p->two); - builtin_checkdiff(name, other, p->one, p->two, o); + builtin_checkdiff(name, other, attr_path, p->one, p->two, o); } void diff_setup(struct diff_options *options) @@ -2076,6 +2105,13 @@ int diff_setup_done(struct diff_options *options) if (DIFF_OPT_TST(options, FIND_COPIES_HARDER)) options->detect_rename = DIFF_DETECT_COPY; + if (!DIFF_OPT_TST(options, RELATIVE_NAME)) + options->prefix = NULL; + if (options->prefix) + options->prefix_length = strlen(options->prefix); + else + options->prefix_length = 0; + if (options->output_format & (DIFF_FORMAT_NAME | DIFF_FORMAT_NAME_STATUS | DIFF_FORMAT_CHECKDIFF | @@ -2264,6 +2300,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--no-renames")) options->detect_rename = 0; + else if (!strcmp(arg, "--relative")) + DIFF_OPT_SET(options, RELATIVE_NAME); /* xdiff options */ else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) @@ -2475,12 +2513,20 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) printf("%c%c", p->status, inter_name_termination); } - if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) { - write_name_quoted(p->one->path, stdout, inter_name_termination); - write_name_quoted(p->two->path, stdout, line_termination); + if (p->status == DIFF_STATUS_COPIED || + p->status == DIFF_STATUS_RENAMED) { + const char *name_a, *name_b; + name_a = p->one->path; + name_b = p->two->path; + strip_prefix(opt->prefix_length, &name_a, &name_b); + write_name_quoted(name_a, stdout, inter_name_termination); + write_name_quoted(name_b, stdout, line_termination); } else { - const char *path = p->one->mode ? p->one->path : p->two->path; - write_name_quoted(path, stdout, line_termination); + const char *name_a, *name_b; + name_a = p->one->mode ? p->one->path : p->two->path; + name_b = NULL; + strip_prefix(opt->prefix_length, &name_a, &name_b); + write_name_quoted(name_a, stdout, line_termination); } } @@ -2677,8 +2723,13 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) diff_flush_checkdiff(p, opt); else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) diff_flush_raw(p, opt); - else if (fmt & DIFF_FORMAT_NAME) - write_name_quoted(p->two->path, stdout, opt->line_termination); + else if (fmt & DIFF_FORMAT_NAME) { + const char *name_a, *name_b; + name_a = p->two->path; + name_b = NULL; + strip_prefix(opt->prefix_length, &name_a, &name_b); + write_name_quoted(name_a, stdout, opt->line_termination); + } } static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) @@ -3164,6 +3215,11 @@ void diff_addremove(struct diff_options *options, if (!path) path = ""; sprintf(concatpath, "%s%s", base, path); + + if (options->prefix && + strncmp(concatpath, options->prefix, options->prefix_length)) + return; + one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); @@ -3193,6 +3249,11 @@ void diff_change(struct diff_options *options, } if (!path) path = ""; sprintf(concatpath, "%s%s", base, path); + + if (options->prefix && + strncmp(concatpath, options->prefix, options->prefix_length)) + return; + one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); fill_filespec(one, old_sha1, old_mode); @@ -3207,6 +3268,11 @@ void diff_unmerge(struct diff_options *options, unsigned mode, const unsigned char *sha1) { struct diff_filespec *one, *two; + + if (options->prefix && + strncmp(path, options->prefix, options->prefix_length)) + return; + one = alloc_filespec(path); two = alloc_filespec(path); fill_filespec(one, sha1, mode); -- cgit v1.2.1 From c0cb4a067972700f0682fbab13768bcc7dc7a3c3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 13 Feb 2008 00:34:39 -0800 Subject: diff --relative: help working in a bare repository This allows the --relative option to say which subdirectory to pretend to be in, so that in a bare repository, you can say: $ git log --relative=drivers/ v2.6.20..v2.6.22 -- drivers/scsi/ Signed-off-by: Junio C Hamano --- diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index db4bd557b..2b89b1600 100644 --- a/diff.c +++ b/diff.c @@ -2302,6 +2302,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->detect_rename = 0; else if (!strcmp(arg, "--relative")) DIFF_OPT_SET(options, RELATIVE_NAME); + else if (!prefixcmp(arg, "--relative=")) { + DIFF_OPT_SET(options, RELATIVE_NAME); + options->prefix = arg + 11; + } /* xdiff options */ else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) -- cgit v1.2.1 From 588071112c7ca14a04d674e4f019572f0bb77326 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 15 Feb 2008 09:37:54 -0800 Subject: diff.c: fixup garding of config parser from value=NULL Christian Couder noticed that there still were a handcrafted error() call that we should have converted to config_error_nonbool() where parse_lldiff_command() parses the configuration file. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 4d2e23ae1..2aaace890 100644 --- a/diff.c +++ b/diff.c @@ -87,7 +87,7 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val } if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); drv->cmd = strdup(value); return 0; } -- cgit v1.2.1 From 8ca496e97d100195c9015f4da8ddaad693e91961 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 14 Feb 2008 06:50:00 +0100 Subject: diff.c: replace a 'strdup' with 'xstrdup'. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 2aaace890..047310fc9 100644 --- a/diff.c +++ b/diff.c @@ -88,7 +88,7 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val if (!value) return config_error_nonbool(var); - drv->cmd = strdup(value); + drv->cmd = xstrdup(value); return 0; } -- cgit v1.2.1 From 2c778210f8877e8f5c88715c2d25d1a43d976566 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 16 Feb 2008 05:59:53 +0100 Subject: diff.c: remove useless check for value != NULL It is not necessary to check if value != NULL before calling 'parse_lldiff_command' as there is already a check inside this function. By the way this patch also improves the existing check inside 'parse_lldiff_command' by using: return config_error_nonbool(var); instead of: return error("%s: lacks value", var); Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- diff.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 047310fc9..ed41ce418 100644 --- a/diff.c +++ b/diff.c @@ -166,13 +166,8 @@ int git_diff_ui_config(const char *var, const char *value) if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); - if (ep != var + 4) { - if (!strcmp(ep, ".command")) { - if (!value) - return config_error_nonbool(var); - return parse_lldiff_command(var, ep, value); - } - } + if (ep != var + 4 && !strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); } return git_diff_basic_config(var, value); -- cgit v1.2.1 From b20a60d0c0f23384cb4141d75d9a7c90e99c1432 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 16 Feb 2008 06:02:17 +0100 Subject: diff.c: add "const" qualifier to "char *cmd" member of "struct ll_diff_driver" Also use "git_config_string" to simplify code where "cmd" is set. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- diff.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index ed41ce418..f082f55bc 100644 --- a/diff.c +++ b/diff.c @@ -57,7 +57,7 @@ static int parse_diff_color_slot(const char *var, int ofs) static struct ll_diff_driver { const char *name; struct ll_diff_driver *next; - char *cmd; + const char *cmd; } *user_diff, **user_diff_tail; /* @@ -86,10 +86,7 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val user_diff_tail = &(drv->next); } - if (!value) - return config_error_nonbool(var); - drv->cmd = xstrdup(value); - return 0; + return git_config_string(&(drv->cmd), var, value); } /* -- cgit v1.2.1 From 0ef617f4b6ea78ad63dd11e90f8c854238176981 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 15 Feb 2008 20:30:05 -0800 Subject: diff: Fix miscounting of --check output c1795bb (Unify whitespace checking) incorrectly made the checking function return without incrementing the line numbers when there is no whitespace problem is found on a '+' line. This resurrects the earlier behaviour. Noticed and reported by Jay Soffian. The test script was stolen from Jay's independent fix. Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f082f55bc..39f6e21aa 100644 --- a/diff.c +++ b/diff.c @@ -1013,6 +1013,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) char *err; if (line[0] == '+') { + data->lineno++; data->status = check_and_emit_line(line + 1, len - 1, data->ws_rule, NULL, NULL, NULL, NULL); if (!data->status) @@ -1023,13 +1024,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) emit_line(set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, stdout, set, reset, ws); - data->lineno++; } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) - data->lineno = strtol(plus, NULL, 10); + data->lineno = strtol(plus, NULL, 10) - 1; else die("invalid diff"); } -- cgit v1.2.1 From 6b2f2d9805dd22c6f74957e0d76a1d2921b40c16 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 18 Feb 2008 08:26:03 +0100 Subject: Add color.ui variable which globally enables colorization if set Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano --- diff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 58fe7750f..c30c25227 100644 --- a/diff.c +++ b/diff.c @@ -20,7 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 100; -static int diff_use_color_default; +int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@ -191,7 +191,7 @@ int git_diff_basic_config(const char *var, const char *value) } } - return git_default_config(var, value); + return git_color_default_config(var, value); } static char *quote_two(const char *one, const char *two) @@ -2055,7 +2055,7 @@ void diff_setup(struct diff_options *options) options->change = diff_change; options->add_remove = diff_addremove; - if (diff_use_color_default) + if (diff_use_color_default > 0) DIFF_OPT_SET(options, COLOR_DIFF); else DIFF_OPT_CLR(options, COLOR_DIFF); -- cgit v1.2.1 From 14a5c7c19351a7db56803c9086b133fe131f55f6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 20 Feb 2008 19:01:16 -0500 Subject: diff: fix java funcname pattern for solaris The Solaris regex library doesn't like having the '$' anchor inside capture parentheses. It rejects the match, causing t4018 to fail. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c30c25227..699b21f4e 100644 --- a/diff.c +++ b/diff.c @@ -1199,7 +1199,7 @@ static struct builtin_funcname_pattern { "new\\|return\\|switch\\|throw\\|while\\)\n" "^[ ]*\\(\\([ ]*" "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*$\\)" }, + "[ ]*([^;]*\\)$" }, { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, }; -- cgit v1.2.1 From 8e0f70033b2bd1679a6e5971978fdc3ee09bdb72 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 31 Jan 2008 18:26:32 +0100 Subject: Avoid unnecessary "if-before-free" tests. This change removes all obvious useless if-before-free tests. E.g., it replaces code like this: if (some_expression) free (some_expression); with the now-equivalent: free (some_expression); It is equivalent not just because POSIX has required free(NULL) to work for a long time, but simply because it has worked for so long that no reasonable porting target fails the test. Here's some evidence from nearly 1.5 years ago: http://www.winehq.org/pipermail/wine-patches/2006-October/031544.html FYI, the change below was prepared by running the following: git ls-files -z | xargs -0 \ perl -0x3b -pi -e \ 's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\(\s*\1\s*\))/$2/s' Note however, that it doesn't handle brace-enclosed blocks like "if (x) { free (x); }". But that's ok, since there were none like that in git sources. Beware: if you do use the above snippet, note that it can produce syntactically invalid C code. That happens when the affected "if"-statement has a matching "else". E.g., it would transform this if (x) free (x); else foo (); into this: free (x); else foo (); There were none of those here, either. If you're interested in automating detection of the useless tests, you might like the useless-if-before-free script in gnulib: [it *does* detect brace-enclosed free statements, and has a --name=S option to make it detect free-like functions with different names] http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=build-aux/useless-if-before-free Addendum: Remove one more (in imap-send.c), spotted by Jean-Luc Herren . Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- diff.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 699b21f4e..f08e663b8 100644 --- a/diff.c +++ b/diff.c @@ -118,8 +118,7 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v pp->next = funcname_pattern_list; funcname_pattern_list = pp; } - if (pp->pattern) - free(pp->pattern); + free(pp->pattern); pp->pattern = xstrdup(value); return 0; } @@ -492,10 +491,8 @@ static void free_diff_words_data(struct emit_callback *ecbdata) ecbdata->diff_words->plus.text.size) diff_words_show(ecbdata->diff_words); - if (ecbdata->diff_words->minus.text.ptr) - free (ecbdata->diff_words->minus.text.ptr); - if (ecbdata->diff_words->plus.text.ptr) - free (ecbdata->diff_words->plus.text.ptr); + free (ecbdata->diff_words->minus.text.ptr); + free (ecbdata->diff_words->plus.text.ptr); free(ecbdata->diff_words); ecbdata->diff_words = NULL; } -- cgit v1.2.1 From 2b0b551d7662a4246ed55b6a7029ba3caa65cf98 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 24 Feb 2008 17:37:15 -0800 Subject: diff --dirstat: saner handling of binary and unmerged files We do not account binary nor unmerged files when --shortstat is asked for (or the summary stat at the end of --stat). The new option --dirstat should work the same way as it is about summarizing the changes of multiple files by adding them up. Signed-off-by: Junio C Hamano --- diff.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dd374d436..bcc323f19 100644 --- a/diff.c +++ b/diff.c @@ -1016,7 +1016,10 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons this = gather_dirstat(dir, changed, f->name, newbaselen); sources++; } else { - this = f->added + f->deleted; + if (f->is_unmerged || f->is_binary) + this = 0; + else + this = f->added + f->deleted; dir->files++; dir->nr--; sources += 2; @@ -1053,6 +1056,8 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options) /* Calculate total changes */ changed = 0; for (i = 0; i < data->nr; i++) { + if (data->files[i]->is_binary || data->files[i]->is_unmerged) + continue; changed += data->files[i]->added; changed += data->files[i]->deleted; } -- cgit v1.2.1 From 1468bd47833c6ec3c85620d6af1d910e9378f714 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 25 Feb 2008 14:24:48 -0500 Subject: Use a single implementation and API for copy_file() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally by Kristian Hᅵgsberg; I fixed the conversion of rerere, which had a different API. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- diff.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 39f6e21aa..76ba5f4af 100644 --- a/diff.c +++ b/diff.c @@ -272,8 +272,8 @@ static void print_line_count(int count) } } -static void copy_file(int prefix, const char *data, int size, - const char *set, const char *reset) +static void copy_file_with_prefix(int prefix, const char *data, int size, + const char *set, const char *reset) { int ch, nl_just_seen = 1; while (0 < size--) { @@ -331,9 +331,9 @@ static void emit_rewrite_diff(const char *name_a, print_line_count(lc_b); printf(" @@%s\n", reset); if (lc_a) - copy_file('-', one->data, one->size, old, reset); + copy_file_with_prefix('-', one->data, one->size, old, reset); if (lc_b) - copy_file('+', two->data, two->size, new, reset); + copy_file_with_prefix('+', two->data, two->size, new, reset); } static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) -- cgit v1.2.1 From 2efb3b061709bd204f11d54c177dae89e4af0f67 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 1 Mar 2008 23:43:32 -0800 Subject: Clean up find_unique_abbrev() callers Now find_unique_abbrev() never returns NULL, there is no need for callers to prepare for seeing NULL and fall back to giving the full 40-hexdigits. While we are at it, drop "..." in the "git reset" output that reports the location of the new HEAD, between the abbreviated commit object name and the one line commit summary. Because we are always showing the HEAD (which cannot be missing!), we never had a case where we show the full 40 hexdigits that is not followed by three dots, and these three dots were stealing 3 columns from the precious horizontal screen real estate out of 80 that can better be used for the one line commit summary. Signed-off-by: Junio C Hamano --- diff.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index ad1616423..b80f656fb 100644 --- a/diff.c +++ b/diff.c @@ -2581,8 +2581,6 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len) return sha1_to_hex(sha1); abbrev = find_unique_abbrev(sha1, len); - if (!abbrev) - return sha1_to_hex(sha1); abblen = strlen(abbrev); if (abblen < 37) { static char hex[41]; -- cgit v1.2.1 From 2b459b483cb264d3e9a39cd7857da2ccab08179f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Mar 2008 00:07:59 -0800 Subject: diff: make sure work tree side is shown as 0{40} when different Ping Yin noticed that "git diff-index --raw" shows 0{40} when work tree has submodule difference, but "git diff --raw" didn't correctly do so. There was a mistake in the diffcore_skip_stat_unmatch() that was meant to clean up the stat-only difference for running diff between the index and work tree and diff between the tree and the work tree, to cause it re-read from the submodule repository HEAD. When ce_stat_match() says work tree is different, we should always say 0{40} on the work tree side. This patch fixes the issue, and adds tests. Signed-off-by: Junio C Hamano --- diff.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b80f656fb..00e1590c6 100644 --- a/diff.c +++ b/diff.c @@ -3187,11 +3187,8 @@ static void diffcore_apply_filter(const char *filter) static int diff_filespec_is_identical(struct diff_filespec *one, struct diff_filespec *two) { - if (S_ISGITLINK(one->mode)) { - diff_fill_sha1_info(one); - diff_fill_sha1_info(two); - return !hashcmp(one->sha1, two->sha1); - } + if (S_ISGITLINK(one->mode)) + return 0; if (diff_populate_filespec(one, 0)) return 0; if (diff_populate_filespec(two, 0)) -- cgit v1.2.1 From c0c77734bf313455364b3be7d3537d40e1138bcc Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 9 Mar 2008 22:43:39 -0400 Subject: Write diff output to a file in struct diff_options Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- diff.c | 306 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 166 insertions(+), 140 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 00e1590c6..8022e678d 100644 --- a/diff.c +++ b/diff.c @@ -256,40 +256,41 @@ static int count_lines(const char *data, int size) return count; } -static void print_line_count(int count) +static void print_line_count(FILE *file, int count) { switch (count) { case 0: - printf("0,0"); + fprintf(file, "0,0"); break; case 1: - printf("1"); + fprintf(file, "1"); break; default: - printf("1,%d", count); + fprintf(file, "1,%d", count); break; } } -static void copy_file_with_prefix(int prefix, const char *data, int size, +static void copy_file_with_prefix(FILE *file, + int prefix, const char *data, int size, const char *set, const char *reset) { int ch, nl_just_seen = 1; while (0 < size--) { ch = *data++; if (nl_just_seen) { - fputs(set, stdout); - putchar(prefix); + fputs(set, file); + putc(prefix, file); } if (ch == '\n') { nl_just_seen = 1; - fputs(reset, stdout); + fputs(reset, file); } else nl_just_seen = 0; - putchar(ch); + putc(ch, file); } if (!nl_just_seen) - printf("%s\n\\ No newline at end of file\n", reset); + fprintf(file, "%s\n\\ No newline at end of file\n", reset); } static void emit_rewrite_diff(const char *name_a, @@ -322,17 +323,18 @@ static void emit_rewrite_diff(const char *name_a, diff_populate_filespec(two, 0); lc_a = count_lines(one->data, one->size); lc_b = count_lines(two->data, two->size); - printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", - metainfo, a_name.buf, name_a_tab, reset, - metainfo, b_name.buf, name_b_tab, reset, fraginfo); - print_line_count(lc_a); - printf(" +"); - print_line_count(lc_b); - printf(" @@%s\n", reset); + fprintf(o->file, + "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", + metainfo, a_name.buf, name_a_tab, reset, + metainfo, b_name.buf, name_b_tab, reset, fraginfo); + print_line_count(o->file, lc_a); + fprintf(o->file, " +"); + print_line_count(o->file, lc_b); + fprintf(o->file, " @@%s\n", reset); if (lc_a) - copy_file_with_prefix('-', one->data, one->size, old, reset); + copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset); if (lc_b) - copy_file_with_prefix('+', two->data, two->size, new, reset); + copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset); } static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) @@ -372,9 +374,10 @@ static void diff_words_append(char *line, unsigned long len, struct diff_words_data { struct xdiff_emit_state xm; struct diff_words_buffer minus, plus; + FILE *file; }; -static void print_word(struct diff_words_buffer *buffer, int len, int color, +static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color, int suppress_newline) { const char *ptr; @@ -391,15 +394,15 @@ static void print_word(struct diff_words_buffer *buffer, int len, int color, len--; } - fputs(diff_get_color(1, color), stdout); - fwrite(ptr, len, 1, stdout); - fputs(diff_get_color(1, DIFF_RESET), stdout); + fputs(diff_get_color(1, color), file); + fwrite(ptr, len, 1, file); + fputs(diff_get_color(1, DIFF_RESET), file); if (eol) { if (suppress_newline) buffer->suppressed_newline = 1; else - putchar('\n'); + putc('\n', file); } } @@ -409,20 +412,23 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) if (diff_words->minus.suppressed_newline) { if (line[0] != '+') - putchar('\n'); + putc('\n', diff_words->file); diff_words->minus.suppressed_newline = 0; } len--; switch (line[0]) { case '-': - print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1); + print_word(diff_words->file, + &diff_words->minus, len, DIFF_FILE_OLD, 1); break; case '+': - print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0); + print_word(diff_words->file, + &diff_words->plus, len, DIFF_FILE_NEW, 0); break; case ' ': - print_word(&diff_words->plus, len, DIFF_PLAIN, 0); + print_word(diff_words->file, + &diff_words->plus, len, DIFF_PLAIN, 0); diff_words->minus.current += len; break; } @@ -466,7 +472,7 @@ static void diff_words_show(struct diff_words_data *diff_words) diff_words->minus.text.size = diff_words->plus.text.size = 0; if (diff_words->minus.suppressed_newline) { - putchar('\n'); + putc('\n', diff_words->file); diff_words->minus.suppressed_newline = 0; } } @@ -481,6 +487,7 @@ struct emit_callback { const char **label_path; struct diff_words_data *diff_words; int *found_changesp; + FILE *file; }; static void free_diff_words_data(struct emit_callback *ecbdata) @@ -505,11 +512,11 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) return ""; } -static void emit_line(const char *set, const char *reset, const char *line, int len) +static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len) { - fputs(set, stdout); - fwrite(line, len, 1, stdout); - fputs(reset, stdout); + fputs(set, file); + fwrite(line, len, 1, file); + fputs(reset, file); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) @@ -518,13 +525,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW); if (!*ws) - emit_line(set, reset, line, len); + emit_line(ecbdata->file, set, reset, line, len); else { /* Emit just the prefix, then the rest. */ - emit_line(set, reset, line, ecbdata->nparents); + emit_line(ecbdata->file, set, reset, line, ecbdata->nparents); (void)check_and_emit_line(line + ecbdata->nparents, len - ecbdata->nparents, ecbdata->ws_rule, - stdout, set, reset, ws); + ecbdata->file, set, reset, ws); } } @@ -563,10 +570,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : ""; name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : ""; - printf("%s--- %s%s%s\n", - meta, ecbdata->label_path[0], reset, name_a_tab); - printf("%s+++ %s%s%s\n", - meta, ecbdata->label_path[1], reset, name_b_tab); + fprintf(ecbdata->file, "%s--- %s%s%s\n", + meta, ecbdata->label_path[0], reset, name_a_tab); + fprintf(ecbdata->file, "%s+++ %s%s%s\n", + meta, ecbdata->label_path[1], reset, name_b_tab); ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } @@ -578,15 +585,16 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) if (2 <= i && i < len && line[i] == ' ') { ecbdata->nparents = i - 1; len = sane_truncate_line(ecbdata, line, len); - emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO), + emit_line(ecbdata->file, + diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO), reset, line, len); if (line[len-1] != '\n') - putchar('\n'); + putc('\n', ecbdata->file); return; } if (len < ecbdata->nparents) { - emit_line(reset, reset, line, len); + emit_line(ecbdata->file, reset, reset, line, len); return; } @@ -609,7 +617,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) diff_words_show(ecbdata->diff_words); line++; len--; - emit_line(plain, reset, line, len); + emit_line(ecbdata->file, plain, reset, line, len); return; } for (i = 0; i < ecbdata->nparents && len; i++) { @@ -620,7 +628,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) } if (color != DIFF_FILE_NEW) { - emit_line(diff_get_color(ecbdata->color_diff, color), + emit_line(ecbdata->file, + diff_get_color(ecbdata->color_diff, color), reset, line, len); return; } @@ -759,20 +768,21 @@ static int scale_linear(int it, int width, int max_change) return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1); } -static void show_name(const char *prefix, const char *name, int len, +static void show_name(FILE *file, + const char *prefix, const char *name, int len, const char *reset, const char *set) { - printf(" %s%s%-*s%s |", set, prefix, len, name, reset); + fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset); } -static void show_graph(char ch, int cnt, const char *set, const char *reset) +static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset) { if (cnt <= 0) return; - printf("%s", set); + fprintf(file, "%s", set); while (cnt--) - putchar(ch); - printf("%s", reset); + putc(ch, file); + fprintf(file, "%s", reset); } static void fill_print_name(struct diffstat_file *file) @@ -877,18 +887,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } if (data->files[i]->is_binary) { - show_name(prefix, name, len, reset, set); - printf(" Bin "); - printf("%s%d%s", del_c, deleted, reset); - printf(" -> "); - printf("%s%d%s", add_c, added, reset); - printf(" bytes"); - printf("\n"); + show_name(options->file, prefix, name, len, reset, set); + fprintf(options->file, " Bin "); + fprintf(options->file, "%s%d%s", del_c, deleted, reset); + fprintf(options->file, " -> "); + fprintf(options->file, "%s%d%s", add_c, added, reset); + fprintf(options->file, " bytes"); + fprintf(options->file, "\n"); continue; } else if (data->files[i]->is_unmerged) { - show_name(prefix, name, len, reset, set); - printf(" Unmerged\n"); + show_name(options->file, prefix, name, len, reset, set); + fprintf(options->file, " Unmerged\n"); continue; } else if (!data->files[i]->is_renamed && @@ -911,17 +921,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) del = scale_linear(del, width, max_change); total = add + del; } - show_name(prefix, name, len, reset, set); - printf("%5d ", added + deleted); - show_graph('+', add, add_c, reset); - show_graph('-', del, del_c, reset); - putchar('\n'); - } - printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n", + show_name(options->file, prefix, name, len, reset, set); + fprintf(options->file, "%5d ", added + deleted); + show_graph(options->file, '+', add, add_c, reset); + show_graph(options->file, '-', del, del_c, reset); + fprintf(options->file, "\n"); + } + fprintf(options->file, + "%s %d files changed, %d insertions(+), %d deletions(-)%s\n", set, total_files, adds, dels, reset); } -static void show_shortstats(struct diffstat_t* data) +static void show_shortstats(struct diffstat_t* data, struct diff_options *options) { int i, adds = 0, dels = 0, total_files = data->nr; @@ -942,7 +953,7 @@ static void show_shortstats(struct diffstat_t* data) } } } - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", + fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n", total_files, adds, dels); } @@ -957,24 +968,25 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) struct diffstat_file *file = data->files[i]; if (file->is_binary) - printf("-\t-\t"); + fprintf(options->file, "-\t-\t"); else - printf("%d\t%d\t", file->added, file->deleted); + fprintf(options->file, + "%d\t%d\t", file->added, file->deleted); if (options->line_termination) { fill_print_name(file); if (!file->is_renamed) - write_name_quoted(file->name, stdout, + write_name_quoted(file->name, options->file, options->line_termination); else { - fputs(file->print_name, stdout); - putchar(options->line_termination); + fputs(file->print_name, options->file); + putc(options->line_termination, options->file); } } else { if (file->is_renamed) { - putchar('\0'); - write_name_quoted(file->from_name, stdout, '\0'); + putc('\0', options->file); + write_name_quoted(file->from_name, options->file, '\0'); } - write_name_quoted(file->name, stdout, '\0'); + write_name_quoted(file->name, options->file, '\0'); } } } @@ -984,7 +996,7 @@ struct diffstat_dir { int nr, percent, cumulative; }; -static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) +static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) { unsigned long this_dir = 0; unsigned int sources = 0; @@ -1002,7 +1014,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons slash = strchr(f->name + baselen, '/'); if (slash) { int newbaselen = slash + 1 - f->name; - this = gather_dirstat(dir, changed, f->name, newbaselen); + this = gather_dirstat(file, dir, changed, f->name, newbaselen); sources++; } else { if (f->is_unmerged || f->is_binary) @@ -1027,7 +1039,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons if (permille) { int percent = permille / 10; if (percent >= dir->percent) { - printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base); + fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base); if (!dir->cumulative) return 0; } @@ -1060,7 +1072,7 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options) dir.nr = data->nr; dir.percent = options->dirstat_percent; dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; - gather_dirstat(&dir, changed, "", 0); + gather_dirstat(options->file, &dir, changed, "", 0); } static void free_diffstat_info(struct diffstat_t *diffstat) @@ -1083,6 +1095,7 @@ struct checkdiff_t { int lineno, color_diff; unsigned ws_rule; unsigned status; + FILE *file; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1100,11 +1113,11 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (!data->status) return; err = whitespace_error_string(data->status); - printf("%s:%d: %s.\n", data->filename, data->lineno, err); + fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err); free(err); - emit_line(set, reset, line, 1); + emit_line(data->file, set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, - stdout, set, reset, ws); + data->file, set, reset, ws); } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { @@ -1140,7 +1153,7 @@ static unsigned char *deflate_it(char *data, return deflated; } -static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two) +static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two) { void *cp; void *delta; @@ -1169,13 +1182,13 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two) } if (delta && delta_size < deflate_size) { - printf("delta %lu\n", orig_size); + fprintf(file, "delta %lu\n", orig_size); free(deflated); data = delta; data_size = delta_size; } else { - printf("literal %lu\n", two->size); + fprintf(file, "literal %lu\n", two->size); free(delta); data = deflated; data_size = deflate_size; @@ -1193,17 +1206,18 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two) line[0] = bytes - 26 + 'a' - 1; encode_85(line + 1, cp, bytes); cp = (char *) cp + bytes; - puts(line); + fputs(line, file); + fputc('\n', file); } - printf("\n"); + fprintf(file, "\n"); free(data); } -static void emit_binary_diff(mmfile_t *one, mmfile_t *two) +static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two) { - printf("GIT binary patch\n"); - emit_binary_diff_body(one, two); - emit_binary_diff_body(two, one); + fprintf(file, "GIT binary patch\n"); + emit_binary_diff_body(file, one, two); + emit_binary_diff_body(file, two, one); } static void setup_diff_attr_check(struct git_attr_check *check) @@ -1334,25 +1348,25 @@ static void builtin_diff(const char *name_a, b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; - printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset); + fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset); if (lbl[0][0] == '/') { /* /dev/null */ - printf("%snew file mode %06o%s\n", set, two->mode, reset); + fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset); if (xfrm_msg && xfrm_msg[0]) - printf("%s%s%s\n", set, xfrm_msg, reset); + fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset); } else if (lbl[1][0] == '/') { - printf("%sdeleted file mode %06o%s\n", set, one->mode, reset); + fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset); if (xfrm_msg && xfrm_msg[0]) - printf("%s%s%s\n", set, xfrm_msg, reset); + fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset); } else { if (one->mode != two->mode) { - printf("%sold mode %06o%s\n", set, one->mode, reset); - printf("%snew mode %06o%s\n", set, two->mode, reset); + fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset); + fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset); } if (xfrm_msg && xfrm_msg[0]) - printf("%s%s%s\n", set, xfrm_msg, reset); + fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset); /* * we do not run diff between different kind * of objects. @@ -1376,10 +1390,10 @@ static void builtin_diff(const char *name_a, !memcmp(mf1.ptr, mf2.ptr, mf1.size)) goto free_ab_and_return; if (DIFF_OPT_TST(o, BINARY)) - emit_binary_diff(&mf1, &mf2); + emit_binary_diff(o->file, &mf1, &mf2); else - printf("Binary files %s and %s differ\n", - lbl[0], lbl[1]); + fprintf(o->file, "Binary files %s and %s differ\n", + lbl[0], lbl[1]); o->found_changes = 1; } else { @@ -1401,6 +1415,7 @@ static void builtin_diff(const char *name_a, ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); ecbdata.found_changesp = &o->found_changes; ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a); + ecbdata.file = o->file; xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; @@ -1415,9 +1430,11 @@ static void builtin_diff(const char *name_a, ecb.outf = xdiff_outf; ecb.priv = &ecbdata; ecbdata.xm.consume = fn_out_consume; - if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) + if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) { ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); + ecbdata.diff_words->file = o->file; + } xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); @@ -1496,6 +1513,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.lineno = 0; data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); data.ws_rule = whitespace_rule(attr_path); + data.file = o->file; if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1966,7 +1984,7 @@ static void run_diff_cmd(const char *pgm, builtin_diff(name, other ? other : name, one, two, xfrm_msg, o, complete_rewrite); else - printf("* Unmerged path %s\n", name); + fprintf(o->file, "* Unmerged path %s\n", name); } static void diff_fill_sha1_info(struct diff_filespec *one) @@ -2157,6 +2175,9 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) void diff_setup(struct diff_options *options) { memset(options, 0, sizeof(*options)); + + options->file = stdout; + options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; @@ -2470,7 +2491,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->b_prefix = arg + 13; else if (!strcmp(arg, "--no-prefix")) options->a_prefix = options->b_prefix = ""; - else + else if (!prefixcmp(arg, "--output=")) { + options->file = fopen(arg + strlen("--output="), "w"); + options->close_file = 1; + } else return 0; return 1; } @@ -2599,15 +2623,15 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) int inter_name_termination = line_termination ? '\t' : '\0'; if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) { - printf(":%06o %06o %s ", p->one->mode, p->two->mode, - diff_unique_abbrev(p->one->sha1, opt->abbrev)); - printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev)); + fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode, + diff_unique_abbrev(p->one->sha1, opt->abbrev)); + fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev)); } if (p->score) { - printf("%c%03d%c", p->status, similarity_index(p), - inter_name_termination); + fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p), + inter_name_termination); } else { - printf("%c%c", p->status, inter_name_termination); + fprintf(opt->file, "%c%c", p->status, inter_name_termination); } if (p->status == DIFF_STATUS_COPIED || @@ -2616,14 +2640,14 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) name_a = p->one->path; name_b = p->two->path; strip_prefix(opt->prefix_length, &name_a, &name_b); - write_name_quoted(name_a, stdout, inter_name_termination); - write_name_quoted(name_b, stdout, line_termination); + write_name_quoted(name_a, opt->file, inter_name_termination); + write_name_quoted(name_b, opt->file, line_termination); } else { const char *name_a, *name_b; name_a = p->one->mode ? p->one->path : p->two->path; name_b = NULL; strip_prefix(opt->prefix_length, &name_a, &name_b); - write_name_quoted(name_a, stdout, line_termination); + write_name_quoted(name_a, opt->file, line_termination); } } @@ -2825,62 +2849,62 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) name_a = p->two->path; name_b = NULL; strip_prefix(opt->prefix_length, &name_a, &name_b); - write_name_quoted(name_a, stdout, opt->line_termination); + write_name_quoted(name_a, opt->file, opt->line_termination); } } -static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) +static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs) { if (fs->mode) - printf(" %s mode %06o ", newdelete, fs->mode); + fprintf(file, " %s mode %06o ", newdelete, fs->mode); else - printf(" %s ", newdelete); - write_name_quoted(fs->path, stdout, '\n'); + fprintf(file, " %s ", newdelete); + write_name_quoted(fs->path, file, '\n'); } -static void show_mode_change(struct diff_filepair *p, int show_name) +static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name) { if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) { - printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode, + fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode, show_name ? ' ' : '\n'); if (show_name) { - write_name_quoted(p->two->path, stdout, '\n'); + write_name_quoted(p->two->path, file, '\n'); } } } -static void show_rename_copy(const char *renamecopy, struct diff_filepair *p) +static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p) { char *names = pprint_rename(p->one->path, p->two->path); - printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p)); + fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p)); free(names); - show_mode_change(p, 0); + show_mode_change(file, p, 0); } -static void diff_summary(struct diff_filepair *p) +static void diff_summary(FILE *file, struct diff_filepair *p) { switch(p->status) { case DIFF_STATUS_DELETED: - show_file_mode_name("delete", p->one); + show_file_mode_name(file, "delete", p->one); break; case DIFF_STATUS_ADDED: - show_file_mode_name("create", p->two); + show_file_mode_name(file, "create", p->two); break; case DIFF_STATUS_COPIED: - show_rename_copy("copy", p); + show_rename_copy(file, "copy", p); break; case DIFF_STATUS_RENAMED: - show_rename_copy("rename", p); + show_rename_copy(file, "rename", p); break; default: if (p->score) { - fputs(" rewrite ", stdout); - write_name_quoted(p->two->path, stdout, ' '); - printf("(%d%%)\n", similarity_index(p)); + fputs(" rewrite ", file); + write_name_quoted(p->two->path, file, ' '); + fprintf(file, "(%d%%)\n", similarity_index(p)); } - show_mode_change(p, !p->score); + show_mode_change(file, p, !p->score); break; } } @@ -3088,14 +3112,14 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_DIFFSTAT) show_stats(&diffstat, options); if (output_format & DIFF_FORMAT_SHORTSTAT) - show_shortstats(&diffstat); + show_shortstats(&diffstat, options); free_diffstat_info(&diffstat); separator++; } if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { for (i = 0; i < q->nr; i++) - diff_summary(q->queue[i]); + diff_summary(options->file, q->queue[i]); separator++; } @@ -3103,9 +3127,9 @@ void diff_flush(struct diff_options *options) if (separator) { if (options->stat_sep) { /* attach patch instead of inline */ - fputs(options->stat_sep, stdout); + fputs(options->stat_sep, options->file); } else { - putchar(options->line_termination); + putc(options->line_termination, options->file); } } @@ -3125,6 +3149,8 @@ free_queue: free(q->queue); q->queue = NULL; q->nr = q->alloc = 0; + if (options->close_file) + fclose(options->file); } static void diffcore_apply_filter(const char *filter) -- cgit v1.2.1 From c04a7155a03e5ef089864912769baf9001f64aa9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Feb 2008 17:06:58 -0800 Subject: diff: make --dirstat binary-file safe Instead of counting added and removed lines (and mixing the byte size reported for binary files in the result), summarize the extent of damage the same way as we count similarity for rename detection. Signed-off-by: Junio C Hamano --- diff.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 23 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 8022e678d..3632b552b 100644 --- a/diff.c +++ b/diff.c @@ -991,18 +991,23 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) } } -struct diffstat_dir { - struct diffstat_file **files; - int nr, percent, cumulative; +struct dirstat_file { + const char *name; + unsigned long changed; }; -static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) +struct dirstat_dir { + struct dirstat_file *files; + int alloc, nr, percent, cumulative; +}; + +static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen) { unsigned long this_dir = 0; unsigned int sources = 0; while (dir->nr) { - struct diffstat_file *f = *dir->files; + struct dirstat_file *f = dir->files; int namelen = strlen(f->name); unsigned long this; char *slash; @@ -1017,10 +1022,7 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c this = gather_dirstat(file, dir, changed, f->name, newbaselen); sources++; } else { - if (f->is_unmerged || f->is_binary) - this = 0; - else - this = f->added + f->deleted; + this = f->changed; dir->files++; dir->nr--; sources += 2; @@ -1048,19 +1050,58 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c return this_dir; } -static void show_dirstat(struct diffstat_t *data, struct diff_options *options) +static void show_dirstat(struct diff_options *options) { int i; unsigned long changed; - struct diffstat_dir dir; + struct dirstat_dir dir; + struct diff_queue_struct *q = &diff_queued_diff; + + dir.files = NULL; + dir.alloc = 0; + dir.nr = 0; + dir.percent = options->dirstat_percent; + dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; - /* Calculate total changes */ changed = 0; - for (i = 0; i < data->nr; i++) { - if (data->files[i]->is_binary || data->files[i]->is_unmerged) + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + const char *name; + unsigned long copied, added, damage; + + name = p->one->path ? p->one->path : p->two->path; + + if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { + diff_populate_filespec(p->one, 0); + diff_populate_filespec(p->two, 0); + diffcore_count_changes(p->one, p->two, NULL, NULL, 0, + &copied, &added); + diff_free_filespec_data(p->one); + diff_free_filespec_data(p->two); + } else if (DIFF_FILE_VALID(p->one)) { + diff_populate_filespec(p->one, 1); + copied = added = 0; + diff_free_filespec_data(p->one); + } else if (DIFF_FILE_VALID(p->two)) { + diff_populate_filespec(p->two, 1); + copied = 0; + added = p->two->size; + diff_free_filespec_data(p->two); + } else continue; - changed += data->files[i]->added; - changed += data->files[i]->deleted; + + /* + * Original minus copied is the removed material, + * added is the new material. They are both damages + * made to the preimage. + */ + damage = (p->one->size - copied) + added; + + ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); + dir.files[dir.nr].name = name; + dir.files[dir.nr].changed = damage; + changed += damage; + dir.nr++; } /* This can happen even with many files, if everything was renames */ @@ -1068,10 +1109,6 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options) return; /* Show all directories with more than x% of the changes */ - dir.files = data->files; - dir.nr = data->nr; - dir.percent = options->dirstat_percent; - dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; gather_dirstat(options->file, &dir, changed, "", 0); } @@ -3095,7 +3132,7 @@ void diff_flush(struct diff_options *options) separator++; } - if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) { + if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) { struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); @@ -3105,8 +3142,6 @@ void diff_flush(struct diff_options *options) if (check_pair_status(p)) diff_flush_stat(p, options, &diffstat); } - if (output_format & DIFF_FORMAT_DIRSTAT) - show_dirstat(&diffstat, options); if (output_format & DIFF_FORMAT_NUMSTAT) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) @@ -3116,6 +3151,8 @@ void diff_flush(struct diff_options *options) free_diffstat_info(&diffstat); separator++; } + if (output_format & DIFF_FORMAT_DIRSTAT) + show_dirstat(options); if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { for (i = 0; i < q->nr; i++) -- cgit v1.2.1 From 028656552bc758b192027c25a1143f1c6ca66d64 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Tue, 29 Apr 2008 01:32:59 -0700 Subject: Remove dead code: show_log() sep argument and diff_options.msg_sep These variables were made unnecessary by commit 3969cf7db1a13a78f3b7a36d8c1084bbe0a53459. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- diff.c | 1 - 1 file changed, 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3632b552b..e35384b44 100644 --- a/diff.c +++ b/diff.c @@ -2220,7 +2220,6 @@ void diff_setup(struct diff_options *options) options->rename_limit = -1; options->dirstat_percent = 3; options->context = 3; - options->msg_sep = ""; options->change = diff_change; options->add_remove = diff_addremove; -- cgit v1.2.1 From 50705915eae89eae490dff30fa370ed02e4d6e72 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 30 Apr 2008 13:24:43 -0400 Subject: bump rename limit defaults The current rename limit default of 100 was arbitrarily chosen. Testing[1] has shown that on modern hardware, a limit of 200 adds about a second of computation time, and a limit of 500 adds about 5 seconds of computation time. This patch bumps the default limit to 200 for viewing diffs, and to 500 for performing a merge. The limit for generating git-status templates is set independently; we bump it up to 200 here, as well, to match the diff limit. [1]: See <20080211113516.GB6344@coredump.intra.peff.net> Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3632b552b..f7355191d 100644 --- a/diff.c +++ b/diff.c @@ -19,7 +19,7 @@ #endif static int diff_detect_rename_default; -static int diff_rename_limit_default = 100; +static int diff_rename_limit_default = 200; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; -- cgit v1.2.1 From ef90d6d4208a5130185b04f06e5f90a5f9959fe3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 18:46:53 +0100 Subject: Provide git_config with a callback-data parameter git_config() only had a function parameter, but no callback data parameter. This assumes that all callback functions only modify global variables. With this patch, every callback gets a void * parameter, and it is hoped that this will help the libification effort. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e35384b44..1f46ff0f0 100644 --- a/diff.c +++ b/diff.c @@ -129,7 +129,7 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v * never be affected by the setting of diff.renames * the user happens to have in the configuration file. */ -int git_diff_ui_config(const char *var, const char *value) +int git_diff_ui_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.renamelimit")) { diff_rename_limit_default = git_config_int(var, value); @@ -166,10 +166,10 @@ int git_diff_ui_config(const char *var, const char *value) return parse_lldiff_command(var, ep, value); } - return git_diff_basic_config(var, value); + return git_diff_basic_config(var, value, cb); } -int git_diff_basic_config(const char *var, const char *value) +int git_diff_basic_config(const char *var, const char *value, void *cb) { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); @@ -190,7 +190,7 @@ int git_diff_basic_config(const char *var, const char *value) } } - return git_color_default_config(var, value); + return git_color_default_config(var, value, cb); } static char *quote_two(const char *one, const char *two) -- cgit v1.2.1 From 50fd9bd8430a957ea6c6674ce6112f375985abbc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 18:03:31 +0100 Subject: diff options: Introduce --ignore-submodules The new option --ignore-submodules can now be used to ignore changes in submodules. Why? Sometimes it is not interesting when a submodule changed. For example, when reordering some commits in the superproject, a dirty submodule is usually totally uninteresting. So we will use this option in git-rebase to test for a dirty working tree. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 439d4746c..d57bc299e 100644 --- a/diff.c +++ b/diff.c @@ -2496,6 +2496,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + else if (!strcmp(arg, "--ignore-submodules")) + DIFF_OPT_SET(options, IGNORE_SUBMODULES); /* misc options */ else if (!strcmp(arg, "-z")) @@ -3355,6 +3357,9 @@ void diff_addremove(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode)) + return; + /* This may look odd, but it is a preparation for * feeding "there are unchanged files which should * not produce diffs, but when you are doing copy @@ -3399,6 +3404,10 @@ void diff_change(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode) + && S_ISGITLINK(new_mode)) + return; + if (DIFF_OPT_TST(options, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_c; -- cgit v1.2.1 From 4d9b53591f64a11da0af4c2b8f11fd4730ce52dd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 14 Jun 2008 03:27:45 -0400 Subject: avoid trailing whitespace in zero-change diffstat lines In some cases, we produce a diffstat line even though no lines have changed (e.g., because of an exact rename). In this case, there is no +/- "graph" after the number of changed lines. However, we output the space separator unconditionally, meaning that these lines contained a trailing space character. This isn't a huge problem, but in cleaning up the output we are able to eliminate some trailing whitespace from a test vector. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 62fdc5492..f77f9e944 100644 --- a/diff.c +++ b/diff.c @@ -922,7 +922,8 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) total = add + del; } show_name(options->file, prefix, name, len, reset, set); - fprintf(options->file, "%5d ", added + deleted); + fprintf(options->file, "%5d%s", added + deleted, + added + deleted ? " " : ""); show_graph(options->file, '+', add, add_c, reset); show_graph(options->file, '-', del, del_c, reset); fprintf(options->file, "\n"); -- cgit v1.2.1 From 06ff64ae3d8c1d446ff61cff78a8faa3a07bfe24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 17 Jun 2008 00:00:02 +0200 Subject: diff: reset color before printing newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It worked that way since commit 50f575fc (Tweak diff colors, 2006-06-22), but commit c1795bb0 (Unify whitespace checking, 2007-12-13) changed it. This patch restores the old behaviour. Besides Linus' arguments in the log message of 50f575fc, resetting color before printing newline is also important to keep 'git add --patch' happy. If the last line(s) of a file are removed, then that hunk will end with a colored line. However, if the newline comes before the color reset, then the diff output will have an additional line at the end containing only the reset sequence. This causes trouble in git-add--interactive.perl's parse_diff function, because @colored will have one more element than @diff, and that last element will contain the color reset. The elements of these arrays will then be copied to @hunk, but only as many as the number of elements in @diff. As a result the last color reset is lost and all subsequent terminal output will be printed in color. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 8022e678d..c0284775d 100644 --- a/diff.c +++ b/diff.c @@ -514,9 +514,13 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len) { + if (len > 0 && line[len-1] == '\n') + len--; + fputs(set, file); fwrite(line, len, 1, file); fputs(reset, file); + fputc('\n', file); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) -- cgit v1.2.1 From 4afbcab9898ce63ad641dd0de6e9b69deda44330 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 16 Jun 2008 17:37:21 -0700 Subject: diff.c: fix emit_line() again not to add extra line Signed-off-by: Junio C Hamano --- diff.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c0284775d..20135cbe7 100644 --- a/diff.c +++ b/diff.c @@ -514,13 +514,15 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len) { - if (len > 0 && line[len-1] == '\n') + int has_trailing_newline = (len > 0 && line[len-1] == '\n'); + if (has_trailing_newline) len--; fputs(set, file); fwrite(line, len, 1, file); fputs(reset, file); - fputc('\n', file); + if (has_trailing_newline) + fputc('\n', file); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) -- cgit v1.2.1 From 18374e584ca7a820457c1d83ee99867c216e7b75 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jun 2008 13:16:33 -0700 Subject: diff --check: do not discard error status upon seeing a good line "git diff --check" should return non-zero when there was any whitespace error but the code only paid attention to the error status of the last new line in the patch. Signed-off-by: Junio C Hamano --- diff.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 526249008..f281c5b82 100644 --- a/diff.c +++ b/diff.c @@ -1150,12 +1150,14 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) char *err; if (line[0] == '+') { + unsigned bad; data->lineno++; - data->status = check_and_emit_line(line + 1, len - 1, + bad = check_and_emit_line(line + 1, len - 1, data->ws_rule, NULL, NULL, NULL, NULL); - if (!data->status) + if (!bad) return; - err = whitespace_error_string(data->status); + data->status |= bad; + err = whitespace_error_string(bad); fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err); free(err); emit_line(data->file, set, reset, line, 1); -- cgit v1.2.1 From 5ff10dd602f5926f0f5a73ae7de5866713428aa7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jun 2008 15:34:54 -0700 Subject: diff --check: explain why we do not care whether old side is binary All other codepaths refrain from running textual diff when either the old or the new side is binary, but this function only checks the new side. I was almost going to change it to check both, but that would be a bad change. Explain why to prevent future mistakes. Signed-off-by: Junio C Hamano --- diff.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 893942359..c00d633c1 100644 --- a/diff.c +++ b/diff.c @@ -1544,8 +1544,9 @@ static void builtin_diffstat(const char *name_a, const char *name_b, static void builtin_checkdiff(const char *name_a, const char *name_b, const char *attr_path, - struct diff_filespec *one, - struct diff_filespec *two, struct diff_options *o) + struct diff_filespec *one, + struct diff_filespec *two, + struct diff_options *o) { mmfile_t mf1, mf2; struct checkdiff_t data; @@ -1564,6 +1565,12 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); + /* + * All the other codepaths check both sides, but not checking + * the "old" side here is deliberate. We are checking the newly + * introduced changes, and as long as the "new" side is text, we + * can and should check what it introduces. + */ if (diff_filespec_is_binary(two)) goto free_and_return; else { -- cgit v1.2.1 From 8f8841e9c8e6a26153b0cf9417c7540cf65ef09f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jun 2008 15:35:21 -0700 Subject: check_and_emit_line(): rename and refactor The function name was too bland and not explicit enough as to what it is checking. Split it into two, and call the one that checks if there is a whitespace breakage "ws_check()", and call the other one that checks and emits the line after color coding "ws_check_emit()". Signed-off-by: Junio C Hamano --- diff.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c00d633c1..52a34eec9 100644 --- a/diff.c +++ b/diff.c @@ -535,9 +535,9 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons else { /* Emit just the prefix, then the rest. */ emit_line(ecbdata->file, set, reset, line, ecbdata->nparents); - (void)check_and_emit_line(line + ecbdata->nparents, - len - ecbdata->nparents, ecbdata->ws_rule, - ecbdata->file, set, reset, ws); + ws_check_emit(line + ecbdata->nparents, + len - ecbdata->nparents, ecbdata->ws_rule, + ecbdata->file, set, reset, ws); } } @@ -1153,8 +1153,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (line[0] == '+') { unsigned bad; data->lineno++; - bad = check_and_emit_line(line + 1, len - 1, - data->ws_rule, NULL, NULL, NULL, NULL); + bad = ws_check(line + 1, len - 1, data->ws_rule); if (!bad) return; data->status |= bad; @@ -1162,8 +1161,8 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err); free(err); emit_line(data->file, set, reset, line, 1); - (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, - data->file, set, reset, ws); + ws_check_emit(line + 1, len - 1, data->ws_rule, + data->file, set, reset, ws); } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { -- cgit v1.2.1 From 1ba111d1d6bd90b2c120ceb05418e01ee304cc46 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jun 2008 15:36:34 -0700 Subject: checkdiff: pass diff_options to the callback This way, we could later use more information from the diff_options. Signed-off-by: Junio C Hamano --- diff.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 52a34eec9..6bcbe2082 100644 --- a/diff.c +++ b/diff.c @@ -1136,18 +1136,19 @@ static void free_diffstat_info(struct diffstat_t *diffstat) struct checkdiff_t { struct xdiff_emit_state xm; const char *filename; - int lineno, color_diff; + int lineno; + struct diff_options *o; unsigned ws_rule; unsigned status; - FILE *file; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) { struct checkdiff_t *data = priv; - const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE); - const char *reset = diff_get_color(data->color_diff, DIFF_RESET); - const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW); + int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF); + const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE); + const char *reset = diff_get_color(color_diff, DIFF_RESET); + const char *set = diff_get_color(color_diff, DIFF_FILE_NEW); char *err; if (line[0] == '+') { @@ -1158,11 +1159,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) return; data->status |= bad; err = whitespace_error_string(bad); - fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err); + fprintf(data->o->file, "%s:%d: %s.\n", + data->filename, data->lineno, err); free(err); - emit_line(data->file, set, reset, line, 1); + emit_line(data->o->file, set, reset, line, 1); ws_check_emit(line + 1, len - 1, data->ws_rule, - data->file, set, reset, ws); + data->o->file, set, reset, ws); } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { @@ -1557,9 +1559,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; - data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); + data.o = o; data.ws_rule = whitespace_rule(attr_path); - data.file = o->file; if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); -- cgit v1.2.1 From 877f23ccb88227203f2576abdfb5d1c15925fcb3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jun 2008 15:36:59 -0700 Subject: Teach "diff --check" about new blank lines at end When a patch adds new blank lines at the end, "git apply --whitespace" warns. This teaches "diff --check" to do the same. Signed-off-by: Junio C Hamano --- diff.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6bcbe2082..f31c72116 100644 --- a/diff.c +++ b/diff.c @@ -1140,6 +1140,7 @@ struct checkdiff_t { struct diff_options *o; unsigned ws_rule; unsigned status; + int trailing_blanks_start; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1154,6 +1155,10 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (line[0] == '+') { unsigned bad; data->lineno++; + if (!ws_blank_line(line + 1, len - 1, data->ws_rule)) + data->trailing_blanks_start = 0; + else if (!data->trailing_blanks_start) + data->trailing_blanks_start = data->lineno; bad = ws_check(line + 1, len - 1, data->ws_rule); if (!bad) return; @@ -1165,14 +1170,16 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) emit_line(data->o->file, set, reset, line, 1); ws_check_emit(line + 1, len - 1, data->ws_rule, data->o->file, set, reset, ws); - } else if (line[0] == ' ') + } else if (line[0] == ' ') { data->lineno++; - else if (line[0] == '@') { + data->trailing_blanks_start = 0; + } else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) data->lineno = strtol(plus, NULL, 10) - 1; else die("invalid diff"); + data->trailing_blanks_start = 0; } } @@ -1584,6 +1591,12 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, ecb.outf = xdiff_outf; ecb.priv = &data; xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + + if (data.trailing_blanks_start) { + fprintf(o->file, "%s:%d: ends with blank lines.\n", + data.filename, data.trailing_blanks_start); + data.status = 1; /* report errors */ + } } free_and_return: diff_free_filespec_data(one); -- cgit v1.2.1 From 049540435fa5f7f583b8f5af257322b17eac7375 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 26 Jun 2008 15:37:21 -0700 Subject: diff --check: detect leftover conflict markers This teaches "diff --check" to detect and complain if the change adds lines that look like leftover conflict markers. We should be able to remove the old Perl script used in the sample pre-commit hook and modernize the script with this facility. Signed-off-by: Junio C Hamano --- diff.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f31c72116..d515b06ea 100644 --- a/diff.c +++ b/diff.c @@ -1143,6 +1143,35 @@ struct checkdiff_t { int trailing_blanks_start; }; +static int is_conflict_marker(const char *line, unsigned long len) +{ + char firstchar; + int cnt; + + if (len < 8) + return 0; + firstchar = line[0]; + switch (firstchar) { + case '=': case '>': case '<': + break; + default: + return 0; + } + for (cnt = 1; cnt < 7; cnt++) + if (line[cnt] != firstchar) + return 0; + /* line[0] thru line[6] are same as firstchar */ + if (firstchar == '=') { + /* divider between ours and theirs? */ + if (len != 8 || line[7] != '\n') + return 0; + } else if (len < 8 || !isspace(line[7])) { + /* not divider before ours nor after theirs */ + return 0; + } + return 1; +} + static void checkdiff_consume(void *priv, char *line, unsigned long len) { struct checkdiff_t *data = priv; @@ -1159,6 +1188,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) data->trailing_blanks_start = 0; else if (!data->trailing_blanks_start) data->trailing_blanks_start = data->lineno; + if (is_conflict_marker(line + 1, len - 1)) { + data->status |= 1; + fprintf(data->o->file, + "%s:%d: leftover conflict marker\n", + data->filename, data->lineno); + } bad = ws_check(line + 1, len - 1, data->ws_rule); if (!bad) return; -- cgit v1.2.1 From 861d1af36ae168353fc352126c0bf2d189c2324a Mon Sep 17 00:00:00 2001 From: Olivier Marin Date: Fri, 27 Jun 2008 02:18:48 +0200 Subject: show_stats(): fix stats width calculation Before this patch, name_width becomes negative or null for width values less than 15 and name_width values greater than 25 (default: 50). This leads to output random data. This patch checks for minimal width and name_width values. Signed-off-by: Olivier Marin Signed-off-by: Junio C Hamano --- diff.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 893942359..66851b564 100644 --- a/diff.c +++ b/diff.c @@ -830,12 +830,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) /* Sanity: give at least 5 columns to the graph, * but leave at least 10 columns for the name. */ - if (width < name_width + 15) { - if (name_width <= 25) - width = name_width + 15; - else - name_width = width - 15; - } + if (width < 25) + width = 25; + if (name_width < 10) + name_width = 10; + else if (width < name_width + 15) + name_width = width - 15; /* Find the longest filename and max number of changes */ reset = diff_get_color_opt(options, DIFF_RESET); -- cgit v1.2.1 From daec808cc61af32de789a31dd5c96001915c3f2d Mon Sep 17 00:00:00 2001 From: Brian Hetro Date: Sat, 5 Jul 2008 01:24:43 -0400 Subject: diff.c: Use 'git_config_string' to get 'diff.external' Signed-off-by: Brian Hetro Signed-off-by: Junio C Hamano --- diff.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f281c5b82..78c4d3a35 100644 --- a/diff.c +++ b/diff.c @@ -153,12 +153,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) diff_auto_refresh_index = git_config_bool(var, value); return 0; } - if (!strcmp(var, "diff.external")) { - if (!value) - return config_error_nonbool(var); - external_diff_cmd_cfg = xstrdup(value); - return 0; - } + if (!strcmp(var, "diff.external")) + return git_config_string(&external_diff_cmd_cfg, var, value); if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); -- cgit v1.2.1 From fd55a19eb1d49ae54008d932a65f79cd6fda45c9 Mon Sep 17 00:00:00 2001 From: Dmitry Potapov Date: Wed, 16 Jul 2008 18:54:02 +0400 Subject: Fix buffer overflow in git diff If PATH_MAX on your system is smaller than a path stored, it may cause buffer overflow and stack corruption in diff_addremove() and diff_change() functions when running git-diff Signed-off-by: Dmitry Potapov Signed-off-by: Junio C Hamano --- diff.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 78c4d3a35..386de826d 100644 --- a/diff.c +++ b/diff.c @@ -3356,9 +3356,8 @@ int diff_result_code(struct diff_options *opt, int status) void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, - const char *base, const char *path) + const char *concatpath) { - char concatpath[PATH_MAX]; struct diff_filespec *one, *two; if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode)) @@ -3380,9 +3379,6 @@ void diff_addremove(struct diff_options *options, addremove = (addremove == '+' ? '-' : addremove == '-' ? '+' : addremove); - if (!path) path = ""; - sprintf(concatpath, "%s%s", base, path); - if (options->prefix && strncmp(concatpath, options->prefix, options->prefix_length)) return; @@ -3403,9 +3399,8 @@ void diff_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, - const char *base, const char *path) + const char *concatpath) { - char concatpath[PATH_MAX]; struct diff_filespec *one, *two; if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode) @@ -3418,8 +3413,6 @@ void diff_change(struct diff_options *options, tmp = old_mode; old_mode = new_mode; new_mode = tmp; tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c; } - if (!path) path = ""; - sprintf(concatpath, "%s%s", base, path); if (options->prefix && strncmp(concatpath, options->prefix, options->prefix_length)) -- cgit v1.2.1 From 6b2fbaaffc5ec762eae5d23b58b1dc0a88e2275e Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 29 Jul 2008 22:49:33 -0700 Subject: format-patch: Produce better output with --inline or --attach This patch makes two small changes to improve the output of --inline and --attach. The first is to write a newline preceding the boundary. This is needed because MIME defines the encapsulation boundary as including the preceding CRLF (or in this case, just LF), so we should be writing one. Without this, the last newline in the pre-diff content is consumed instead. The second change is to always write the line termination character (default: newline) even when using --inline or --attach. This is simply to improve the aesthetics of the resulting message. When using --inline an email client should render the resulting message identically to the non-inline version. And when using --attach this adds a blank line preceding the attachment in the email, which is visually attractive. Signed-off-by: Kevin Ballard Signed-off-by: Junio C Hamano --- diff.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a07812c5c..cbf25473c 100644 --- a/diff.c +++ b/diff.c @@ -3223,11 +3223,10 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_PATCH) { if (separator) { + putc(options->line_termination, options->file); if (options->stat_sep) { /* attach patch instead of inline */ fputs(options->stat_sep, options->file); - } else { - putc(options->line_termination, options->file); } } -- cgit v1.2.1 From ad8c1d92600da963692d57c213979e638c357f1a Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 31 Jul 2008 09:21:48 +0200 Subject: diff: add ruby funcname pattern Provide a regexp that catches class, module and method definitions in Ruby scripts, since the built-in default only finds classes. Signed-off-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index cbf25473c..c253015c5 100644 --- a/diff.c +++ b/diff.c @@ -1381,6 +1381,7 @@ static struct builtin_funcname_pattern { "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" "[ ]*([^;]*\\)$" }, { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, + { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, }; static const char *diff_funcname_pattern(struct diff_filespec *one) -- cgit v1.2.1 From b50005b79f6c77f9846c3f34ba319675fe489b72 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Fri, 1 Aug 2008 17:00:15 -0400 Subject: Teach "git diff -p" Pascal/Delphi funcname pattern Finds classes, records, functions, procedures, and sections. Most lines need to start at the first column, or else there's no way to differentiate a procedure's definition from its declaration. Signed-off-by: Avery Pennarun Signed-off-by: Junio C Hamano --- diff.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c253015c5..75a8b1664 100644 --- a/diff.c +++ b/diff.c @@ -1380,6 +1380,12 @@ static struct builtin_funcname_pattern { "^[ ]*\\(\\([ ]*" "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" "[ ]*([^;]*\\)$" }, + { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|" + "destructor\\|interface\\|implementation\\|" + "initialization\\|finalization\\)[ \t]*.*\\)$" + "\\|" + "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" + }, { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, }; -- cgit v1.2.1 From 807d86945336f676c9f650a6cbae9baa3191aaec Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Sat, 2 Aug 2008 23:56:44 +0200 Subject: diff: chapter and part in funcname for tex This patch enhances the tex funcname by adding support for chapter and part sectioning commands. It also matches the starred version of the sectioning commands. Signed-off-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 75a8b1664..bd04fdfd2 100644 --- a/diff.c +++ b/diff.c @@ -1386,7 +1386,7 @@ static struct builtin_funcname_pattern { "\\|" "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" }, - { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, + { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, }; -- cgit v1.2.1 From 2b6ca6df2dbe9c23d0adfc1f59678a2d85c560b2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 5 Aug 2008 11:27:30 -0700 Subject: diff.renamelimit is a basic diff configuration The configuration was added as a core option in 3299c6f (diff: make default rename detection limit configurable., 2005-11-15), but 9ce392f (Move diff.renamelimit out of default configuration., 2005-11-21) separated diff-related stuff out of the core. Up to that point it was Ok. When we separated the Porcelain options out of the git_diff_config in 83ad63c (diff: do not use configuration magic at the core-level, 2006-07-08), we should have been more careful. This mistake made diff-tree plumbing and git-show Porcelain to notice different set of renames when the user explicitly asked for rename detection. Signed-off-by: Junio C Hamano --- diff.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 386de826d..342733ba7 100644 --- a/diff.c +++ b/diff.c @@ -131,10 +131,6 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v */ int git_diff_ui_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, "diff.renamelimit")) { - diff_rename_limit_default = git_config_int(var, value); - return 0; - } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { diff_use_color_default = git_config_colorbool(var, value, -1); return 0; @@ -167,6 +163,11 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) int git_diff_basic_config(const char *var, const char *value, void *cb) { + if (!strcmp(var, "diff.renamelimit")) { + diff_rename_limit_default = git_config_int(var, value); + return 0; + } + if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); if (!value) -- cgit v1.2.1 From 04c6e9e9ca34226db095bbaa1218030f99f0b7c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 11 Aug 2008 22:15:28 -0700 Subject: diff --check: do not unconditionally complain about trailing empty lines Recently "git diff --check" learned to detect new trailing blank lines just like "git apply --whitespace" does. However this check should not trigger unconditionally. This patch makes it honor the whitespace settings from core.whitespace and gitattributes. Signed-off-by: Junio C Hamano --- diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 8746c60b9..6954f992c 100644 --- a/diff.c +++ b/diff.c @@ -1631,7 +1631,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, ecb.priv = &data; xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); - if (data.trailing_blanks_start) { + if ((data.ws_rule & WS_TRAILING_SPACE) && + data.trailing_blanks_start) { fprintf(o->file, "%s:%d: ends with blank lines.\n", data.filename, data.trailing_blanks_start); data.status = 1; /* report errors */ -- cgit v1.2.1 From 23b5beb28fdadbb1d80ebf686a35385609f7a180 Mon Sep 17 00:00:00 2001 From: Gustaf Hendeby Date: Tue, 12 Aug 2008 16:24:26 +0200 Subject: Teach git diff about BibTeX head hunk patterns All BibTeX entries starts with an @ followed by an entry type. Since there are many entry types and own can be defined, the pattern matches legal entry type names instead of just the default types (which would be a long list). The pattern also matches strings and comments since they will also be useful to position oneself in a bib-file. Signed-off-by: Gustaf Hendeby Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6954f992c..bf5d5f15a 100644 --- a/diff.c +++ b/diff.c @@ -1387,6 +1387,7 @@ static struct builtin_funcname_pattern { "\\|" "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" }, + { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, }; -- cgit v1.2.1 From c99db9d292c5f63c83ae2b441a67121d76553413 Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Thu, 14 Aug 2008 00:36:50 -0500 Subject: Make xdi_diff_outf interface for running xdiff_outf diffs To prepare for the need to initialize and release resources for an xdi_diff with the xdiff_outf output function, make a new function to wrap this usage. Old: ecb.outf = xdiff_outf; ecb.priv = &state; ... xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb); New: xdi_diff_outf(file_p, file_o, &state.xm, &xpp, &xecfg, &ecb); Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- diff.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index bf5d5f15a..913a92f3e 100644 --- a/diff.c +++ b/diff.c @@ -459,10 +459,8 @@ static void diff_words_show(struct diff_words_data *diff_words) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; - ecb.outf = xdiff_outf; - ecb.priv = diff_words; diff_words->xm.consume = fn_out_diff_words_aux; - xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); + xdi_diff_outf(&minus, &plus, &diff_words->xm, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); @@ -1521,15 +1519,13 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); else if (!prefixcmp(diffopts, "-u")) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecb.outf = xdiff_outf; - ecb.priv = &ecbdata; ecbdata.xm.consume = fn_out_consume; if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) { ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; } - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, &ecbdata.xm, &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1580,9 +1576,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; - ecb.outf = xdiff_outf; - ecb.priv = diffstat; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, &diffstat->xm, &xpp, &xecfg, &ecb); } free_and_return: @@ -1628,9 +1622,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL; - ecb.outf = xdiff_outf; - ecb.priv = &data; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, &data.xm, &xpp, &xecfg, &ecb); if ((data.ws_rule & WS_TRAILING_SPACE) && data.trailing_blanks_start) { @@ -3128,9 +3120,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = 3; xecfg.flags = XDL_EMIT_FUNCNAMES; - ecb.outf = xdiff_outf; - ecb.priv = &data; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, &data.xm, &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); -- cgit v1.2.1 From 8a3f524bf2ac534b313a7d8e70cc164cef744949 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 13 Aug 2008 23:18:22 -0700 Subject: xdiff-interface: hide the whole "xdiff_emit_state" business from the caller This further enhances xdi_diff_outf() interface so that it takes two common parameters: the callback function that processes one line at a time, and a pointer to its application specific callback data structure. xdi_diff_outf() creates its own "xdiff_emit_state" structure and stashes these two away inside it, which is used by the lowest level output function in the xdiff_outf() callchain, consume_one(), to call back to the application layer. With this restructuring, we lift the requirement that the caller supplied callback data structure embeds xdiff_emit_state structure as its first member. Signed-off-by: Junio C Hamano --- diff.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 913a92f3e..a6c143251 100644 --- a/diff.c +++ b/diff.c @@ -369,7 +369,6 @@ static void diff_words_append(char *line, unsigned long len, } struct diff_words_data { - struct xdiff_emit_state xm; struct diff_words_buffer minus, plus; FILE *file; }; @@ -459,9 +458,8 @@ static void diff_words_show(struct diff_words_data *diff_words) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; - diff_words->xm.consume = fn_out_diff_words_aux; - xdi_diff_outf(&minus, &plus, &diff_words->xm, &xpp, &xecfg, &ecb); - + xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, + &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); diff_words->minus.text.size = diff_words->plus.text.size = 0; @@ -475,7 +473,6 @@ static void diff_words_show(struct diff_words_data *diff_words) typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); struct emit_callback { - struct xdiff_emit_state xm; int nparents, color_diff; unsigned ws_rule; sane_truncate_fn truncate; @@ -706,8 +703,6 @@ static char *pprint_rename(const char *a, const char *b) } struct diffstat_t { - struct xdiff_emit_state xm; - int nr; int alloc; struct diffstat_file { @@ -1129,7 +1124,6 @@ static void free_diffstat_info(struct diffstat_t *diffstat) } struct checkdiff_t { - struct xdiff_emit_state xm; const char *filename; int lineno; struct diff_options *o; @@ -1519,13 +1513,13 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); else if (!prefixcmp(diffopts, "-u")) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecbdata.xm.consume = fn_out_consume; if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) { ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; } - xdi_diff_outf(&mf1, &mf2, &ecbdata.xm, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, + &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1576,7 +1570,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; - xdi_diff_outf(&mf1, &mf2, &diffstat->xm, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat, + &xpp, &xecfg, &ecb); } free_and_return: @@ -1597,7 +1592,6 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, return; memset(&data, 0, sizeof(data)); - data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; data.o = o; @@ -1622,7 +1616,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL; - xdi_diff_outf(&mf1, &mf2, &data.xm, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data, + &xpp, &xecfg, &ecb); if ((data.ws_rule & WS_TRAILING_SPACE) && data.trailing_blanks_start) { @@ -3010,7 +3005,6 @@ static void diff_summary(FILE *file, struct diff_filepair *p) } struct patch_id_t { - struct xdiff_emit_state xm; SHA_CTX *ctx; int patchlen; }; @@ -3055,7 +3049,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) SHA1_Init(&ctx); memset(&data, 0, sizeof(struct patch_id_t)); data.ctx = &ctx; - data.xm.consume = patch_id_consume; for (i = 0; i < q->nr; i++) { xpparam_t xpp; @@ -3120,7 +3113,8 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = 3; xecfg.flags = XDL_EMIT_FUNCNAMES; - xdi_diff_outf(&mf1, &mf2, &data.xm, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data, + &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); @@ -3197,7 +3191,6 @@ void diff_flush(struct diff_options *options) struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); - diffstat.xm.consume = diffstat_consume; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (check_pair_status(p)) -- cgit v1.2.1 From a624eaa7820f4f9814e41e911c665a0aba2fce34 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 15 Aug 2008 13:39:26 +0200 Subject: add boolean diff.suppress-blank-empty config option GNU diff's --suppress-blank-empty option makes it so that diff no longer outputs trailing white space unless the input data has it. With this option, empty context lines are now empty also in diff -u output. Before, they would have a single trailing space. * diff.c (diff_suppress_blank_empty): New global. (git_diff_basic_config): Set it. (fn_out_consume): Honor it. * t/t4029-diff-trailing-space.sh: New file. * Documentation/config.txt: Document it. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- diff.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index bf5d5f15a..fe43407b7 100644 --- a/diff.c +++ b/diff.c @@ -20,6 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 200; +static int diff_suppress_blank_empty; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@ -176,6 +177,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return 0; } + /* like GNU diff's --suppress-blank-empty option */ + if (!strcmp(var, "diff.suppress-blank-empty")) { + diff_suppress_blank_empty = git_config_bool(var, value); + return 0; + } + if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { @@ -580,6 +587,12 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } + if (diff_suppress_blank_empty + && len == 2 && line[0] == ' ' && line[1] == '\n') { + line[0] = '\n'; + len = 1; + } + /* This is not really necessary for now because * this codepath only deals with two-way diffs. */ -- cgit v1.2.1 From c35539eb10b0ab2a180e523f03ff65dc061bd47e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Aug 2008 11:47:55 -0700 Subject: diff --check: do not get confused by new blank lines in the middle The code remembered that the last diff output it saw was an empty line, and tried to reset that state whenever it sees a context line, a non-blank new line, or a new hunk. However, this codepath asks the underlying diff engine to feed diff without any context, and the "just saw an empty line" state was not reset if you added a new blank line in the last hunk of your patch, even if it is not the last line of the file. Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index bf5d5f15a..f70e6b491 100644 --- a/diff.c +++ b/diff.c @@ -1627,6 +1627,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, xdemitcb_t ecb; memset(&xecfg, 0, sizeof(xecfg)); + xecfg.ctxlen = 1; /* at least one context line */ xpp.flags = XDF_NEED_MINIMAL; ecb.outf = xdiff_outf; ecb.priv = &data; -- cgit v1.2.1 From 7c17205b64a0e668d8a6e59109043bd4f2af3a0c Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Wed, 20 Aug 2008 19:57:07 +0400 Subject: Teach "git diff -p" Python funcname patterns Find classes, functions, and methods definitions. Signed-off-by: Kirill Smelkov Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5923fe281..221541644 100644 --- a/diff.c +++ b/diff.c @@ -1394,6 +1394,7 @@ static struct builtin_funcname_pattern { }, { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, + { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" }, { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, }; -- cgit v1.2.1 From 1a1fcf4abeb2ed4ef6075970788711cf62405158 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Wed, 20 Aug 2008 19:49:15 +0200 Subject: Teach "git diff -p" HTML funcname patterns Find lines with

..

tags. [jc: while at it, reordered entries to sort alphabetically.] Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- diff.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 221541644..18fa7a712 100644 --- a/diff.c +++ b/diff.c @@ -1381,6 +1381,8 @@ 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" "^[ ]*\\(\\([ ]*" @@ -1392,10 +1394,9 @@ static struct builtin_funcname_pattern { "\\|" "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" }, - { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, - { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" }, { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, + { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, }; static const char *diff_funcname_pattern(struct diff_filespec *one) -- cgit v1.2.1 From 441bca0bbc6d80a78ce332ef4fa6225155dfbce6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 28 Aug 2008 16:19:08 -0700 Subject: Fix '--dirstat' with cross-directory renaming The dirstat code depends on the fact that we always generate diffs with the names sorted, since it then just does a single-pass walk-over of the sorted list of names and how many changes there were. The sorting means that all files are nicely grouped by directory. That all works fine. Except when we have rename detection, and suddenly the nicely sorted list of pathnames isn't all that sorted at all. And now the single-pass dirstat walk gets all confused, and you can get results like this: [torvalds@nehalem linux]$ git diff --dirstat=2 -M v2.6.27-rc4..v2.6.27-rc5 3.0% arch/powerpc/configs/ 6.8% arch/arm/configs/ 2.7% arch/powerpc/configs/ 4.2% arch/arm/configs/ 5.6% arch/powerpc/configs/ 8.4% arch/arm/configs/ 5.5% arch/powerpc/configs/ 23.3% arch/arm/configs/ 8.6% arch/powerpc/configs/ 4.0% arch/ 4.4% drivers/usb/musb/ 4.0% drivers/watchdog/ 7.6% drivers/ 3.5% fs/ The trivial fix is to add a sorting pass, fixing it to: [torvalds@nehalem linux]$ git diff --dirstat=2 -M v2.6.27-rc4..v2.6.27-rc5 43.0% arch/arm/configs/ 25.5% arch/powerpc/configs/ 5.3% arch/ 4.4% drivers/usb/musb/ 4.0% drivers/watchdog/ 7.6% drivers/ 3.5% fs/ Spot the difference. In case anybody wonders: it's because of a ton of renames from {include/asm-blackfin => arch/blackfin/include/asm} that just totally messed up the file ordering in between arch/arm and arch/powerpc. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f70e6b491..7b4300a74 100644 --- a/diff.c +++ b/diff.c @@ -1054,6 +1054,13 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch return this_dir; } +static int dirstat_compare(const void *_a, const void *_b) +{ + const struct dirstat_file *a = _a; + const struct dirstat_file *b = _b; + return strcmp(a->name, b->name); +} + static void show_dirstat(struct diff_options *options) { int i; @@ -1113,6 +1120,7 @@ static void show_dirstat(struct diff_options *options) return; /* Show all directories with more than x% of the changes */ + qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare); gather_dirstat(options->file, &dir, changed, "", 0); } -- cgit v1.2.1 From 392809702016cde59d50a7b07e8c27f6d0ec3c3f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Aug 2008 19:48:01 -0700 Subject: diff: Help "less" hide ^M from the output When the tracked contents have CRLF line endings, colored diff output shows "^M" at the end of output lines, which is distracting, even though the pager we use by default ("less") knows to hide them. The problem is that "less" hides a carriage-return only at the end of the line, immediately before a line feed. The colored diff output does not take this into account, and emits four element sequence for each line: - force this color; - the line up to but not including the terminating line feed; - reset color - line feed. By including the carriage return at the end of the line in the second item, we are breaking the smart our pager has in order not to show "^M". This can be fixed by changing the sequence to: - force this color; - the line up to but not including the terminating end-of-line; - reset color - end-of-line. where end-of-line is either a single linefeed or a CRLF pair. When the output is not colored, "force this color" and "reset color" sequences are both empty, so we won't have this problem with or without this patch. Signed-off-by: Junio C Hamano --- diff.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7b4300a74..6d56c6981 100644 --- a/diff.c +++ b/diff.c @@ -511,13 +511,20 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len) { - int has_trailing_newline = (len > 0 && line[len-1] == '\n'); + int has_trailing_newline, has_trailing_carriage_return; + + has_trailing_newline = (len > 0 && line[len-1] == '\n'); if (has_trailing_newline) len--; + has_trailing_carriage_return = (len > 0 && line[len-1] == '\r'); + if (has_trailing_carriage_return) + len--; fputs(set, file); fwrite(line, len, 1, file); fputs(reset, file); + if (has_trailing_carriage_return) + fputc('\r', file); if (has_trailing_newline) fputc('\n', file); } -- cgit v1.2.1 From a5a818ee4877e4458e8e6741a03ac3b19941d58a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 18 Aug 2008 20:08:09 -0700 Subject: diff: vary default prefix depending on what are compared With a new configuration "diff.mnemonicprefix", "git diff" shows the differences between various combinations of preimage and postimage trees with prefixes different from the standard "a/" and "b/". Hopefully this will make the distinction stand out for some people. "git diff" compares the (i)ndex and the (w)ork tree; "git diff HEAD" compares a (c)ommit and the (w)ork tree; "git diff --cached" compares a (c)ommit and the (i)ndex; "git-diff HEAD:file1 file2" compares an (o)bject and a (w)ork tree entity; "git diff --no-index a b" compares two non-git things (1) and (2). Because these mnemonics now have meanings, they are swapped when reverse diff is in effect and this feature is enabled. Signed-off-by: Junio C Hamano --- diff.c | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7b4300a74..c1804efed 100644 --- a/diff.c +++ b/diff.c @@ -23,6 +23,7 @@ static int diff_rename_limit_default = 200; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; +static int diff_mnemonic_prefix; static char diff_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ @@ -149,6 +150,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) diff_auto_refresh_index = git_config_bool(var, value); return 0; } + if (!strcmp(var, "diff.mnemonicprefix")) { + diff_mnemonic_prefix = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "diff.external")) return git_config_string(&external_diff_cmd_cfg, var, value); if (!prefixcmp(var, "diff.")) { @@ -305,6 +310,15 @@ static void emit_rewrite_diff(const char *name_a, const char *new = diff_get_color(color_diff, DIFF_FILE_NEW); const char *reset = diff_get_color(color_diff, DIFF_RESET); static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT; + const char *a_prefix, *b_prefix; + + if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) { + a_prefix = o->b_prefix; + b_prefix = o->a_prefix; + } else { + a_prefix = o->a_prefix; + b_prefix = o->b_prefix; + } name_a += (*name_a == '/'); name_b += (*name_b == '/'); @@ -313,8 +327,8 @@ static void emit_rewrite_diff(const char *name_a, strbuf_reset(&a_name); strbuf_reset(&b_name); - quote_two_c_style(&a_name, o->a_prefix, name_a, 0); - quote_two_c_style(&b_name, o->b_prefix, name_b, 0); + quote_two_c_style(&a_name, a_prefix, name_a, 0); + quote_two_c_style(&b_name, b_prefix, name_b, 0); diff_populate_filespec(one, 0); diff_populate_filespec(two, 0); @@ -1432,6 +1446,14 @@ static const char *diff_funcname_pattern(struct diff_filespec *one) return NULL; } +void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b) +{ + if (!options->a_prefix) + options->a_prefix = a; + if (!options->b_prefix) + options->b_prefix = b; +} + static void builtin_diff(const char *name_a, const char *name_b, struct diff_filespec *one, @@ -1445,9 +1467,19 @@ static void builtin_diff(const char *name_a, char *a_one, *b_two; const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); + const char *a_prefix, *b_prefix; + + diff_set_mnemonic_prefix(o, "a/", "b/"); + if (DIFF_OPT_TST(o, REVERSE_DIFF)) { + a_prefix = o->b_prefix; + b_prefix = o->a_prefix; + } else { + a_prefix = o->a_prefix; + b_prefix = o->b_prefix; + } - a_one = quote_two(o->a_prefix, name_a + (*name_a == '/')); - b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); + a_one = quote_two(a_prefix, name_a + (*name_a == '/')); + b_two = quote_two(b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset); @@ -2308,8 +2340,10 @@ void diff_setup(struct diff_options *options) DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; - options->a_prefix = "a/"; - options->b_prefix = "b/"; + if (!diff_mnemonic_prefix) { + options->a_prefix = "a/"; + options->b_prefix = "b/"; + } } int diff_setup_done(struct diff_options *options) -- cgit v1.2.1 From f88d225feb117f0429540da90b2552e198fa2e82 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Sep 2008 17:28:59 -0700 Subject: diff --cumulative is a sub-option of --dirstat The option used to be implemented as if it is a totally independent one, but "git diff --cumulative" would not mean anything without "--dirstat". This makes --cumulative imply --dirstat. Signed-off-by: Junio C Hamano --- diff.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7b4300a74..b3a7da70a 100644 --- a/diff.c +++ b/diff.c @@ -1072,7 +1072,7 @@ static void show_dirstat(struct diff_options *options) dir.alloc = 0; dir.nr = 0; dir.percent = options->dirstat_percent; - dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; + dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE); changed = 0; for (i = 0; i < q->nr; i++) { @@ -2298,6 +2298,7 @@ void diff_setup(struct diff_options *options) options->break_opt = -1; options->rename_limit = -1; options->dirstat_percent = 3; + DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE); options->context = 3; options->change = diff_change; @@ -2470,8 +2471,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_SHORTSTAT; else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent)) options->output_format |= DIFF_FORMAT_DIRSTAT; - else if (!strcmp(arg, "--cumulative")) - options->output_format |= DIFF_FORMAT_CUMULATIVE; + else if (!strcmp(arg, "--cumulative")) { + options->output_format |= DIFF_FORMAT_DIRSTAT; + DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE); + } else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) -- cgit v1.2.1 From fd33777b7871eefa440f89ccf7c432895af09c74 Mon Sep 17 00:00:00 2001 From: Heikki Orsila Date: Fri, 5 Sep 2008 22:27:35 +0300 Subject: diff --dirstat-by-file: count changed files, not lines This new option --dirstat-by-file is the same as --dirstat, but it counts "impacted files" instead of "impacted lines" (lines that are added or removed). Signed-off-by: Heikki Orsila Signed-off-by: Junio C Hamano --- diff.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index cbd151bbc..2de86eb76 100644 --- a/diff.c +++ b/diff.c @@ -1110,9 +1110,13 @@ static void show_dirstat(struct diff_options *options) /* * Original minus copied is the removed material, * added is the new material. They are both damages - * made to the preimage. + * made to the preimage. In --dirstat-by-file mode, count + * damaged files, not damaged lines. This is done by + * counting only a single damaged line per file. */ damage = (p->one->size - copied) + added; + if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0) + damage = 1; ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); dir.files[dir.nr].name = name; @@ -2476,6 +2480,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--cumulative")) { options->output_format |= DIFF_FORMAT_DIRSTAT; DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE); + } else if (opt_arg(arg, 0, "dirstat-by-file", + &options->dirstat_percent)) { + options->output_format |= DIFF_FORMAT_DIRSTAT; + DIFF_OPT_SET(options, DIRSTAT_BY_FILE); } else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; -- cgit v1.2.1 From 9d865356abc08b253bc15b5fb50471d80c828be1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 6 Sep 2008 19:09:16 -0700 Subject: diff Porcelain: do not disable auto index refreshing on -C -C When we enabled the automatic refreshing of the index to "diff" Porcelain, we disabled it when --find-copies-harder was asked, but there is no good reason to do so. In the following command sequence, the first "diff" shows an "empty" diff exposing stat dirtyness, while the second one does not. $ >foo $ git add foo $ touch foo $ git diff -C -C $ git diff -C This fixes the inconsistency. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b3a7da70a..85d690183 100644 --- a/diff.c +++ b/diff.c @@ -3394,7 +3394,7 @@ void diffcore_std(struct diff_options *options) if (DIFF_OPT_TST(options, QUIET)) return; - if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER)) + if (options->skip_stat_unmatch) diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) diffcore_break(options->break_opt); -- cgit v1.2.1 From df58a8274d6865020682a6739bc59b87a9761991 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 Sep 2008 23:20:26 -0700 Subject: diff --quiet: make it synonym to --exit-code >/dev/null The point of --quiet was to return the status as early as possible without doing any extra processing. Well behaved scripts, when they expect to run many diff operations inside, are supposed to run "update-index --refresh" upfront; we do not want them to pay the price of iterating over the index and comparing the contents to fix the stat dirtiness, and we avoided most of the processing in diffcore_std() when --quiet is in effect. But scripts that adhere to the good practice won't have to pay any more price than the necessary lstat(2) that will report stat cleanliness, as long as only -q is given without any fancier diff options. More importantly, users who do ask for "--quiet -M --filter=D" (in order to notice only the deletion, not paths that disappeared only because they have been renamed away) deserve to get the result they asked for, even it means they have to pay the extra price; the alternative is to get a cheap early return that gives a result they did not ask for, which is much worse. Signed-off-by: Junio C Hamano --- diff.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 85d690183..13be21156 100644 --- a/diff.c +++ b/diff.c @@ -2393,13 +2393,6 @@ int diff_setup_done(struct diff_options *options) DIFF_OPT_SET(options, EXIT_WITH_STATUS); } - /* - * If we postprocess in diffcore, we cannot simply return - * upon the first hit. We need to run diff as usual. - */ - if (options->pickaxe || options->filter) - DIFF_OPT_CLR(options, QUIET); - return 0; } @@ -3391,9 +3384,6 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) void diffcore_std(struct diff_options *options) { - if (DIFF_OPT_TST(options, QUIET)) - return; - if (options->skip_stat_unmatch) diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) -- cgit v1.2.1 From af9ce1ffc6de9774e90a91f27fb1febd027f74f1 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Sun, 7 Sep 2008 22:15:29 +0200 Subject: Teach "git diff -p" to locate PHP class methods Otherwise it will always print the class-name rather than the name of the function inside that class. While we're at it, reorder the gitattributes manpage to list the built-in funcname pattern names in alphabetical order. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index cbd151bbc..e7afbe28c 100644 --- a/diff.c +++ b/diff.c @@ -1402,6 +1402,7 @@ static struct builtin_funcname_pattern { "\\|" "^\\(.*=[ \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\\}{.*\\)$" }, -- cgit v1.2.1 From 45e7ca0f0e1042c26d56b578165365c3f70c0121 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 18 Sep 2008 17:40:48 -0500 Subject: diff.c: return pattern entry pointer rather than just the hunk header pattern This is in preparation for associating a flag with each pattern which will control how the pattern is interpreted. For example, as a basic or extended regular expression. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- diff.c | 55 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5e01b2bb2..406a76a20 100644 --- a/diff.c +++ b/diff.c @@ -94,32 +94,35 @@ 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; +}; +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) { 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); return 0; } @@ -1377,20 +1380,17 @@ 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[] = { +static const struct funcname_pattern_entry builtin_funcname_pattern[] = { { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" "new\\|return\\|switch\\|throw\\|while\\)\n" "^[ ]*\\(\\([ ]*" @@ -1407,9 +1407,10 @@ static struct builtin_funcname_pattern { { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, }; -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); @@ -1424,9 +1425,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 @@ -1434,7 +1435,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; } @@ -1512,11 +1513,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)); @@ -1528,8 +1529,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); if (!diffopts) ; else if (!prefixcmp(diffopts, "--unified=")) -- cgit v1.2.1 From a013585b20ac757b0e75a72181ffa44674f35235 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 18 Sep 2008 17:42:48 -0500 Subject: diff.c: associate a flag with each pattern and use it for compiling regex This is in preparation for allowing extended regular expression patterns. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- diff.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 406a76a20..6881cf4ef 100644 --- a/diff.c +++ b/diff.c @@ -97,13 +97,14 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val struct funcname_pattern_entry { char *name; char *pattern; + 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; @@ -123,6 +124,7 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v } free(pp->e.pattern); pp->e.pattern = xstrdup(value); + pp->e.cflags = cflags; return 0; } @@ -185,7 +187,8 @@ 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); } } } @@ -1395,16 +1398,16 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = { "new\\|return\\|switch\\|throw\\|while\\)\n" "^[ ]*\\(\\([ ]*" "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*\\)$" }, + "[ ]*([^;]*\\)$", 0 }, { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|" "destructor\\|interface\\|implementation\\|" "initialization\\|finalization\\)[ \t]*.*\\)$" "\\|" - "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$" - }, - { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" }, - { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" }, - { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" }, + "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$", + 0 }, + { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 }, + { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 }, + { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$", 0 }, }; static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one) @@ -1530,7 +1533,7 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; if (pe) - xdiff_set_find_func(&xecfg, pe->pattern); + xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); if (!diffopts) ; else if (!prefixcmp(diffopts, "--unified=")) -- cgit v1.2.1 From 45d9414fa5599b41578625961b53e18a9b9148c7 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 18 Sep 2008 17:44:33 -0500 Subject: diff.*.xfuncname which uses "extended" regex's for hunk header selection Currently, the hunk headers produced by 'diff -p' are customizable by setting the diff.*.funcname option in the config file. The 'funcname' option takes a basic regular expression. This functionality was designed using the GNU regex library which, by default, allows using backslashed versions of some extended regular expression operators, even in Basic Regular Expression mode. For example, the following characters, when backslashed, are interpreted according to the extended regular expression rules: ?, +, and |. As such, the builtin funcname patterns were created using some extended regular expression operators. Other platforms which adhere more strictly to the POSIX spec do not interpret the backslashed extended RE operators in Basic Regular Expression mode. This causes the pattern matching for the builtin funcname patterns to fail on those platforms. Introduce a new option 'xfuncname' which uses extended regular expressions, and advertise it _instead_ of funcname. Since most users are on GNU platforms, the majority of funcname patterns are created and tested there. Advertising only xfuncname should help to avoid the creation of non-portable patterns which work with GNU regex but not elsewhere. Additionally, the extended regular expressions may be less ugly and complicated compared to the basic RE since many common special operators do not need to be backslashed. For example, the GNU Basic RE: ^[ ]*\\(\\(public\\|static\\).*\\)$ becomes the following Extended RE: ^[ ]*((public|static).*)$ Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- diff.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6881cf4ef..dabb4b4a0 100644 --- a/diff.c +++ b/diff.c @@ -189,6 +189,11 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return config_error_nonbool(var); 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); } } } -- cgit v1.2.1 From 6a6baf9b4e819a0bbfd70627f966cd7144dd8301 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Sep 2008 23:45:04 -0700 Subject: diff: use extended regexp to find hunk headers Using ERE elements such as "|" (alternation) by backquoting in BRE is a GNU extension and should not be done in portable programs. Signed-off-by: Junio C Hamano --- diff.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dabb4b4a0..175a044a3 100644 --- a/diff.c +++ b/diff.c @@ -1399,20 +1399,23 @@ static const struct funcname_pattern_entry *funcname_pattern(const char *ident) } static const struct funcname_pattern_entry builtin_funcname_pattern[] = { - { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|" - "new\\|return\\|switch\\|throw\\|while\\)\n" - "^[ ]*\\(\\([ ]*" - "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*\\)$", 0 }, - { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|" - "destructor\\|interface\\|implementation\\|" - "initialization\\|finalization\\)[ \t]*.*\\)$" - "\\|" - "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$", - 0 }, - { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 }, - { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 }, - { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$", 0 }, + { "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]*.*)$" + "|" + "^(.*=[ \t]*(class|record).*)$", + REG_EXTENDED }, + { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + REG_EXTENDED }, + { "tex", + "^(\\\\((sub)*section|chapter|part)\\*{0,1}\{.*)$", + REG_EXTENDED }, + { "ruby", "^[ \t]*((class|module|def)[ \t].*)$", + REG_EXTENDED }, }; static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one) -- cgit v1.2.1 From 1883a0d3b7ad7c9de1ac790bda6f1a6181237439 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Sep 2008 23:52:49 -0700 Subject: diff: use extended regexp to find hunk headers Using ERE elements such as "|" (alternation) by backquoting in BRE is a GNU extension and should not be done in portable programs. Signed-off-by: Junio C Hamano --- diff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 5b9b07485..a73301017 100644 --- a/diff.c +++ b/diff.c @@ -1406,7 +1406,7 @@ static const struct funcname_pattern_entry *funcname_pattern(const char *ident) static const struct funcname_pattern_entry builtin_funcname_pattern[] = { { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", REG_EXTENDED }, - { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$", 0 }, + { "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]*\\([^;]*)$", @@ -1417,8 +1417,8 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = { "|" "^(.*=[ \t]*(class|record).*)$", REG_EXTENDED }, - { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)", 0 }, - { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$", 0 }, + { "php", "^[\t ]*((function|class).*)", REG_EXTENDED }, + { "python", "^[ \t]*((class|def)[ \t].*)$", REG_EXTENDED }, { "ruby", "^[ \t]*((class|module|def)[ \t].*)$", REG_EXTENDED }, { "tex", -- cgit v1.2.1 From 3d8dccd74aa29a9019c4e8b52e75a40189e6f5cb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Sep 2008 00:52:11 -0700 Subject: diff: fix "multiple regexp" semantics to find hunk header comment When multiple regular expressions are concatenated with "\n", they were traditionally AND'ed together, and only a line that matches _all_ of them is taken as a match. This however is unwieldy when multiple regexp feature is used to specify alternatives. This fixes the semantics to take the first match. A nagative pattern, if matches, makes the line to fail as before. A match with a positive pattern will be the final match, and what it captures in $1 is used as the hunk header comment. We could write alternatives using "|" in ERE, but the machinery can only use captured $1 as the hunk header comment (or $0 if there is no match in $1), so you cannot write: "junk ( A | B ) | garbage ( C | D )" and expect both "junk" and "garbage" to get stripped with the existing code. With this fix, you can write it as: "junk ( A | B ) \n garbage ( C | D )" and the way capture works would match the user expectation more naturally. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a73301017..1bcbbd5bb 100644 --- a/diff.c +++ b/diff.c @@ -1414,7 +1414,7 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = { { "pascal", "^((procedure|function|constructor|destructor|interface|" "implementation|initialization|finalization)[ \t]*.*)$" - "|" + "\n" "^(.*=[ \t]*(class|record).*)$", REG_EXTENDED }, { "php", "^[\t ]*((function|class).*)", REG_EXTENDED }, -- cgit v1.2.1 From 96d1a8e9d44fd635fad8466dbe0aab6d73495c9f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Sep 2008 15:30:12 -0700 Subject: diff hunk pattern: fix misconverted "\{" tex macro introducers Pointed out by Brandon Casey. Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 175a044a3..a28373861 100644 --- a/diff.c +++ b/diff.c @@ -1409,10 +1409,10 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = { "|" "^(.*=[ \t]*(class|record).*)$", REG_EXTENDED }, - { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", REG_EXTENDED }, { "tex", - "^(\\\\((sub)*section|chapter|part)\\*{0,1}\{.*)$", + "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", REG_EXTENDED }, { "ruby", "^[ \t]*((class|module|def)[ \t].*)$", REG_EXTENDED }, -- cgit v1.2.1 From fdac6692a0b0eda293f9f1bf4bc49b05b29f3c45 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Mon, 22 Sep 2008 18:26:20 -0500 Subject: t4018-diff-funcname: test syntax of builtin xfuncname patterns [jc: fixes bibtex pattern breakage exposed by this test] Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0f98bff46..05dd8f0b5 100644 --- a/diff.c +++ b/diff.c @@ -1404,7 +1404,7 @@ static const struct funcname_pattern_entry *funcname_pattern(const char *ident) } static const struct funcname_pattern_entry builtin_funcname_pattern[] = { - { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", REG_EXTENDED }, { "html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", REG_EXTENDED }, { "java", -- cgit v1.2.1 From 416f80a60b209d2ca0326407b801e9fb6ed8fdd7 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Mon, 29 Sep 2008 16:52:01 -0500 Subject: diff.c: remove duplicate bibtex pattern introduced by merge 92bb9785 Signed-off-by: Brandon Casey Signed-off-by: Shawn O. Pearce --- diff.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b001d7b50..7c982b494 100644 --- a/diff.c +++ b/diff.c @@ -1439,8 +1439,6 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = { { "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 }, -- cgit v1.2.1 From 9126f0091f271f090cc030a788219574ab0fea97 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 1 Oct 2008 14:05:20 -0400 Subject: fix openssl headers conflicting with custom SHA1 implementations On ARM I have the following compilation errors: CC fast-import.o In file included from cache.h:8, from builtin.h:6, from fast-import.c:142: arm/sha1.h:14: error: conflicting types for 'SHA_CTX' /usr/include/openssl/sha.h:105: error: previous declaration of 'SHA_CTX' was here arm/sha1.h:16: error: conflicting types for 'SHA1_Init' /usr/include/openssl/sha.h:115: error: previous declaration of 'SHA1_Init' was here arm/sha1.h:17: error: conflicting types for 'SHA1_Update' /usr/include/openssl/sha.h:116: error: previous declaration of 'SHA1_Update' was here arm/sha1.h:18: error: conflicting types for 'SHA1_Final' /usr/include/openssl/sha.h:117: error: previous declaration of 'SHA1_Final' was here make: *** [fast-import.o] Error 1 This is because openssl header files are always included in git-compat-util.h since commit 684ec6c63c whenever NO_OPENSSL is not set, which somehow brings in clashing with the custom ARM version. Compilation of git is probably broken on PPC too for the same reason. Turns out that the only file requiring openssl/ssl.h and openssl/err.h is imap-send.c. But only moving those problematic includes there doesn't solve the issue as it also includes cache.h which brings in the conflicting local SHA1 header file. As suggested by Jeff King, the best solution is to rename our references to SHA1 functions and structure to something git specific, and define those according to the implementation used. Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- diff.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7c982b494..4e4e439e0 100644 --- a/diff.c +++ b/diff.c @@ -3087,7 +3087,7 @@ static void diff_summary(FILE *file, struct diff_filepair *p) } struct patch_id_t { - SHA_CTX *ctx; + git_SHA_CTX *ctx; int patchlen; }; @@ -3115,7 +3115,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len) new_len = remove_space(line, len); - SHA1_Update(data->ctx, line, new_len); + git_SHA1_Update(data->ctx, line, new_len); data->patchlen += new_len; } @@ -3124,11 +3124,11 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) { struct diff_queue_struct *q = &diff_queued_diff; int i; - SHA_CTX ctx; + git_SHA_CTX ctx; struct patch_id_t data; char buffer[PATH_MAX * 4 + 20]; - SHA1_Init(&ctx); + git_SHA1_Init(&ctx); memset(&data, 0, sizeof(struct patch_id_t)); data.ctx = &ctx; @@ -3190,7 +3190,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) len2, p->two->path, len1, p->one->path, len2, p->two->path); - SHA1_Update(&ctx, buffer, len1); + git_SHA1_Update(&ctx, buffer, len1); xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = 3; @@ -3199,7 +3199,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) &xpp, &xecfg, &ecb); } - SHA1_Final(sha1, &ctx); + git_SHA1_Final(sha1, &ctx); return 0; } -- cgit v1.2.1 From 71b989e7dd1dcf891369319cfeda0ed8b6a152e1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 5 Oct 2008 15:35:15 -0400 Subject: fix bogus "diff --git" header from "diff --no-index" When "git diff --no-index" is given an absolute pathname, it would generate a diff header with the absolute path prepended by the prefix, like: diff --git a/dev/null b/foo Not only is this nonsensical, and not only does it violate the description of diffs given in git-diff(1), but it would produce broken binary diffs. Unlike text diffs, the binary diffs don't contain the filenames anywhere else, and so "git apply" relies on this header to figure out the filename. This patch just refuses to use an invalid name for anything visible in the diff. Now, this fixes the "git diff --no-index --binary a /dev/null" kind of case (and we'll end up using "a" as the basename), but some other insane cases are impossible to handle. If you do git diff --no-index --binary a /bin/echo you'll still get a patch like diff --git a/a b/bin/echo old mode 100644 new mode 100755 index ... and "git apply" will refuse to apply it for a couple of reasons, and the diff is simply bogus. And that, btw, is no longer a bug, I think. It's impossible to know whethe the user meant for the patch to be a rename or not. And as such, refusing to apply it because you don't know what name you should use is probably _exactly_ the right thing to do! Original problem reported by Imre Deak. Test script and problem description by Jeff King. Signed-off-by: Jeff King Signed-off-by: Linus Torvalds Signed-off-by: Shawn O. Pearce --- diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 781fa15ac..f91f256c5 100644 --- a/diff.c +++ b/diff.c @@ -1465,6 +1465,10 @@ static void builtin_diff(const char *name_a, const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); + /* Never use a non-valid filename anywhere if at all possible */ + name_a = DIFF_FILE_VALID(one) ? name_a : name_b; + name_b = DIFF_FILE_VALID(two) ? name_b : name_a; + a_one = quote_two(o->a_prefix, name_a + (*name_a == '/')); b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; -- cgit v1.2.1 From 5d1e958e2467ef295637844fd85be7a8ab187ef1 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Wed, 1 Oct 2008 00:46:34 +0100 Subject: Teach git diff about Objective-C syntax Add support for recognition of Objective-C class & instance methods, C functions, and class implementation/interfaces. Signed-off-by: Jonathan del Strother Signed-off-by: Shawn O. Pearce --- diff.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 02e948c9d..2af3a9748 100644 --- a/diff.c +++ b/diff.c @@ -1429,6 +1429,16 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = { "!^[ \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 }, + { "objc", + /* Negate C statements that can look like functions */ + "!^[ \t]*(do|for|if|else|return|switch|while)\n" + /* Objective-C methods */ + "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n" + /* C functions */ + "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n" + /* Objective-C class/protocol definitions */ + "^(@(implementation|interface|protocol)[ \t].*)$", + REG_EXTENDED }, { "pascal", "^((procedure|function|constructor|destructor|interface|" "implementation|initialization|finalization)[ \t]*.*)$" -- cgit v1.2.1 From f285a2d7ed6548666989406de8f0e7233eb84368 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 9 Oct 2008 14:12:12 -0500 Subject: Replace calls to strbuf_init(&foo, 0) with STRBUF_INIT initializer Many call sites use strbuf_init(&foo, 0) to initialize local strbuf variable "foo" which has not been accessed since its declaration. These can be replaced with a static initialization using the STRBUF_INIT macro which is just as readable, saves a function call, and takes up fewer lines. Signed-off-by: Brandon Casey Signed-off-by: Shawn O. Pearce --- diff.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 2af3a9748..1c6be897b 100644 --- a/diff.c +++ b/diff.c @@ -217,9 +217,8 @@ static char *quote_two(const char *one, const char *two) { int need_one = quote_c_style(one, NULL, NULL, 1); int need_two = quote_c_style(two, NULL, NULL, 1); - struct strbuf res; + struct strbuf res = STRBUF_INIT; - strbuf_init(&res, 0); if (need_one + need_two) { strbuf_addch(&res, '"'); quote_c_style(one, &res, NULL, 1); @@ -683,7 +682,7 @@ static char *pprint_rename(const char *a, const char *b) { const char *old = a; const char *new = b; - struct strbuf name; + struct strbuf name = STRBUF_INIT; int pfx_length, sfx_length; int len_a = strlen(a); int len_b = strlen(b); @@ -691,7 +690,6 @@ static char *pprint_rename(const char *a, const char *b) int qlen_a = quote_c_style(a, NULL, NULL, 0); int qlen_b = quote_c_style(b, NULL, NULL, 0); - strbuf_init(&name, 0); if (qlen_a || qlen_b) { quote_c_style(a, &name, NULL, 0); strbuf_addstr(&name, " => "); @@ -834,8 +832,7 @@ static void fill_print_name(struct diffstat_file *file) return; if (!file->is_renamed) { - struct strbuf buf; - strbuf_init(&buf, 0); + struct strbuf buf = STRBUF_INIT; if (quote_c_style(file->name, &buf, NULL, 0)) { pname = strbuf_detach(&buf, NULL); } else { @@ -1820,10 +1817,9 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { - struct strbuf buf; + struct strbuf buf = STRBUF_INIT; size_t size = 0; - strbuf_init(&buf, 0); if (strbuf_read(&buf, 0, 0) < 0) return error("error while reading from stdin %s", strerror(errno)); @@ -1875,7 +1871,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (!s->sha1_valid || reuse_worktree_file(s->path, s->sha1, 0)) { - struct strbuf buf; + struct strbuf buf = STRBUF_INIT; struct stat st; int fd; @@ -1918,7 +1914,6 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) /* * Convert from working tree format to canonical git format */ - strbuf_init(&buf, 0); if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) { size_t size = 0; munmap(s->data, s->size); -- cgit v1.2.1 From be58e70dbadf3cb3f4aa5829d513d886ae8bc460 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 5 Oct 2008 17:43:21 -0400 Subject: diff: unify external diff and funcname parsing code Both sets of code assume that one specifies a diff profile as a gitattribute via the "diff=foo" attribute. They then pull information about that profile from the config as diff.foo.*. The code for each is currently completely separate from the other, which has several disadvantages: - there is duplication as we maintain code to create and search the separate lists of external drivers and funcname patterns - it is difficult to add new profile options, since it is unclear where they should go - the code is difficult to follow, as we rely on the "check if this file is binary" code to find the funcname pattern as a side effect. This is the first step in refactoring the binary-checking code. This patch factors out these diff profiles into "userdiff" drivers. A file with "diff=foo" uses the "foo" driver, which is specified by a single struct. Note that one major difference between the two pieces of code is that the funcname patterns are always loaded, whereas external drivers are loaded only for the "git diff" porcelain; the new code takes care to retain that situation. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- diff.c | 251 +++++++---------------------------------------------------------- 1 file changed, 27 insertions(+), 224 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 1c6be897b..d50355e84 100644 --- a/diff.c +++ b/diff.c @@ -11,6 +11,7 @@ #include "attr.h" #include "run-command.h" #include "utf8.h" +#include "userdiff.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -56,80 +57,6 @@ static int parse_diff_color_slot(const char *var, int ofs) die("bad config variable '%s'", var); } -static struct ll_diff_driver { - const char *name; - struct ll_diff_driver *next; - const char *cmd; -} *user_diff, **user_diff_tail; - -/* - * Currently there is only "diff..command" variable; - * because there are "diff.color." variables, we are parsing - * this in a bit convoluted way to allow low level diff driver - * called "color". - */ -static int parse_lldiff_command(const char *var, const char *ep, const char *value) -{ - const char *name; - int namelen; - struct ll_diff_driver *drv; - - name = var + 5; - namelen = ep - name; - for (drv = user_diff; drv; drv = drv->next) - if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) - break; - if (!drv) { - drv = xcalloc(1, sizeof(struct ll_diff_driver)); - drv->name = xmemdupz(name, namelen); - if (!user_diff_tail) - user_diff_tail = &user_diff; - *user_diff_tail = drv; - user_diff_tail = &(drv->next); - } - - return git_config_string(&(drv->cmd), var, value); -} - -/* - * 'diff..funcname' attribute can be specified in the configuration - * to define a customized regexp to find the beginning of a function to - * be used for hunk header lines of "diff -p" style output. - */ -struct funcname_pattern_entry { - char *name; - char *pattern; - 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, int cflags) -{ - const char *name; - int namelen; - struct funcname_pattern_list *pp; - - name = var + 5; /* "diff." */ - namelen = ep - name; - - for (pp = funcname_pattern_list; pp; pp = pp->next) - if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen]) - break; - if (!pp) { - pp = xcalloc(1, sizeof(*pp)); - pp->e.name = xmemdupz(name, namelen); - pp->next = funcname_pattern_list; - funcname_pattern_list = pp; - } - free(pp->e.pattern); - pp->e.pattern = xstrdup(value); - pp->e.cflags = cflags; - return 0; -} - /* * These are to give UI layer defaults. * The core-level commands such as git-diff-files should @@ -162,11 +89,11 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) } if (!strcmp(var, "diff.external")) return git_config_string(&external_diff_cmd_cfg, var, value); - if (!prefixcmp(var, "diff.")) { - const char *ep = strrchr(var, '.'); - if (ep != var + 4 && !strcmp(ep, ".command")) - return parse_lldiff_command(var, ep, value); + switch (userdiff_config_porcelain(var, value)) { + case 0: break; + case -1: return -1; + default: return 0; } return git_diff_basic_config(var, value, cb); @@ -193,21 +120,10 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return 0; } - if (!prefixcmp(var, "diff.")) { - const char *ep = strrchr(var, '.'); - if (ep != var + 4) { - if (!strcmp(ep, ".funcname")) { - if (!value) - return config_error_nonbool(var); - 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); - } - } + switch (userdiff_config_basic(var, value)) { + case 0: break; + case -1: return -1; + default: return 0; } return git_color_default_config(var, value, cb); @@ -1352,46 +1268,24 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two) emit_binary_diff_body(file, two, one); } -static void setup_diff_attr_check(struct git_attr_check *check) -{ - static struct git_attr *attr_diff; - - if (!attr_diff) { - attr_diff = git_attr("diff", 4); - } - check[0].attr = attr_diff; -} - static void diff_filespec_check_attr(struct diff_filespec *one) { - struct git_attr_check attr_diff_check; + struct userdiff_driver *drv; int check_from_data = 0; if (one->checked_attr) return; - setup_diff_attr_check(&attr_diff_check); + drv = userdiff_find_by_path(one->path); one->is_binary = 0; - one->funcname_pattern_ident = NULL; - if (!git_checkattr(one->path, 1, &attr_diff_check)) { - const char *value; - - /* binaryness */ - value = attr_diff_check.value; - if (ATTR_TRUE(value)) - ; - else if (ATTR_FALSE(value)) - one->is_binary = 1; - else - check_from_data = 1; - - /* funcname pattern ident */ - if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value)) - ; - else - one->funcname_pattern_ident = value; - } + /* binaryness */ + if (drv == USERDIFF_ATTR_TRUE) + ; + else if (drv == USERDIFF_ATTR_FALSE) + one->is_binary = 1; + else + check_from_data = 1; if (check_from_data) { if (!one->data && DIFF_FILE_VALID(one)) @@ -1408,80 +1302,12 @@ int diff_filespec_is_binary(struct diff_filespec *one) return one->is_binary; } -static const struct funcname_pattern_entry *funcname_pattern(const char *ident) -{ - struct funcname_pattern_list *pp; - - for (pp = funcname_pattern_list; pp; pp = pp->next) - if (!strcmp(ident, pp->e.name)) - return &pp->e; - return NULL; -} - -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 }, - { "objc", - /* Negate C statements that can look like functions */ - "!^[ \t]*(do|for|if|else|return|switch|while)\n" - /* Objective-C methods */ - "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n" - /* C functions */ - "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n" - /* Objective-C class/protocol definitions */ - "^(@(implementation|interface|protocol)[ \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 }, - { "tex", - "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", - REG_EXTENDED }, -}; - -static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one) +static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one) { - const char *ident; - const struct funcname_pattern_entry *pe; - int i; - - diff_filespec_check_attr(one); - ident = one->funcname_pattern_ident; - - if (!ident) - /* - * If the config file has "funcname.default" defined, that - * regexp is used; otherwise NULL is returned and xemit uses - * the built-in default. - */ - return funcname_pattern("default"); - - /* Look up custom "funcname.$ident" regexp from config. */ - pe = funcname_pattern(ident); - if (pe) - return pe; - - /* - * And define built-in fallback patterns here. Note that - * these can be overridden by the user's config settings. - */ - for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++) - if (!strcmp(ident, builtin_funcname_pattern[i].name)) - return &builtin_funcname_pattern[i]; - - return NULL; + struct userdiff_driver *drv = userdiff_find_by_path(one->path); + if (!drv) + drv = userdiff_find_by_name("default"); + return drv && drv->funcname.pattern ? &drv->funcname : NULL; } void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b) @@ -1579,7 +1405,7 @@ static void builtin_diff(const char *name_a, xdemitconf_t xecfg; xdemitcb_t ecb; struct emit_callback ecbdata; - const struct funcname_pattern_entry *pe; + const struct userdiff_funcname *pe; pe = diff_funcname_pattern(one); if (!pe) @@ -2117,29 +1943,6 @@ static void run_external_diff(const char *pgm, } } -static const char *external_diff_attr(const char *name) -{ - struct git_attr_check attr_diff_check; - - if (!name) - return NULL; - - setup_diff_attr_check(&attr_diff_check); - if (!git_checkattr(name, 1, &attr_diff_check)) { - const char *value = attr_diff_check.value; - if (!ATTR_TRUE(value) && - !ATTR_FALSE(value) && - !ATTR_UNSET(value)) { - struct ll_diff_driver *drv; - - for (drv = user_diff; drv; drv = drv->next) - if (!strcmp(drv->name, value)) - return drv->cmd; - } - } - return NULL; -} - static void run_diff_cmd(const char *pgm, const char *name, const char *other, @@ -2153,9 +1956,9 @@ static void run_diff_cmd(const char *pgm, if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { - const char *cmd = external_diff_attr(attr_path); - if (cmd) - pgm = cmd; + struct userdiff_driver *drv = userdiff_find_by_path(attr_path); + if (drv && drv->external) + pgm = drv->external; } if (pgm) { -- cgit v1.2.1 From 122aa6f9c000d0d286898e2eb7b3504ac6cb9ebd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 5 Oct 2008 17:43:36 -0400 Subject: diff: introduce diff..binary The "diff" gitattribute is somewhat overloaded right now. It can say one of three things: 1. this file is definitely binary, or definitely not (i.e., diff or !diff) 2. this file should use an external diff engine (i.e., diff=foo, diff.foo.command = custom-script) 3. this file should use particular funcname patterns (i.e., diff=foo, diff.foo.(x?)funcname = some-regex) Most of the time, there is no conflict between these uses, since using one implies that the other is irrelevant (e.g., an external diff engine will decide for itself whether the file is binary). However, there is at least one conflicting situation: there is no way to say "use the regular rules to determine whether this file is binary, but if we do diff it textually, use this funcname pattern." That is, currently setting diff=foo indicates that the file is definitely text. This patch introduces a "binary" config option for a diff driver, so that one can explicitly set diff.foo.binary. We default this value to "don't know". That is, setting a diff attribute to "foo" and using "diff.foo.funcname" will have no effect on the binaryness of a file. To get the current behavior, one can set diff.foo.binary to true. This patch also has one additional advantage: it cleans up the interface to the userdiff code a bit. Before, calling code had to know more about whether attributes were false, true, or unset to determine binaryness. Now that binaryness is a property of a driver, we can represent these situations just by passing back a driver struct. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- diff.c | 52 ++++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d50355e84..dabd7f50e 100644 --- a/diff.c +++ b/diff.c @@ -1268,46 +1268,37 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two) emit_binary_diff_body(file, two, one); } -static void diff_filespec_check_attr(struct diff_filespec *one) +void diff_filespec_load_driver(struct diff_filespec *one) { - struct userdiff_driver *drv; - int check_from_data = 0; - - if (one->checked_attr) - return; - - drv = userdiff_find_by_path(one->path); - one->is_binary = 0; - - /* binaryness */ - if (drv == USERDIFF_ATTR_TRUE) - ; - else if (drv == USERDIFF_ATTR_FALSE) - one->is_binary = 1; - else - check_from_data = 1; - - if (check_from_data) { - if (!one->data && DIFF_FILE_VALID(one)) - diff_populate_filespec(one, 0); - - if (one->data) - one->is_binary = buffer_is_binary(one->data, one->size); - } + if (!one->driver) + one->driver = userdiff_find_by_path(one->path); + if (!one->driver) + one->driver = userdiff_find_by_name("default"); } int diff_filespec_is_binary(struct diff_filespec *one) { - diff_filespec_check_attr(one); + if (one->is_binary == -1) { + diff_filespec_load_driver(one); + if (one->driver->binary != -1) + one->is_binary = one->driver->binary; + else { + if (!one->data && DIFF_FILE_VALID(one)) + diff_populate_filespec(one, 0); + if (one->data) + one->is_binary = buffer_is_binary(one->data, + one->size); + if (one->is_binary == -1) + one->is_binary = 0; + } + } return one->is_binary; } static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one) { - struct userdiff_driver *drv = userdiff_find_by_path(one->path); - if (!drv) - drv = userdiff_find_by_name("default"); - return drv && drv->funcname.pattern ? &drv->funcname : NULL; + diff_filespec_load_driver(one); + return one->driver->funcname.pattern ? &one->driver->funcname : NULL; } void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b) @@ -1559,6 +1550,7 @@ struct diff_filespec *alloc_filespec(const char *path) spec->path = (char *)(spec + 1); memcpy(spec->path, path, namelen+1); spec->count = 1; + spec->is_binary = -1; return spec; } -- cgit v1.2.1 From 9cb92c390cefd3bf3f71bbda12eb04893c861361 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 5 Oct 2008 17:43:45 -0400 Subject: diff: add filter for converting binary to text When diffing binary files, it is sometimes nice to see the differences of a canonical text form rather than either a binary patch or simply "binary files differ." Until now, the only option for doing this was to define an external diff command to perform the diff. This was a lot of work, since the external command needed to take care of doing the diff itself (including mode changes), and lost the benefit of git's colorization and other options. This patch adds a text conversion option, which converts a file to its canonical format before performing the diff. This is less flexible than an arbitrary external diff, but is much less work to set up. For example: $ echo '*.jpg diff=exif' >>.gitattributes $ git config diff.exif.textconv exiftool $ git config diff.exif.binary false allows one to see jpg diffs represented by the text output of exiftool. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- diff.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dabd7f50e..e368fef14 100644 --- a/diff.c +++ b/diff.c @@ -38,6 +38,9 @@ static char diff_colors[][COLOR_MAXLEN] = { "\033[41m", /* WHITESPACE (red background) */ }; +static void diff_filespec_load_driver(struct diff_filespec *one); +static char *run_textconv(const char *, struct diff_filespec *, size_t *); + static int parse_diff_color_slot(const char *var, int ofs) { if (!strcasecmp(var+ofs, "plain")) @@ -290,8 +293,19 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) } else if (diff_populate_filespec(one, 0)) return -1; - mf->ptr = one->data; - mf->size = one->size; + + diff_filespec_load_driver(one); + if (one->driver->textconv) { + size_t size; + mf->ptr = run_textconv(one->driver->textconv, one, &size); + if (!mf->ptr) + return -1; + mf->size = size; + } + else { + mf->ptr = one->data; + mf->size = one->size; + } return 0; } @@ -3373,3 +3387,34 @@ void diff_unmerge(struct diff_options *options, fill_filespec(one, sha1, mode); diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1; } + +static char *run_textconv(const char *pgm, struct diff_filespec *spec, + size_t *outsize) +{ + struct diff_tempfile temp; + const char *argv[3]; + const char **arg = argv; + struct child_process child; + struct strbuf buf = STRBUF_INIT; + + prepare_temp_file(spec->path, &temp, spec); + *arg++ = pgm; + *arg++ = temp.name; + *arg = NULL; + + memset(&child, 0, sizeof(child)); + child.argv = argv; + child.out = -1; + if (start_command(&child) != 0 || + strbuf_read(&buf, child.out, 0) < 0 || + finish_command(&child) != 0) { + if (temp.name == temp.tmp_path) + unlink(temp.name); + error("error running textconv command '%s'", pgm); + return NULL; + } + if (temp.name == temp.tmp_path) + unlink(temp.name); + + return strbuf_detach(&buf, outsize); +} -- cgit v1.2.1 From 9ccd0a88ac3ea13ac2df4630bfd01c02084f965e Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Sat, 25 Oct 2008 15:30:37 +0200 Subject: Always initialize xpparam_t to 0 We're going to be adding some parameters to this, so we can't have any uninitialized data in it. Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- diff.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 1c6be897b..f141e7c8f 100644 --- a/diff.c +++ b/diff.c @@ -470,6 +470,7 @@ static void diff_words_show(struct diff_words_data *diff_words) mmfile_t minus, plus; int i; + memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); minus.size = diff_words->minus.text.size; minus.ptr = xmalloc(minus.size); @@ -1585,6 +1586,7 @@ static void builtin_diff(const char *name_a, if (!pe) pe = diff_funcname_pattern(two); + memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.label_path = lbl; @@ -1658,6 +1660,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xdemitconf_t xecfg; xdemitcb_t ecb; + memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat, @@ -1704,6 +1707,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, xdemitconf_t xecfg; xdemitcb_t ecb; + memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); xecfg.ctxlen = 1; /* at least one context line */ xpp.flags = XDF_NEED_MINIMAL; @@ -3149,6 +3153,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) struct diff_filepair *p = q->queue[i]; int len1, len2; + memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); if (p->status == 0) return error("internal diff status error"); -- cgit v1.2.1 From 72cf48414071636546eddfbfc828eda81649cb48 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 26 Oct 2008 00:41:28 -0400 Subject: diff: add missing static declaration This function isn't used outside of diff.c; the 'static' was simply overlooked in the original writing. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e368fef14..d1fd594ba 100644 --- a/diff.c +++ b/diff.c @@ -1282,7 +1282,7 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two) emit_binary_diff_body(file, two, one); } -void diff_filespec_load_driver(struct diff_filespec *one) +static void diff_filespec_load_driver(struct diff_filespec *one) { if (!one->driver) one->driver = userdiff_find_by_path(one->path); -- cgit v1.2.1 From 04427ac8483f61dcb01a48c78a821f5042c88195 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 26 Oct 2008 00:44:53 -0400 Subject: refactor userdiff textconv code The original implementation of textconv put the conversion into fill_mmfile. This was a bad idea for a number of reasons: - it made the semantics of fill_mmfile unclear. In some cases, it was allocating data (if a text conversion occurred), and in some cases not (if we could use the data directly from the filespec). But the caller had no idea which had happened, and so didn't know whether the memory should be freed - similarly, the caller had no idea if a text conversion had occurred, and so didn't know whether the contents should be treated as binary or not. This meant that we incorrectly guessed that text-converted content was binary and didn't actually show it (unless the user overrode us with "diff.foo.binary = false", which then created problems in plumbing where the text conversion did _not_ occur) - not all callers of fill_mmfile want the text contents. In particular, we don't really want diffstat, whitespace checks, patch id generation, etc, to look at the converted contents. This patch pulls the conversion code directly into builtin_diff, so that we only see the conversion when generating an actual patch. We also then know whether we are doing a conversion, so we can check the binary-ness and free the data from the mmfile appropriately (the previous version leaked quite badly when text conversion was used) Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d1fd594ba..6f01595ec 100644 --- a/diff.c +++ b/diff.c @@ -294,18 +294,8 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) else if (diff_populate_filespec(one, 0)) return -1; - diff_filespec_load_driver(one); - if (one->driver->textconv) { - size_t size; - mf->ptr = run_textconv(one->driver->textconv, one, &size); - if (!mf->ptr) - return -1; - mf->size = size; - } - else { - mf->ptr = one->data; - mf->size = one->size; - } + mf->ptr = one->data; + mf->size = one->size; return 0; } @@ -1323,6 +1313,14 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const options->b_prefix = b; } +static const char *get_textconv(struct diff_filespec *one) +{ + if (!DIFF_FILE_VALID(one)) + return NULL; + diff_filespec_load_driver(one); + return one->driver->textconv; +} + static void builtin_diff(const char *name_a, const char *name_b, struct diff_filespec *one, @@ -1337,6 +1335,7 @@ static void builtin_diff(const char *name_a, const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); const char *a_prefix, *b_prefix; + const char *textconv_one, *textconv_two; diff_set_mnemonic_prefix(o, "a/", "b/"); if (DIFF_OPT_TST(o, REVERSE_DIFF)) { @@ -1390,8 +1389,12 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); + textconv_one = get_textconv(one); + textconv_two = get_textconv(two); + if (!DIFF_OPT_TST(o, TEXT) && - (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) { + ( (diff_filespec_is_binary(one) && !textconv_one) || + (diff_filespec_is_binary(two) && !textconv_two) )) { /* Quite common confusing case */ if (mf1.size == mf2.size && !memcmp(mf1.ptr, mf2.ptr, mf1.size)) @@ -1412,6 +1415,21 @@ static void builtin_diff(const char *name_a, struct emit_callback ecbdata; const struct userdiff_funcname *pe; + if (textconv_one) { + size_t size; + mf1.ptr = run_textconv(textconv_one, one, &size); + if (!mf1.ptr) + die("unable to read files to diff"); + mf1.size = size; + } + if (textconv_two) { + size_t size; + mf2.ptr = run_textconv(textconv_two, two, &size); + if (!mf2.ptr) + die("unable to read files to diff"); + mf2.size = size; + } + pe = diff_funcname_pattern(one); if (!pe) pe = diff_funcname_pattern(two); @@ -1443,6 +1461,10 @@ static void builtin_diff(const char *name_a, &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); + if (textconv_one) + free(mf1.ptr); + if (textconv_two) + free(mf2.ptr); } free_ab_and_return: -- cgit v1.2.1 From c7534ef4a12bb44806d522fc8e3961e390f9169b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 26 Oct 2008 00:45:55 -0400 Subject: userdiff: require explicitly allowing textconv Diffs that have been produced with textconv almost certainly cannot be applied, so we want to be careful not to generate them in things like format-patch. This introduces a new diff options, ALLOW_TEXTCONV, which controls this behavior. It is off by default, but is explicitly turned on for the "log" family of commands, as well as the "diff" porcelain (but not diff-* plumbing). Because both text conversion and external diffing are controlled by these diff options, we can get rid of the "plumbing versus porcelain" distinction when reading the config. This was an attempt to control the same thing, but suffered from being too coarse-grained. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6f01595ec..608223ab5 100644 --- a/diff.c +++ b/diff.c @@ -93,12 +93,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) if (!strcmp(var, "diff.external")) return git_config_string(&external_diff_cmd_cfg, var, value); - switch (userdiff_config_porcelain(var, value)) { - case 0: break; - case -1: return -1; - default: return 0; - } - return git_diff_basic_config(var, value, cb); } @@ -109,6 +103,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return 0; } + switch (userdiff_config(var, value)) { + case 0: break; + case -1: return -1; + default: return 0; + } + if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); if (!value) @@ -123,12 +123,6 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return 0; } - switch (userdiff_config_basic(var, value)) { - case 0: break; - case -1: return -1; - default: return 0; - } - return git_color_default_config(var, value, cb); } @@ -1335,7 +1329,7 @@ static void builtin_diff(const char *name_a, const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); const char *a_prefix, *b_prefix; - const char *textconv_one, *textconv_two; + const char *textconv_one = NULL, *textconv_two = NULL; diff_set_mnemonic_prefix(o, "a/", "b/"); if (DIFF_OPT_TST(o, REVERSE_DIFF)) { @@ -1389,8 +1383,10 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - textconv_one = get_textconv(one); - textconv_two = get_textconv(two); + if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) { + textconv_one = get_textconv(one); + textconv_two = get_textconv(two); + } if (!DIFF_OPT_TST(o, TEXT) && ( (diff_filespec_is_binary(one) && !textconv_one) || -- cgit v1.2.1 From 2675773af893ae81f9b09f18c1f2ec86ca2158e7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 26 Oct 2008 00:46:21 -0400 Subject: only textconv regular files We treat symlinks as text containing the results of the symlink, so it doesn't make much sense to text-convert them. Similarly gitlink components just end up as the text "Subproject commit $sha1", which we should leave intact. Note that a typechange may be broken into two parts: the removal of the old part and the addition of the new. In that case, we _do_ show the textconv for any part which is the addition or removal of a file we would ordinarily textconv, since it is purely acting on the file contents. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 608223ab5..23d454e71 100644 --- a/diff.c +++ b/diff.c @@ -1311,6 +1311,8 @@ static const char *get_textconv(struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) return NULL; + if (!S_ISREG(one->mode)) + return NULL; diff_filespec_load_driver(one); return one->driver->textconv; } -- cgit v1.2.1 From e10ea8126c27b079e81932ca487747b4961d0604 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 7 Dec 2008 21:57:01 -0500 Subject: diff: allow turning on textconv explicitly for plumbing Some history viewers use the diff plumbing to generate diffs rather than going through the "git diff" porcelain. Currently, there is no way for them to specify that they would like to see the text-converted version of the diff. This patch adds a "--textconv" option to allow such a plumbing user to allow text conversion. The user can then tell the viewer whether or not they would like text conversion enabled. While it may be tempting add a configuration option rather than requiring each plumbing user to be configured to pass --textconv, that is somewhat dangerous. Text-converted diffs generally cannot be applied directly, so each plumbing user should "opt in" to generating such a diff, either by explicit request of the user or by confirming that their output will not be fed to patch. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f644947c8..e21af3b7e 100644 --- a/diff.c +++ b/diff.c @@ -2477,6 +2477,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + else if (!strcmp(arg, "--textconv")) + DIFF_OPT_SET(options, ALLOW_TEXTCONV); + else if (!strcmp(arg, "--no-textconv")) + DIFF_OPT_CLR(options, ALLOW_TEXTCONV); else if (!strcmp(arg, "--ignore-submodules")) DIFF_OPT_SET(options, IGNORE_SUBMODULES); -- cgit v1.2.1 From 0c01857df57fe8723714e49459e0c061fcaf056b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 9 Dec 2008 03:12:28 -0500 Subject: diff: fix handling of binary rewrite diffs The current emit_rewrite_diff code always writes a text patch without checking whether the content is binary. This means that if you end up with a rewrite diff for a binary file, you get lots of raw binary goo in your patch. Instead, if we have binary files, then let's just skip emit_rewrite_diff altogether. We will already have shown the "dissimilarity index" line, so it is really about the diff contents. If binary diffs are turned off, the "Binary files a/file and b/file differ" message should be the same in either case. If we do have binary patches turned on, there isn't much point in making a less-efficient binary patch that does a total rewrite; no human is going to read it, and since binary patches don't apply with any fuzz anyway, the result of application should be the same. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e21af3b7e..50277b82b 100644 --- a/diff.c +++ b/diff.c @@ -1376,7 +1376,9 @@ static void builtin_diff(const char *name_a, */ if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; - if (complete_rewrite) { + if (complete_rewrite && + !diff_filespec_is_binary(one) && + !diff_filespec_is_binary(two)) { emit_rewrite_diff(name_a, name_b, one, two, o); o->found_changes = 1; goto free_ab_and_return; -- cgit v1.2.1 From 3aa1f7ca3779f73164b285c070b71abcdd7397c1 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 9 Dec 2008 03:13:21 -0500 Subject: diff: respect textconv in rewrite diffs Currently we just skip rewrite diffs for binary files; this patch makes an exception for files which will be textconv'd, and actually performs the textconv before generating the diff. Conceptually, rewrite diffs should be in the exact same format as the a non-rewrite diff, except that we refuse to share any context. Thus it makes very little sense for "git diff" to show a textconv'd diff, but for "git diff -B" to show "Binary files differ". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 50277b82b..afefe087b 100644 --- a/diff.c +++ b/diff.c @@ -229,6 +229,8 @@ static void emit_rewrite_diff(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, + const char *textconv_one, + const char *textconv_two, struct diff_options *o) { int lc_a, lc_b; @@ -241,6 +243,8 @@ static void emit_rewrite_diff(const char *name_a, const char *reset = diff_get_color(color_diff, DIFF_RESET); static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT; const char *a_prefix, *b_prefix; + const char *data_one, *data_two; + size_t size_one, size_two; if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) { a_prefix = o->b_prefix; @@ -262,8 +266,27 @@ static void emit_rewrite_diff(const char *name_a, diff_populate_filespec(one, 0); diff_populate_filespec(two, 0); - lc_a = count_lines(one->data, one->size); - lc_b = count_lines(two->data, two->size); + if (textconv_one) { + data_one = run_textconv(textconv_one, one, &size_one); + if (!data_one) + die("unable to read files to diff"); + } + else { + data_one = one->data; + size_one = one->size; + } + if (textconv_two) { + data_two = run_textconv(textconv_two, two, &size_two); + if (!data_two) + die("unable to read files to diff"); + } + else { + data_two = two->data; + size_two = two->size; + } + + lc_a = count_lines(data_one, size_one); + lc_b = count_lines(data_two, size_two); fprintf(o->file, "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", metainfo, a_name.buf, name_a_tab, reset, @@ -273,9 +296,9 @@ static void emit_rewrite_diff(const char *name_a, print_line_count(o->file, lc_b); fprintf(o->file, " @@%s\n", reset); if (lc_a) - copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset); + copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset); if (lc_b) - copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset); + copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset); } static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) @@ -1334,6 +1357,11 @@ static void builtin_diff(const char *name_a, const char *a_prefix, *b_prefix; const char *textconv_one = NULL, *textconv_two = NULL; + if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) { + textconv_one = get_textconv(one); + textconv_two = get_textconv(two); + } + diff_set_mnemonic_prefix(o, "a/", "b/"); if (DIFF_OPT_TST(o, REVERSE_DIFF)) { a_prefix = o->b_prefix; @@ -1377,9 +1405,10 @@ static void builtin_diff(const char *name_a, if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; if (complete_rewrite && - !diff_filespec_is_binary(one) && - !diff_filespec_is_binary(two)) { - emit_rewrite_diff(name_a, name_b, one, two, o); + (textconv_one || !diff_filespec_is_binary(one)) && + (textconv_two || !diff_filespec_is_binary(two))) { + emit_rewrite_diff(name_a, name_b, one, two, + textconv_one, textconv_two, o); o->found_changes = 1; goto free_ab_and_return; } @@ -1388,11 +1417,6 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) { - textconv_one = get_textconv(one); - textconv_two = get_textconv(two); - } - if (!DIFF_OPT_TST(o, TEXT) && ( (diff_filespec_is_binary(one) && !textconv_one) || (diff_filespec_is_binary(two) && !textconv_two) )) { -- cgit v1.2.1 From cf219d8c68ada1aa2855f4862f15753a32d09641 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 Dec 2008 10:26:13 -0800 Subject: Make 'diff_populate_filespec()' use the new 'strbuf_readlink()' This makes all tests pass on a system where 'lstat()' has been hacked to return bogus data in st_size for symlinks. Of course, the test coverage isn't complete, but it's a good baseline. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index afefe087b..4b2029caa 100644 --- a/diff.c +++ b/diff.c @@ -1773,19 +1773,17 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) s->size = xsize_t(st.st_size); if (!s->size) goto empty; - if (size_only) - return 0; if (S_ISLNK(st.st_mode)) { - int ret; - s->data = xmalloc(s->size); - s->should_free = 1; - ret = readlink(s->path, s->data, s->size); - if (ret < 0) { - free(s->data); + struct strbuf sb = STRBUF_INIT; + + if (strbuf_readlink(&sb, s->path, s->size)) goto err_empty; - } + s->data = strbuf_detach(&sb, &s->size); + s->should_free = 1; return 0; } + if (size_only) + return 0; fd = open(s->path, O_RDONLY); if (fd < 0) goto err_empty; -- cgit v1.2.1 From dfab6aaecfe9df67123efc778f6aea4e9814715a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 Dec 2008 10:31:36 -0800 Subject: Make 'prepare_temp_file()' ignore st_size for symlinks The code was already set up to not really need it, so this just massages it a bit to remove the use entirely. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 4b2029caa..f160c1a35 100644 --- a/diff.c +++ b/diff.c @@ -1881,13 +1881,12 @@ static void prepare_temp_file(const char *name, if (S_ISLNK(st.st_mode)) { int ret; char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */ - size_t sz = xsize_t(st.st_size); - if (sizeof(buf) <= st.st_size) - die("symlink too long: %s", name); - ret = readlink(name, buf, sz); + ret = readlink(name, buf, sizeof(buf)); if (ret < 0) die("readlink(%s)", name); - prep_temp_blob(temp, buf, sz, + if (ret == sizeof(buf)) + die("symlink too long: %s", name); + prep_temp_blob(temp, buf, ret, (one->sha1_valid ? one->sha1 : null_sha1), (one->sha1_valid ? -- cgit v1.2.1 From 0956a6db7ae3a93c7bce62c1e3a6e0795055ad9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Thu, 18 Dec 2008 17:56:51 +0100 Subject: Fix type-mismatch compiler warning from diff_populate_filespec() The type of the size member of filespec is ulong, while strbuf_detach expects a size_t pointer. This patch should fix the warning: Signed-off-by: Junio C Hamano --- diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f160c1a35..0484601f4 100644 --- a/diff.c +++ b/diff.c @@ -1778,7 +1778,8 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (strbuf_readlink(&sb, s->path, s->size)) goto err_empty; - s->data = strbuf_detach(&sb, &s->size); + s->size = sb.len; + s->data = strbuf_detach(&sb, NULL); s->should_free = 1; return 0; } -- cgit v1.2.1 From 6d0e674a575421347abe5749e645ca6dc78c8207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 28 Dec 2008 19:45:32 +0100 Subject: diff: add option to show context between close hunks Merge two hunks if there is only the specified number of otherwise unshown context between them. For --inter-hunk-context=1, the resulting patch has the same number of lines but shows uninterrupted context instead of a context header line in between. Patches generated with this option are easier to read but are also more likely to conflict if the file to be patched contains other changes. This patch keeps the default for this option at 0. It is intended to just make the feature available in order to see its advantages and downsides. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0484601f4..56b80f960 100644 --- a/diff.c +++ b/diff.c @@ -1469,6 +1469,7 @@ static void builtin_diff(const char *name_a, ecbdata.file = o->file; xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; + xecfg.interhunkctxlen = o->interhunkcontext; xecfg.flags = XDL_EMIT_FUNCNAMES; if (pe) xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); @@ -2538,6 +2539,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->b_prefix = arg + 13; else if (!strcmp(arg, "--no-prefix")) options->a_prefix = options->b_prefix = ""; + else if (opt_arg(arg, '\0', "inter-hunk-context", + &options->interhunkcontext)) + ; else if (!prefixcmp(arg, "--output=")) { options->file = fopen(arg + strlen("--output="), "w"); options->close_file = 1; -- cgit v1.2.1 From d75307084da5f89329de190bb9b4a3196cec1d0e Mon Sep 17 00:00:00 2001 From: Alexander Potashev Date: Sun, 4 Jan 2009 21:38:41 +0300 Subject: remove trailing LF in die() messages LF at the end of format strings given to die() is redundant because die already adds one on its own. Signed-off-by: Alexander Potashev Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0484601f4..c159a5fc6 100644 --- a/diff.c +++ b/diff.c @@ -2039,7 +2039,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one) if (lstat(one->path, &st) < 0) die("stat %s", one->path); if (index_path(one->sha1, one->path, &st, 0)) - die("cannot hash %s\n", one->path); + die("cannot hash %s", one->path); } } else -- cgit v1.2.1 From 34292bddb861f3cb52a524fdce67234430a744fe Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 1 Jan 2009 17:39:17 +0100 Subject: Introduce the diff option '--patience' This commit teaches Git to produce diff output using the patience diff algorithm with the diff option '--patience'. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0484601f4..4aeab7746 100644 --- a/diff.c +++ b/diff.c @@ -2471,6 +2471,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; else if (!strcmp(arg, "--ignore-space-at-eol")) options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + else if (!strcmp(arg, "--patience")) + options->xdl_opts |= XDF_PATIENCE_DIFF; /* flags options */ else if (!strcmp(arg, "--binary")) { -- cgit v1.2.1 From 23c1575f747393f9847874fd1ed72a44557459d1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 17 Jan 2009 17:29:43 +0100 Subject: color-words: refactor word splitting and use ALLOC_GROW() Word splitting is now performed by the function diff_words_fill(), avoiding having the same code twice. In the same spirit, avoid duplicating the code of ALLOC_GROW(). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d23548292..c111eef13 100644 --- a/diff.c +++ b/diff.c @@ -326,10 +326,7 @@ struct diff_words_buffer { static void diff_words_append(char *line, unsigned long len, struct diff_words_buffer *buffer) { - if (buffer->text.size + len > buffer->alloc) { - buffer->alloc = (buffer->text.size + len) * 3 / 2; - buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc); - } + ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc); line++; len--; memcpy(buffer->text.ptr + buffer->text.size, line, len); @@ -398,6 +395,22 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) } } +/* + * This function splits the words in buffer->text, and stores the list with + * newline separator into out. + */ +static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out) +{ + int i; + out->size = buffer->text.size; + out->ptr = xmalloc(out->size); + memcpy(out->ptr, buffer->text.ptr, out->size); + for (i = 0; i < out->size; i++) + if (isspace(out->ptr[i])) + out->ptr[i] = '\n'; + buffer->current = 0; +} + /* this executes the word diff on the accumulated buffers */ static void diff_words_show(struct diff_words_data *diff_words) { @@ -405,26 +418,11 @@ static void diff_words_show(struct diff_words_data *diff_words) xdemitconf_t xecfg; xdemitcb_t ecb; mmfile_t minus, plus; - int i; memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); - minus.size = diff_words->minus.text.size; - minus.ptr = xmalloc(minus.size); - memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size); - for (i = 0; i < minus.size; i++) - if (isspace(minus.ptr[i])) - minus.ptr[i] = '\n'; - diff_words->minus.current = 0; - - plus.size = diff_words->plus.text.size; - plus.ptr = xmalloc(plus.size); - memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size); - for (i = 0; i < plus.size; i++) - if (isspace(plus.ptr[i])) - plus.ptr[i] = '\n'; - diff_words->plus.current = 0; - + diff_words_fill(&diff_words->minus, &minus); + diff_words_fill(&diff_words->plus, &plus); xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, -- cgit v1.2.1 From 2e5d2003b28820f88296e47a79eb440ca0295000 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 17 Jan 2009 17:29:44 +0100 Subject: color-words: change algorithm to allow for 0-character word boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, the color-words code assumed that word boundaries are identical to white space characters. Therefore, it could get away with a very simple scheme: it copied the hunks, substituted newlines for each white space character, called libxdiff with the processed text, and then identified the text to output by the offsets (which agreed since the original text had the same length). This code was ugly, for a number of reasons: - it was impossible to introduce 0-character word boundaries, - we had to print everything word by word, and - the code needed extra special handling of newlines in the removed part. Fix all of these issues by processing the text such that - we build word lists, separated by newlines, - we remember the original offsets for every word, and - after calling libxdiff on the wordlists, we parse the hunk headers, and find the corresponding offsets, and then - we print the removed/added parts in one go. The pre and post samples in the test were provided by Santi Béjar. Note that there is some strange special handling of hunk headers where one line range is 0 due to POSIX: in this case, the start is one too low. In other words a hunk header '@@ -1,0 +2 @@' actually means that the line must be added after the _second_ line of the pre text, _not_ the first. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 157 ++++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 66 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c111eef13..37c886a81 100644 --- a/diff.c +++ b/diff.c @@ -319,8 +319,10 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) struct diff_words_buffer { mmfile_t text; long alloc; - long current; /* output pointer */ - int suppressed_newline; + struct diff_words_orig { + const char *begin, *end; + } *orig; + int orig_nr, orig_alloc; }; static void diff_words_append(char *line, unsigned long len, @@ -335,80 +337,89 @@ static void diff_words_append(char *line, unsigned long len, struct diff_words_data { struct diff_words_buffer minus, plus; + const char *current_plus; FILE *file; }; -static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color, - int suppress_newline) +static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) { - const char *ptr; - int eol = 0; + struct diff_words_data *diff_words = priv; + int minus_first, minus_len, plus_first, plus_len; + const char *minus_begin, *minus_end, *plus_begin, *plus_end; - if (len == 0) + if (line[0] != '@' || parse_hunk_header(line, len, + &minus_first, &minus_len, &plus_first, &plus_len)) return; - ptr = buffer->text.ptr + buffer->current; - buffer->current += len; + /* POSIX requires that first be decremented by one if len == 0... */ + if (minus_len) { + minus_begin = diff_words->minus.orig[minus_first].begin; + minus_end = + diff_words->minus.orig[minus_first + minus_len - 1].end; + } else + minus_begin = minus_end = + diff_words->minus.orig[minus_first].end; - if (ptr[len - 1] == '\n') { - eol = 1; - len--; - } + if (plus_len) { + plus_begin = diff_words->plus.orig[plus_first].begin; + plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end; + } else + plus_begin = plus_end = diff_words->plus.orig[plus_first].end; - fputs(diff_get_color(1, color), file); - fwrite(ptr, len, 1, file); - fputs(diff_get_color(1, DIFF_RESET), file); + if (diff_words->current_plus != plus_begin) + fwrite(diff_words->current_plus, + plus_begin - diff_words->current_plus, 1, + diff_words->file); + if (minus_begin != minus_end) + color_fwrite_lines(diff_words->file, + diff_get_color(1, DIFF_FILE_OLD), + minus_end - minus_begin, minus_begin); + if (plus_begin != plus_end) + color_fwrite_lines(diff_words->file, + diff_get_color(1, DIFF_FILE_NEW), + plus_end - plus_begin, plus_begin); - if (eol) { - if (suppress_newline) - buffer->suppressed_newline = 1; - else - putc('\n', file); - } -} - -static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) -{ - struct diff_words_data *diff_words = priv; - - if (diff_words->minus.suppressed_newline) { - if (line[0] != '+') - putc('\n', diff_words->file); - diff_words->minus.suppressed_newline = 0; - } - - len--; - switch (line[0]) { - case '-': - print_word(diff_words->file, - &diff_words->minus, len, DIFF_FILE_OLD, 1); - break; - case '+': - print_word(diff_words->file, - &diff_words->plus, len, DIFF_FILE_NEW, 0); - break; - case ' ': - print_word(diff_words->file, - &diff_words->plus, len, DIFF_PLAIN, 0); - diff_words->minus.current += len; - break; - } + diff_words->current_plus = plus_end; } /* - * This function splits the words in buffer->text, and stores the list with - * newline separator into out. + * This function splits the words in buffer->text, stores the list with + * newline separator into out, and saves the offsets of the original words + * in buffer->orig. */ static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out) { - int i; - out->size = buffer->text.size; - out->ptr = xmalloc(out->size); - memcpy(out->ptr, buffer->text.ptr, out->size); - for (i = 0; i < out->size; i++) - if (isspace(out->ptr[i])) - out->ptr[i] = '\n'; - buffer->current = 0; + int i, j; + + out->size = 0; + out->ptr = xmalloc(buffer->text.size); + + /* fake an empty "0th" word */ + ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc); + buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr; + buffer->orig_nr = 1; + + for (i = 0; i < buffer->text.size; i++) { + if (isspace(buffer->text.ptr[i])) + continue; + for (j = i + 1; j < buffer->text.size && + !isspace(buffer->text.ptr[j]); j++) + ; /* find the end of the word */ + + /* store original boundaries */ + ALLOC_GROW(buffer->orig, buffer->orig_nr + 1, + buffer->orig_alloc); + buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i; + buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j; + buffer->orig_nr++; + + /* store one word */ + memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i); + out->ptr[out->size + j - i] = '\n'; + out->size += j - i + 1; + + i = j - 1; + } } /* this executes the word diff on the accumulated buffers */ @@ -419,22 +430,34 @@ static void diff_words_show(struct diff_words_data *diff_words) xdemitcb_t ecb; mmfile_t minus, plus; + /* special case: only removal */ + if (!diff_words->plus.text.size) { + color_fwrite_lines(diff_words->file, + diff_get_color(1, DIFF_FILE_OLD), + diff_words->minus.text.size, diff_words->minus.text.ptr); + diff_words->minus.text.size = 0; + return; + } + + diff_words->current_plus = diff_words->plus.text.ptr; + memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); diff_words_fill(&diff_words->minus, &minus); diff_words_fill(&diff_words->plus, &plus); xpp.flags = XDF_NEED_MINIMAL; - xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; + xecfg.ctxlen = 0; xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); + if (diff_words->current_plus != diff_words->plus.text.ptr + + diff_words->plus.text.size) + fwrite(diff_words->current_plus, + diff_words->plus.text.ptr + diff_words->plus.text.size + - diff_words->current_plus, 1, + diff_words->file); diff_words->minus.text.size = diff_words->plus.text.size = 0; - - if (diff_words->minus.suppressed_newline) { - putc('\n', diff_words->file); - diff_words->minus.suppressed_newline = 0; - } } typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); @@ -458,7 +481,9 @@ static void free_diff_words_data(struct emit_callback *ecbdata) diff_words_show(ecbdata->diff_words); free (ecbdata->diff_words->minus.text.ptr); + free (ecbdata->diff_words->minus.orig); free (ecbdata->diff_words->plus.text.ptr); + free (ecbdata->diff_words->plus.orig); free(ecbdata->diff_words); ecbdata->diff_words = NULL; } -- cgit v1.2.1 From 2b6a5417d750d086d1da906e46de2b3ad8df6753 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 17 Jan 2009 17:29:45 +0100 Subject: color-words: take an optional regular expression describing words In some applications, words are not delimited by white space. To allow for that, you can specify a regular expression describing what makes a word with git diff --color-words='[A-Za-z0-9]+' Note that words cannot contain newline characters. As suggested by Thomas Rast, the words are the exact matches of the regular expression. Note that a regular expression beginning with a '^' will match only a word at the beginning of the hunk, not a word at the beginning of a line, and is probably not what you want. This commit contains a quoting fix by Thomas Rast. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 9 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 37c886a81..9fb3d0df3 100644 --- a/diff.c +++ b/diff.c @@ -333,12 +333,14 @@ static void diff_words_append(char *line, unsigned long len, len--; memcpy(buffer->text.ptr + buffer->text.size, line, len); buffer->text.size += len; + buffer->text.ptr[buffer->text.size] = '\0'; } struct diff_words_data { struct diff_words_buffer minus, plus; const char *current_plus; FILE *file; + regex_t *word_regex; }; static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) @@ -382,17 +384,49 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) diff_words->current_plus = plus_end; } +/* This function starts looking at *begin, and returns 0 iff a word was found. */ +static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex, + int *begin, int *end) +{ + if (word_regex && *begin < buffer->size) { + regmatch_t match[1]; + if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) { + char *p = memchr(buffer->ptr + *begin + match[0].rm_so, + '\n', match[0].rm_eo - match[0].rm_so); + *end = p ? p - buffer->ptr : match[0].rm_eo + *begin; + *begin += match[0].rm_so; + return *begin >= *end; + } + return -1; + } + + /* find the next word */ + while (*begin < buffer->size && isspace(buffer->ptr[*begin])) + (*begin)++; + if (*begin >= buffer->size) + return -1; + + /* find the end of the word */ + *end = *begin + 1; + while (*end < buffer->size && !isspace(buffer->ptr[*end])) + (*end)++; + + return 0; +} + /* * This function splits the words in buffer->text, stores the list with * newline separator into out, and saves the offsets of the original words * in buffer->orig. */ -static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out) +static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out, + regex_t *word_regex) { int i, j; + long alloc = 0; out->size = 0; - out->ptr = xmalloc(buffer->text.size); + out->ptr = NULL; /* fake an empty "0th" word */ ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc); @@ -400,11 +434,8 @@ static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out) buffer->orig_nr = 1; for (i = 0; i < buffer->text.size; i++) { - if (isspace(buffer->text.ptr[i])) - continue; - for (j = i + 1; j < buffer->text.size && - !isspace(buffer->text.ptr[j]); j++) - ; /* find the end of the word */ + if (find_word_boundaries(&buffer->text, word_regex, &i, &j)) + return; /* store original boundaries */ ALLOC_GROW(buffer->orig, buffer->orig_nr + 1, @@ -414,6 +445,7 @@ static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out) buffer->orig_nr++; /* store one word */ + ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc); memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i); out->ptr[out->size + j - i] = '\n'; out->size += j - i + 1; @@ -443,9 +475,10 @@ static void diff_words_show(struct diff_words_data *diff_words) memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); - diff_words_fill(&diff_words->minus, &minus); - diff_words_fill(&diff_words->plus, &plus); + diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex); + diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex); xpp.flags = XDF_NEED_MINIMAL; + /* as only the hunk header will be parsed, we need a 0-context */ xecfg.ctxlen = 0; xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, &xpp, &xecfg, &ecb); @@ -484,6 +517,7 @@ static void free_diff_words_data(struct emit_callback *ecbdata) free (ecbdata->diff_words->minus.orig); free (ecbdata->diff_words->plus.text.ptr); free (ecbdata->diff_words->plus.orig); + free(ecbdata->diff_words->word_regex); free(ecbdata->diff_words); ecbdata->diff_words = NULL; } @@ -1506,6 +1540,14 @@ static void builtin_diff(const char *name_a, ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; + if (o->word_regex) { + ecbdata.diff_words->word_regex = (regex_t *) + xmalloc(sizeof(regex_t)); + if (regcomp(ecbdata.diff_words->word_regex, + o->word_regex, REG_EXTENDED)) + die ("Invalid regular expression: %s", + o->word_regex); + } } xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, &xpp, &xecfg, &ecb); @@ -2517,6 +2559,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_CLR(options, COLOR_DIFF); else if (!strcmp(arg, "--color-words")) options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + else if (!prefixcmp(arg, "--color-words=")) { + options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + options->word_regex = arg + 14; + } else if (!strcmp(arg, "--exit-code")) DIFF_OPT_SET(options, EXIT_WITH_STATUS); else if (!strcmp(arg, "--quiet")) -- cgit v1.2.1 From bf82940dbf12f066ba42a2a03a5bb626ba22c067 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 17 Jan 2009 17:29:46 +0100 Subject: color-words: enable REG_NEWLINE to help user We silently truncate a match at the newline, which may lead to unexpected behaviour, e.g., when matching "<[^>]*>" against since then "" doesn't!) even though the regex said only angle-bracket-delimited things can be words. To alleviate the problem slightly, use REG_NEWLINE so that negated classes can't match a newline. Of course newlines can still be matched explicitly. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9fb3d0df3..00c661f82 100644 --- a/diff.c +++ b/diff.c @@ -1544,7 +1544,8 @@ static void builtin_diff(const char *name_a, ecbdata.diff_words->word_regex = (regex_t *) xmalloc(sizeof(regex_t)); if (regcomp(ecbdata.diff_words->word_regex, - o->word_regex, REG_EXTENDED)) + o->word_regex, + REG_EXTENDED | REG_NEWLINE)) die ("Invalid regular expression: %s", o->word_regex); } -- cgit v1.2.1 From 80c49c3de2d5a3aa12b0980a65f1163c8aef0c16 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 17 Jan 2009 17:29:48 +0100 Subject: color-words: make regex configurable via attributes Make the --color-words splitting regular expression configurable via the diff driver's 'wordregex' attribute. The user can then set the driver on a file in .gitattributes. If a regex is given on the command line, it overrides the driver's setting. We also provide built-in regexes for the languages that already had funcname patterns, and add an appropriate diff driver entry for C/++. (The patterns are designed to run UTF-8 sequences into a single chunk to make sure they remain readable.) Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 00c661f82..9fcde963d 100644 --- a/diff.c +++ b/diff.c @@ -1380,6 +1380,12 @@ static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespe return one->driver->funcname.pattern ? &one->driver->funcname : NULL; } +static const char *userdiff_word_regex(struct diff_filespec *one) +{ + diff_filespec_load_driver(one); + return one->driver->word_regex; +} + void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b) { if (!options->a_prefix) @@ -1540,6 +1546,10 @@ static void builtin_diff(const char *name_a, ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; + if (!o->word_regex) + o->word_regex = userdiff_word_regex(one); + if (!o->word_regex) + o->word_regex = userdiff_word_regex(two); if (o->word_regex) { ecbdata.diff_words->word_regex = (regex_t *) xmalloc(sizeof(regex_t)); -- cgit v1.2.1 From 950db8798d51cb183c858938263425b367b21dfd Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 20 Jan 2009 22:08:33 +0100 Subject: Rename diff.suppress-blank-empty to diff.suppressBlankEmpty All the other config variables use CamelCase. This config variable should not be an exception. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 0484601f4..5b85b4077 100644 --- a/diff.c +++ b/diff.c @@ -118,7 +118,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) } /* like GNU diff's --suppress-blank-empty option */ - if (!strcmp(var, "diff.suppress-blank-empty")) { + if (!strcmp(var, "diff.suppressblankempty") || + /* for backwards compatibility */ + !strcmp(var, "diff.suppress-blank-empty")) { diff_suppress_blank_empty = git_config_bool(var, value); return 0; } -- cgit v1.2.1 From 98a4d87b87e9846eafd21ba232cc2b7ba3f718fc Mon Sep 17 00:00:00 2001 From: Boyd Stephen Smith Jr Date: Tue, 20 Jan 2009 21:46:57 -0600 Subject: color-words: Support diff.wordregex config option When diff is invoked with --color-words (w/o =regex), use the regular expression the user has configured as diff.wordregex. diff drivers configured via attributes take precedence over the diff.wordregex-words setting. If the user wants to change them, they have their own configuration variables. Signed-off-by: Boyd Stephen Smith Jr Signed-off-by: Junio C Hamano --- diff.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9fcde963d..ed8b83c68 100644 --- a/diff.c +++ b/diff.c @@ -23,6 +23,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 200; static int diff_suppress_blank_empty; int diff_use_color_default = -1; +static const char *diff_word_regex_cfg; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; @@ -92,6 +93,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) } if (!strcmp(var, "diff.external")) return git_config_string(&external_diff_cmd_cfg, var, value); + if (!strcmp(var, "diff.wordregex")) + return git_config_string(&diff_word_regex_cfg, var, value); return git_diff_basic_config(var, value, cb); } @@ -1550,6 +1553,8 @@ static void builtin_diff(const char *name_a, o->word_regex = userdiff_word_regex(one); if (!o->word_regex) o->word_regex = userdiff_word_regex(two); + if (!o->word_regex) + o->word_regex = diff_word_regex_cfg; if (o->word_regex) { ecbdata.diff_words->word_regex = (regex_t *) xmalloc(sizeof(regex_t)); -- cgit v1.2.1 From 479b0ae81c9291a8bb8d7b2347cc58eeaa701304 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 22 Jan 2009 00:59:56 -0500 Subject: diff: refactor tempfile cleanup handling There are two pieces of code that create tempfiles for diff: run_external_diff and run_textconv. The former cleans up its tempfiles in the face of premature death (i.e., by die() or by signal), but the latter does not. After this patch, they will both use the same cleanup routines. To make clear what the change is, let me first explain what happens now: - run_external_diff uses a static global array of 2 diff_tempfile structs (since it knows it will always need exactly 2 tempfiles). It calls prepare_temp_file (which doesn't know anything about the global array) on each of the structs, creating the tempfiles that need to be cleaned up. It then registers atexit and signal handlers to look through the global array and remove the tempfiles. If it succeeds, it calls the handler manually (which marks the tempfile structs as unused). - textconv has its own tempfile struct, which it allocates using prepare_temp_file and cleans up manually. No signal or atexit handlers. The new code moves the installation of cleanup handlers into the prepare_temp_file function. Which means that that function now has to understand that there is static tempfile storage. So what happens now is: - run_external_diff calls prepare_temp_file - prepare_temp_file calls claim_diff_tempfile, which allocates an unused slot from our global array - prepare_temp_file installs (if they have not already been installed) atexit and signal handlers for cleanup - prepare_temp_file sets up the tempfile as usual - prepare_temp_file returns a pointer to the allocated tempfile The advantage being that run_external_diff no longer has to care about setting up cleanup handlers. Now by virtue of calling prepare_temp_file, run_textconv gets the same benefit, as will any future users of prepare_temp_file. There are also a few side benefits to the specific implementation: - we now install cleanup handlers _before_ allocating the tempfile, closing a race which could leave temp cruft - when allocating a slot in the global array, we will now detect a situation where the old slots were not properly vacated (i.e., somebody forgot to call remove upon leaving the function). In the old code, such a situation would silently overwrite the tempfile names, meaning we would forget to clean them up. The new code dies with a bug warning. - we make sure only to install the signal handler once. This isn't a big deal, since we are just overwriting the old handler, but will become an issue when a later patch converts the code to use sigchain Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 107 +++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 55 insertions(+), 52 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d23548292..3cfc0b636 100644 --- a/diff.c +++ b/diff.c @@ -165,6 +165,33 @@ static struct diff_tempfile { char tmp_path[PATH_MAX]; } diff_temp[2]; +static struct diff_tempfile *claim_diff_tempfile(void) { + int i; + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) + if (!diff_temp[i].name) + return diff_temp + i; + die("BUG: diff is failing to clean up its tempfiles"); +} + +static int remove_tempfile_installed; + +static void remove_tempfile(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) + if (diff_temp[i].name == diff_temp[i].tmp_path) { + unlink(diff_temp[i].name); + diff_temp[i].name = NULL; + } +} + +static void remove_tempfile_on_signal(int signo) +{ + remove_tempfile(); + signal(SIGINT, SIG_DFL); + raise(signo); +} + static int count_lines(const char *data, int size) { int count, ch, completely_empty = 1, nl_just_seen = 0; @@ -1857,10 +1884,11 @@ static void prep_temp_blob(struct diff_tempfile *temp, sprintf(temp->mode, "%06o", mode); } -static void prepare_temp_file(const char *name, - struct diff_tempfile *temp, - struct diff_filespec *one) +static struct diff_tempfile *prepare_temp_file(const char *name, + struct diff_filespec *one) { + struct diff_tempfile *temp = claim_diff_tempfile(); + if (!DIFF_FILE_VALID(one)) { not_a_valid_file: /* A '-' entry produces this for file-2, and @@ -1869,7 +1897,13 @@ static void prepare_temp_file(const char *name, temp->name = "/dev/null"; strcpy(temp->hex, "."); strcpy(temp->mode, "."); - return; + return temp; + } + + if (!remove_tempfile_installed) { + atexit(remove_tempfile); + signal(SIGINT, remove_tempfile_on_signal); + remove_tempfile_installed = 1; } if (!one->sha1_valid || @@ -1909,7 +1943,7 @@ static void prepare_temp_file(const char *name, */ sprintf(temp->mode, "%06o", one->mode); } - return; + return temp; } else { if (diff_populate_filespec(one, 0)) @@ -1917,24 +1951,7 @@ static void prepare_temp_file(const char *name, prep_temp_blob(temp, one->data, one->size, one->sha1, one->mode); } -} - -static void remove_tempfile(void) -{ - int i; - - for (i = 0; i < 2; i++) - if (diff_temp[i].name == diff_temp[i].tmp_path) { - unlink(diff_temp[i].name); - diff_temp[i].name = NULL; - } -} - -static void remove_tempfile_on_signal(int signo) -{ - remove_tempfile(); - signal(SIGINT, SIG_DFL); - raise(signo); + return temp; } /* An external diff command takes: @@ -1952,34 +1969,22 @@ static void run_external_diff(const char *pgm, int complete_rewrite) { const char *spawn_arg[10]; - struct diff_tempfile *temp = diff_temp; int retval; - static int atexit_asked = 0; - const char *othername; const char **arg = &spawn_arg[0]; - othername = (other? other : name); - if (one && two) { - prepare_temp_file(name, &temp[0], one); - prepare_temp_file(othername, &temp[1], two); - if (! atexit_asked && - (temp[0].name == temp[0].tmp_path || - temp[1].name == temp[1].tmp_path)) { - atexit_asked = 1; - atexit(remove_tempfile); - } - signal(SIGINT, remove_tempfile_on_signal); - } - if (one && two) { + struct diff_tempfile *temp_one, *temp_two; + const char *othername = (other ? other : name); + temp_one = prepare_temp_file(name, one); + temp_two = prepare_temp_file(othername, two); *arg++ = pgm; *arg++ = name; - *arg++ = temp[0].name; - *arg++ = temp[0].hex; - *arg++ = temp[0].mode; - *arg++ = temp[1].name; - *arg++ = temp[1].hex; - *arg++ = temp[1].mode; + *arg++ = temp_one->name; + *arg++ = temp_one->hex; + *arg++ = temp_one->mode; + *arg++ = temp_two->name; + *arg++ = temp_two->hex; + *arg++ = temp_two->mode; if (other) { *arg++ = other; *arg++ = xfrm_msg; @@ -3448,15 +3453,15 @@ void diff_unmerge(struct diff_options *options, static char *run_textconv(const char *pgm, struct diff_filespec *spec, size_t *outsize) { - struct diff_tempfile temp; + struct diff_tempfile *temp; const char *argv[3]; const char **arg = argv; struct child_process child; struct strbuf buf = STRBUF_INIT; - prepare_temp_file(spec->path, &temp, spec); + temp = prepare_temp_file(spec->path, spec); *arg++ = pgm; - *arg++ = temp.name; + *arg++ = temp->name; *arg = NULL; memset(&child, 0, sizeof(child)); @@ -3465,13 +3470,11 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, if (start_command(&child) != 0 || strbuf_read(&buf, child.out, 0) < 0 || finish_command(&child) != 0) { - if (temp.name == temp.tmp_path) - unlink(temp.name); + remove_tempfile(); error("error running textconv command '%s'", pgm); return NULL; } - if (temp.name == temp.tmp_path) - unlink(temp.name); + remove_tempfile(); return strbuf_detach(&buf, outsize); } -- cgit v1.2.1 From 4a16d072723b48699ea162da24eff05eba298834 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 22 Jan 2009 01:02:35 -0500 Subject: chain kill signals for cleanup functions If a piece of code wanted to do some cleanup before exiting (e.g., cleaning up a lockfile or a tempfile), our usual strategy was to install a signal handler that did something like this: do_cleanup(); /* actual work */ signal(signo, SIG_DFL); /* restore previous behavior */ raise(signo); /* deliver signal, killing ourselves */ For a single handler, this works fine. However, if we want to clean up two _different_ things, we run into a problem. The most recently installed handler will run, but when it removes itself as a handler, it doesn't put back the first handler. This patch introduces sigchain, a tiny library for handling a stack of signal handlers. You sigchain_push each handler, and use sigchain_pop to restore whoever was before you in the stack. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3cfc0b636..9c9977d89 100644 --- a/diff.c +++ b/diff.c @@ -12,6 +12,7 @@ #include "run-command.h" #include "utf8.h" #include "userdiff.h" +#include "sigchain.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -188,7 +189,7 @@ static void remove_tempfile(void) static void remove_tempfile_on_signal(int signo) { remove_tempfile(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -1902,7 +1903,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, if (!remove_tempfile_installed) { atexit(remove_tempfile); - signal(SIGINT, remove_tempfile_on_signal); + sigchain_push(SIGINT, remove_tempfile_on_signal); remove_tempfile_installed = 1; } -- cgit v1.2.1 From 57b235a4bc8884a57c6f863605a54b7bfceb0997 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 22 Jan 2009 01:03:08 -0500 Subject: refactor signal handling for cleanup functions The current code is very inconsistent about which signals are caught for doing cleanup of temporary files and lock files. Some callsites checked only SIGINT, while others checked a variety of death-dealing signals. This patch factors out those signals to a single function, and then calls it everywhere. For some sites, that means this is a simple clean up. For others, it is an improvement in that they will now properly clean themselves up after a larger variety of signals. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 9c9977d89..8ce898a6b 100644 --- a/diff.c +++ b/diff.c @@ -1903,7 +1903,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, if (!remove_tempfile_installed) { atexit(remove_tempfile); - sigchain_push(SIGINT, remove_tempfile_on_signal); + sigchain_push_common(remove_tempfile_on_signal); remove_tempfile_installed = 1; } -- cgit v1.2.1 From b67b9612e1a90ae093445abeaeff930e9f4cf936 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 Jan 2009 00:08:24 -0800 Subject: diff.c: output correct index lines for a split diff A patch that changes the filetype (e.g. regular file to symlink) of a path must be split into a deletion event followed by a creation event, which means that we need to have two independent metainfo lines for each. However, the code reused the single set of metainfo lines. As the blob object names recorded on the index lines are usually not used nor validated on the receiving end, this is not an issue with normal use of the resulting patch. However, when accepting a binary patch to delete a blob, git-apply verified that the postimage blob object name on the index line is 0{40}, hence a patch that deletes a regular file blob that records binary contents to create a blob with different filetype (e.g. a symbolic link) failed to apply. "git am -3" also uses the blob object names recorded on the index line, so it would also misbehave when synthesizing a preimage tree. This moves the code to generate metainfo lines around, so that two independent sets of metainfo lines are used for the split halves. Additional tests by Jeff King. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 146 ++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 80 insertions(+), 66 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f91f256c5..6fea3c034 100644 --- a/diff.c +++ b/diff.c @@ -2096,16 +2096,86 @@ static const char *external_diff_attr(const char *name) return NULL; } +static int similarity_index(struct diff_filepair *p) +{ + return p->score * 100 / MAX_SCORE; +} + +static void fill_metainfo(struct strbuf *msg, + const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + struct diff_options *o, + struct diff_filepair *p) +{ + strbuf_init(msg, PATH_MAX * 2 + 300); + switch (p->status) { + case DIFF_STATUS_COPIED: + strbuf_addf(msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(msg, "\ncopy from "); + quote_c_style(name, msg, NULL, 0); + strbuf_addstr(msg, "\ncopy to "); + quote_c_style(other, msg, NULL, 0); + strbuf_addch(msg, '\n'); + break; + case DIFF_STATUS_RENAMED: + strbuf_addf(msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(msg, "\nrename from "); + quote_c_style(name, msg, NULL, 0); + strbuf_addstr(msg, "\nrename to "); + quote_c_style(other, msg, NULL, 0); + strbuf_addch(msg, '\n'); + break; + case DIFF_STATUS_MODIFIED: + if (p->score) { + strbuf_addf(msg, "dissimilarity index %d%%\n", + similarity_index(p)); + break; + } + /* fallthru */ + default: + /* nothing */ + ; + } + if (one && two && hashcmp(one->sha1, two->sha1)) { + int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; + + if (DIFF_OPT_TST(o, BINARY)) { + mmfile_t mf; + if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || + (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) + abbrev = 40; + } + strbuf_addf(msg, "index %.*s..%.*s", + abbrev, sha1_to_hex(one->sha1), + abbrev, sha1_to_hex(two->sha1)); + if (one->mode == two->mode) + strbuf_addf(msg, " %06o", one->mode); + strbuf_addch(msg, '\n'); + } + if (msg->len) + strbuf_setlen(msg, msg->len - 1); +} + static void run_diff_cmd(const char *pgm, const char *name, const char *other, const char *attr_path, struct diff_filespec *one, struct diff_filespec *two, - const char *xfrm_msg, + struct strbuf *msg, struct diff_options *o, - int complete_rewrite) + struct diff_filepair *p) { + const char *xfrm_msg = NULL; + int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score; + + if (msg) { + fill_metainfo(msg, name, other, one, two, o, p); + xfrm_msg = msg->len ? msg->buf : NULL; + } + if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { @@ -2145,11 +2215,6 @@ static void diff_fill_sha1_info(struct diff_filespec *one) hashclr(one->sha1); } -static int similarity_index(struct diff_filepair *p) -{ - return p->score * 100 / MAX_SCORE; -} - static void strip_prefix(int prefix_length, const char **namep, const char **otherp) { /* Strip the prefix but do not molest /dev/null and absolute paths */ @@ -2163,13 +2228,11 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); struct strbuf msg; - char *xfrm_msg; struct diff_filespec *one = p->one; struct diff_filespec *two = p->two; const char *name; const char *other; const char *attr_path; - int complete_rewrite = 0; name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); @@ -2179,83 +2242,34 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (DIFF_PAIR_UNMERGED(p)) { run_diff_cmd(pgm, name, NULL, attr_path, - NULL, NULL, NULL, o, 0); + NULL, NULL, NULL, o, p); return; } diff_fill_sha1_info(one); diff_fill_sha1_info(two); - strbuf_init(&msg, PATH_MAX * 2 + 300); - switch (p->status) { - case DIFF_STATUS_COPIED: - strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); - strbuf_addstr(&msg, "\ncopy from "); - quote_c_style(name, &msg, NULL, 0); - strbuf_addstr(&msg, "\ncopy to "); - quote_c_style(other, &msg, NULL, 0); - strbuf_addch(&msg, '\n'); - break; - case DIFF_STATUS_RENAMED: - strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); - strbuf_addstr(&msg, "\nrename from "); - quote_c_style(name, &msg, NULL, 0); - strbuf_addstr(&msg, "\nrename to "); - quote_c_style(other, &msg, NULL, 0); - strbuf_addch(&msg, '\n'); - break; - case DIFF_STATUS_MODIFIED: - if (p->score) { - strbuf_addf(&msg, "dissimilarity index %d%%\n", - similarity_index(p)); - complete_rewrite = 1; - break; - } - /* fallthru */ - default: - /* nothing */ - ; - } - - if (hashcmp(one->sha1, two->sha1)) { - int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; - - if (DIFF_OPT_TST(o, BINARY)) { - mmfile_t mf; - if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || - (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) - abbrev = 40; - } - strbuf_addf(&msg, "index %.*s..%.*s", - abbrev, sha1_to_hex(one->sha1), - abbrev, sha1_to_hex(two->sha1)); - if (one->mode == two->mode) - strbuf_addf(&msg, " %06o", one->mode); - strbuf_addch(&msg, '\n'); - } - - if (msg.len) - strbuf_setlen(&msg, msg.len - 1); - xfrm_msg = msg.len ? msg.buf : NULL; - if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && (S_IFMT & one->mode) != (S_IFMT & two->mode)) { - /* a filepair that changes between file and symlink + /* + * a filepair that changes between file and symlink * needs to be split into deletion and creation. */ struct diff_filespec *null = alloc_filespec(two->path); run_diff_cmd(NULL, name, other, attr_path, - one, null, xfrm_msg, o, 0); + one, null, &msg, o, p); free(null); + strbuf_release(&msg); + null = alloc_filespec(one->path); run_diff_cmd(NULL, name, other, attr_path, - null, two, xfrm_msg, o, 0); + null, two, &msg, o, p); free(null); } else run_diff_cmd(pgm, name, other, attr_path, - one, two, xfrm_msg, o, complete_rewrite); + one, two, &msg, o, p); strbuf_release(&msg); } -- cgit v1.2.1 From a8344abe0f70f9fc629ee055d73ffa65fbc58311 Mon Sep 17 00:00:00 2001 From: Nazri Ramliy Date: Thu, 12 Feb 2009 21:36:14 +0800 Subject: Bugfix: GIT_EXTERNAL_DIFF with more than one changed files When there is more than one file that are changed, running git diff with GIT_EXTERNAL_DIFF incorrectly diagnoses an programming error and dies. The check introduced in 479b0ae (diff: refactor tempfile cleanup handling, 2009-01-22) to detect a temporary file slot that forgot to remove its temporary file was inconsistent with the way the codepath to remove the temporary to mark the slot that it is done with it. This patch fixes this problem and adds a test case for it. Signed-off-by: Nazri Ramliy Acked-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index a5a540fd3..be3859e0a 100644 --- a/diff.c +++ b/diff.c @@ -184,11 +184,11 @@ static int remove_tempfile_installed; static void remove_tempfile(void) { int i; - for (i = 0; i < ARRAY_SIZE(diff_temp); i++) - if (diff_temp[i].name == diff_temp[i].tmp_path) { + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) { + if (diff_temp[i].name == diff_temp[i].tmp_path) unlink(diff_temp[i].name); - diff_temp[i].name = NULL; - } + diff_temp[i].name = NULL; + } } static void remove_tempfile_on_signal(int signo) -- cgit v1.2.1 From dc6ebd4cc5028d59146e02e30f7945ee91974e6e Mon Sep 17 00:00:00 2001 From: Arjen Laarhoven Date: Fri, 13 Feb 2009 22:53:40 +0100 Subject: Clean up use of ANSI color sequences Remove the literal ANSI escape sequences and replace them by readable constants. Signed-off-by: Arjen Laarhoven Signed-off-by: Junio C Hamano --- diff.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index be3859e0a..a3db16ea6 100644 --- a/diff.c +++ b/diff.c @@ -30,14 +30,14 @@ int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static char diff_colors[][COLOR_MAXLEN] = { - "\033[m", /* reset */ - "", /* PLAIN (normal) */ - "\033[1m", /* METAINFO (bold) */ - "\033[36m", /* FRAGINFO (cyan) */ - "\033[31m", /* OLD (red) */ - "\033[32m", /* NEW (green) */ - "\033[33m", /* COMMIT (yellow) */ - "\033[41m", /* WHITESPACE (red background) */ + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_BOLD, /* METAINFO */ + GIT_COLOR_CYAN, /* FRAGINFO */ + GIT_COLOR_RED, /* OLD */ + GIT_COLOR_GREEN, /* NEW */ + GIT_COLOR_YELLOW, /* COMMIT */ + GIT_COLOR_BG_RED, /* WHITESPACE */ }; static void diff_filespec_load_driver(struct diff_filespec *one); -- cgit v1.2.1 From 4b15b4ab5f9b19caff6d4a910ecc3e1d4f0e13f0 Mon Sep 17 00:00:00 2001 From: Keith Cascio Date: Fri, 13 Feb 2009 09:33:34 -0800 Subject: Remove redundant bit clears from diff_setup() All bits already clear after memset(0). Signed-off-by: Junio C Hamano --- diff.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index be3859e0a..006aa017e 100644 --- a/diff.c +++ b/diff.c @@ -2326,15 +2326,12 @@ void diff_setup(struct diff_options *options) options->break_opt = -1; options->rename_limit = -1; options->dirstat_percent = 3; - DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE); options->context = 3; options->change = diff_change; options->add_remove = diff_addremove; if (diff_use_color_default > 0) DIFF_OPT_SET(options, COLOR_DIFF); - else - DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; if (!diff_mnemonic_prefix) { -- cgit v1.2.1 From cd673c1f17228d272c4b7f81fbb28bc31cf0cac6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 27 Feb 2009 23:15:53 -0800 Subject: has_sha1_pack(): refactor "pretend these packs do not exist" interface Most of the callers of this function except only one pass NULL to its last parameter, ignore_packed. Introduce has_sha1_kept_pack() function that has the function signature and the semantics of this function, and convert the sole caller that does not pass NULL to call this new function. All other callers and has_sha1_pack() lose the ignore_packed parameter. Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f91f256c5..a34d26c23 100644 --- a/diff.c +++ b/diff.c @@ -1743,7 +1743,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int * objects however would tend to be slower as they need * to be individually opened and inflated. */ - if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL)) + if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1)) return 0; len = strlen(name); -- cgit v1.2.1 From 628d5c2b707db207e47c42ca112b182aa171cfaa Mon Sep 17 00:00:00 2001 From: Keith Cascio Date: Mon, 16 Feb 2009 19:26:49 -0800 Subject: Use DIFF_XDL_SET/DIFF_OPT_SET instead of raw bit-masking Signed-off-by: Keith Cascio Signed-off-by: Junio C Hamano --- diff.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 006aa017e..ff3624e9f 100644 --- a/diff.c +++ b/diff.c @@ -2567,13 +2567,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) /* xdiff options */ else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE; + DIFF_XDL_SET(options, IGNORE_WHITESPACE); else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE); else if (!strcmp(arg, "--ignore-space-at-eol")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL); else if (!strcmp(arg, "--patience")) - options->xdl_opts |= XDF_PATIENCE_DIFF; + DIFF_XDL_SET(options, PATIENCE_DIFF); /* flags options */ else if (!strcmp(arg, "--binary")) { @@ -2594,10 +2594,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, COLOR_DIFF); else if (!strcmp(arg, "--no-color")) DIFF_OPT_CLR(options, COLOR_DIFF); - else if (!strcmp(arg, "--color-words")) - options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + else if (!strcmp(arg, "--color-words")) { + DIFF_OPT_SET(options, COLOR_DIFF); + DIFF_OPT_SET(options, COLOR_DIFF_WORDS); + } else if (!prefixcmp(arg, "--color-words=")) { - options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + DIFF_OPT_SET(options, COLOR_DIFF); + DIFF_OPT_SET(options, COLOR_DIFF_WORDS); options->word_regex = arg + 14; } else if (!strcmp(arg, "--exit-code")) -- cgit v1.2.1 From eb3a9dd3279fe4b05f286665986ebf6d43a6ccc0 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Sat, 7 Mar 2009 21:02:10 +0100 Subject: Remove unused function scope local variables These variables were unused and can be removed safely: builtin-clone.c::cmd_clone(): use_local_hardlinks, use_separate_remote builtin-fetch-pack.c::find_common(): len builtin-remote.c::mv(): symref diff.c::show_stats():show_stats(): total diffcore-break.c::should_break(): base_size fast-import.c::validate_raw_date(): date, sign fsck.c::fsck_tree(): o_sha1, sha1 xdiff-interface.c::parse_num(): read_some Signed-off-by: Benjamin Kramer Signed-off-by: Junio C Hamano --- diff.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3feca1b17..e06c93707 100644 --- a/diff.c +++ b/diff.c @@ -875,7 +875,7 @@ static void fill_print_name(struct diffstat_file *file) static void show_stats(struct diffstat_t* data, struct diff_options *options) { - int i, len, add, del, total, adds = 0, dels = 0; + int i, len, add, del, adds = 0, dels = 0; int max_change = 0, max_len = 0; int total_files = data->nr; int width, name_width; @@ -978,14 +978,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) */ add = added; del = deleted; - total = add + del; adds += add; dels += del; if (width <= max_change) { add = scale_linear(add, width, max_change); del = scale_linear(del, width, max_change); - total = add + del; } show_name(options->file, prefix, name, len, reset, set); fprintf(options->file, "%5d%s", added + deleted, -- cgit v1.2.1 From 4e218f54b3de6aa8ef7d15020c4507031a519f7d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 21 Mar 2009 12:42:52 +0100 Subject: Smudge the files fed to external diff and textconv When preparing temporary files for an external diff or textconv, it is easier on the external tools, especially when they are implemented using platform tools, if they are fed the input after convert_to_working_tree(). This fixes msysGit issue 177. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 006aa017e..11798af0c 100644 --- a/diff.c +++ b/diff.c @@ -1948,17 +1948,23 @@ void diff_free_filespec_data(struct diff_filespec *s) s->cnt_data = NULL; } -static void prep_temp_blob(struct diff_tempfile *temp, +static void prep_temp_blob(const char *path, struct diff_tempfile *temp, void *blob, unsigned long size, const unsigned char *sha1, int mode) { int fd; + struct strbuf buf = STRBUF_INIT; fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX"); if (fd < 0) die("unable to create temp-file: %s", strerror(errno)); + if (convert_to_working_tree(path, + (const char *)blob, (size_t)size, &buf)) { + blob = buf.buf; + size = buf.len; + } if (write_in_full(fd, blob, size) != size) die("unable to write temp-file"); close(fd); @@ -1966,6 +1972,7 @@ static void prep_temp_blob(struct diff_tempfile *temp, strcpy(temp->hex, sha1_to_hex(sha1)); temp->hex[40] = 0; sprintf(temp->mode, "%06o", mode); + strbuf_release(&buf); } static struct diff_tempfile *prepare_temp_file(const char *name, @@ -2006,7 +2013,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, die("readlink(%s)", name); if (ret == sizeof(buf)) die("symlink too long: %s", name); - prep_temp_blob(temp, buf, ret, + prep_temp_blob(name, temp, buf, ret, (one->sha1_valid ? one->sha1 : null_sha1), (one->sha1_valid ? @@ -2032,7 +2039,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, else { if (diff_populate_filespec(one, 0)) die("cannot read data blob for %s", one->path); - prep_temp_blob(temp, one->data, one->size, + prep_temp_blob(name, temp, one->data, one->size, one->sha1, one->mode); } return temp; -- cgit v1.2.1 From 150115aded2e1e0a83db7366f59e4b5bf1aa8135 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 22 Mar 2009 15:26:07 -0700 Subject: diff --cached: do not borrow from a work tree when a path is marked as assume-unchanged When the index says that the file in the work tree that corresponds to the blob object that is used for comparison is known to be unchanged, "diff" reads from the file and applies convert_to_git(), instead of inflating the object, to feed the internal diff engine with, because an earlier benchnark found that it tends to be faster to use this optimization. However, the index can lie when the path is marked as assume-unchanged. Disable the optimization for such paths. Signed-off-by: Junio C Hamano --- diff.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index bf5d5f15a..c2d277a52 100644 --- a/diff.c +++ b/diff.c @@ -1687,7 +1687,8 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int struct stat st; int pos, len; - /* We do not read the cache ourselves here, because the + /* + * We do not read the cache ourselves here, because the * benchmark with my previous version that always reads cache * shows that it makes things worse for diff-tree comparing * two linux-2.6 kernel trees in an already checked out work @@ -1727,6 +1728,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode)) return 0; + /* + * If ce is marked as "assume unchanged", there is no + * guarantee that work tree matches what we are looking for. + */ + if (ce->ce_flags & CE_VALID) + return 0; + /* * If ce matches the file in the work tree, we can reuse it. */ -- cgit v1.2.1 From cced5fbc241f1274ba532040b985f38c15bbf555 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 9 Apr 2009 11:46:15 -0700 Subject: Allow users to un-configure rename detection I told people on the kernel mailing list to please use "-M" when sending me rename patches, so that I can see what they do while reading email rather than having to apply the patch and then look at the end result. I also told them that if they want to make it the default, they can just add [diff] renames to their ~/.gitconfig file. And while I was thinking about that, I wanted to also check whether you can then mark individual projects to _not_ have that default in the per-repository .git/config file. And you can't. Currently you cannot have a global "enable renames by default" and then a local ".. but not for _this_ project". Why? Because if somebody writes [diff] renames = no we simply ignore it, rather than resetting "diff_detect_rename_default" back to zero. Fixed thusly. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index e0fa78c84..3ac71686e 100644 --- a/diff.c +++ b/diff.c @@ -62,6 +62,15 @@ static int parse_diff_color_slot(const char *var, int ofs) die("bad config variable '%s'", var); } +static int git_config_rename(const char *var, const char *value) +{ + if (!value) + return DIFF_DETECT_RENAME; + if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy")) + return DIFF_DETECT_COPY; + return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0; +} + /* * These are to give UI layer defaults. * The core-level commands such as git-diff-files should @@ -75,13 +84,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) return 0; } if (!strcmp(var, "diff.renames")) { - if (!value) - diff_detect_rename_default = DIFF_DETECT_RENAME; - else if (!strcasecmp(value, "copies") || - !strcasecmp(value, "copy")) - diff_detect_rename_default = DIFF_DETECT_COPY; - else if (git_config_bool(var,value)) - diff_detect_rename_default = DIFF_DETECT_RENAME; + diff_detect_rename_default = git_config_rename(var, value); return 0; } if (!strcmp(var, "diff.autorefreshindex")) { -- cgit v1.2.1 From a408e0e649b79ede7422bf837a31d281e4188cef Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sat, 25 Apr 2009 00:06:47 +0200 Subject: diff: do not color --stat output like patch context The diffstat used the color.diff.plain slot (context text) for coloring filenames and the whole summary line. This didn't look nice and the affected text isn't patch context at all. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- diff.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3ac71686e..d581d4d9f 100644 --- a/diff.c +++ b/diff.c @@ -839,10 +839,9 @@ static int scale_linear(int it, int width, int max_change) } static void show_name(FILE *file, - const char *prefix, const char *name, int len, - const char *reset, const char *set) + const char *prefix, const char *name, int len) { - fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset); + fprintf(file, " %s%-*s |", prefix, len, name); } static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset) @@ -956,7 +955,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } if (data->files[i]->is_binary) { - show_name(options->file, prefix, name, len, reset, set); + show_name(options->file, prefix, name, len); fprintf(options->file, " Bin "); fprintf(options->file, "%s%d%s", del_c, deleted, reset); fprintf(options->file, " -> "); @@ -966,7 +965,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) continue; } else if (data->files[i]->is_unmerged) { - show_name(options->file, prefix, name, len, reset, set); + show_name(options->file, prefix, name, len); fprintf(options->file, " Unmerged\n"); continue; } @@ -988,7 +987,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) add = scale_linear(add, width, max_change); del = scale_linear(del, width, max_change); } - show_name(options->file, prefix, name, len, reset, set); + show_name(options->file, prefix, name, len); fprintf(options->file, "%5d%s", added + deleted, added + deleted ? " " : ""); show_graph(options->file, '+', add, add_c, reset); @@ -996,8 +995,8 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) fprintf(options->file, "\n"); } fprintf(options->file, - "%s %d files changed, %d insertions(+), %d deletions(-)%s\n", - set, total_files, adds, dels, reset); + " %d files changed, %d insertions(+), %d deletions(-)\n", + total_files, adds, dels); } static void show_shortstats(struct diffstat_t* data, struct diff_options *options) -- cgit v1.2.1 From 691f1a28bf57618d8b44a193b1d28013c858aba6 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 29 Apr 2009 23:22:56 +0200 Subject: replace direct calls to unlink(2) with unlink_or_warn This helps to notice when something's going wrong, especially on systems which lock open files. I used the following criteria when selecting the code for replacement: - it was already printing a warning for the unlink failures - it is in a function which already printing something or is called from such a function - it is in a static function, returning void and the function is only called from a builtin main function (cmd_) - it is in a function which handles emergency exit (signal handlers) - it is in a function which is obvously cleaning up the lockfiles Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3ac71686e..6802f5ac1 100644 --- a/diff.c +++ b/diff.c @@ -189,7 +189,7 @@ static void remove_tempfile(void) int i; for (i = 0; i < ARRAY_SIZE(diff_temp); i++) { if (diff_temp[i].name == diff_temp[i].tmp_path) - unlink(diff_temp[i].name); + unlink_or_warn(diff_temp[i].name); diff_temp[i].name = NULL; } } -- cgit v1.2.1 From 4b25d091ba53c758fae0096b8c0662371857b9d9 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 1 May 2009 12:06:36 +0300 Subject: Fix a bunch of pointer declarations (codestyle) Essentially; s/type* /type */ as per the coding guidelines. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 3ac71686e..363dcb961 100644 --- a/diff.c +++ b/diff.c @@ -876,7 +876,7 @@ static void fill_print_name(struct diffstat_file *file) file->print_name = pname; } -static void show_stats(struct diffstat_t* data, struct diff_options *options) +static void show_stats(struct diffstat_t *data, struct diff_options *options) { int i, len, add, del, adds = 0, dels = 0; int max_change = 0, max_len = 0; @@ -1025,7 +1025,7 @@ static void show_shortstats(struct diffstat_t* data, struct diff_options *option total_files, adds, dels); } -static void show_numstat(struct diffstat_t* data, struct diff_options *options) +static void show_numstat(struct diffstat_t *data, struct diff_options *options) { int i; -- cgit v1.2.1 From 3cd7388d57db4f4a29949e8de96493fb77059484 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 25 May 2009 06:46:09 -0400 Subject: convert bare readlink to strbuf_readlink This particular readlink call never NUL-terminated its result, making it a potential source of bugs (though there is no bug now, as it currently always respects the length field). Let's just switch it to strbuf_readlink which is shorter and less error-prone. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index f06876be6..dcfbcb021 100644 --- a/diff.c +++ b/diff.c @@ -2014,18 +2014,15 @@ static struct diff_tempfile *prepare_temp_file(const char *name, die("stat(%s): %s", name, strerror(errno)); } if (S_ISLNK(st.st_mode)) { - int ret; - char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */ - ret = readlink(name, buf, sizeof(buf)); - if (ret < 0) + struct strbuf sb = STRBUF_INIT; + if (strbuf_readlink(&sb, name, st.st_size) < 0) die("readlink(%s)", name); - if (ret == sizeof(buf)) - die("symlink too long: %s", name); - prep_temp_blob(name, temp, buf, ret, + prep_temp_blob(name, temp, sb.buf, sb.len, (one->sha1_valid ? one->sha1 : null_sha1), (one->sha1_valid ? one->mode : S_IFLNK)); + strbuf_release(&sb); } else { /* we can borrow from the file in the work tree */ -- cgit v1.2.1 From 003b33a8ad686ee4a0d0b36635bfd6aba940b24a Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 31 May 2009 01:35:52 -0700 Subject: diff: generate pretty filenames in prep_temp_blob() Naturally, prep_temp_blob() did not care about filenames. As a result, GIT_EXTERNAL_DIFF and textconv generated filenames such as ".diff_XXXXXX". This modifies prep_temp_blob() to generate user-friendly filenames when creating temporary files. Diffing "name.ext" now generates "XXXXXX_name.ext". Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- diff.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index dcfbcb021..4d0a5b9ae 100644 --- a/diff.c +++ b/diff.c @@ -1964,8 +1964,16 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, { int fd; struct strbuf buf = STRBUF_INIT; + struct strbuf template = STRBUF_INIT; + char *path_dup = xstrdup(path); + const char *base = basename(path_dup); - fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX"); + /* Generate "XXXXXX_basename.ext" */ + strbuf_addstr(&template, "XXXXXX_"); + strbuf_addstr(&template, base); + + fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf, + strlen(base) + 1); if (fd < 0) die("unable to create temp-file: %s", strerror(errno)); if (convert_to_working_tree(path, @@ -1981,6 +1989,8 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, temp->hex[40] = 0; sprintf(temp->mode, "%06o", mode); strbuf_release(&buf); + strbuf_release(&template); + free(path_dup); } static struct diff_tempfile *prepare_temp_file(const char *name, -- cgit v1.2.1 From 802f9c9cb21321d3ffe7576e01bbe31c51bd4c70 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 8 Jun 2009 22:34:30 +0200 Subject: diff.c: plug a memory leak in an error path Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index d24bff1e4..f0b580c15 100644 --- a/diff.c +++ b/diff.c @@ -3590,6 +3590,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, if (start_command(&child) != 0 || strbuf_read(&buf, child.out, 0) < 0 || finish_command(&child) != 0) { + strbuf_release(&buf); remove_tempfile(); error("error running textconv command '%s'", pgm); return NULL; -- cgit v1.2.1 From d824cbba02a4061400a0e382f9bd241fbbff34f0 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 27 Jun 2009 17:58:46 +0200 Subject: Convert existing die(..., strerror(errno)) to die_errno() Change calls to die(..., strerror(errno)) to use the new die_errno(). In the process, also make slight style adjustments: at least state _something_ about the function that failed (instead of just printing the pathname), and put paths in single quotes. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 4d0a5b9ae..48043f5bd 100644 --- a/diff.c +++ b/diff.c @@ -1975,7 +1975,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf, strlen(base) + 1); if (fd < 0) - die("unable to create temp-file: %s", strerror(errno)); + die_errno("unable to create temp-file"); if (convert_to_working_tree(path, (const char *)blob, (size_t)size, &buf)) { blob = buf.buf; @@ -2021,7 +2021,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, if (lstat(name, &st) < 0) { if (errno == ENOENT) goto not_a_valid_file; - die("stat(%s): %s", name, strerror(errno)); + die_errno("stat(%s)", name); } if (S_ISLNK(st.st_mode)) { struct strbuf sb = STRBUF_INIT; -- cgit v1.2.1 From 0721c314a5c8fddc877140ab5a333c42c62f780d Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 27 Jun 2009 17:58:47 +0200 Subject: Use die_errno() instead of die() when checking syscalls Lots of die() calls did not actually report the kind of error, which can leave the user confused as to the real problem. Use die_errno() where we check a system/library call that sets errno on failure, or one of the following that wrap such calls: Function Passes on error from -------- -------------------- odb_pack_keep open read_ancestry fopen read_in_full xread strbuf_read xread strbuf_read_file open or strbuf_read_file strbuf_readlink readlink write_in_full xwrite Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 48043f5bd..aec613f85 100644 --- a/diff.c +++ b/diff.c @@ -1982,7 +1982,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, size = buf.len; } if (write_in_full(fd, blob, size) != size) - die("unable to write temp-file"); + die_errno("unable to write temp-file"); close(fd); temp->name = temp->tmp_path; strcpy(temp->hex, sha1_to_hex(sha1)); @@ -2026,7 +2026,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, if (S_ISLNK(st.st_mode)) { struct strbuf sb = STRBUF_INIT; if (strbuf_readlink(&sb, name, st.st_size) < 0) - die("readlink(%s)", name); + die_errno("readlink(%s)", name); prep_temp_blob(name, temp, sb.buf, sb.len, (one->sha1_valid ? one->sha1 : null_sha1), @@ -2219,7 +2219,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one) return; } if (lstat(one->path, &st) < 0) - die("stat %s", one->path); + die_errno("stat '%s'", one->path); if (index_path(one->sha1, one->path, &st, 0)) die("cannot hash %s", one->path); } -- cgit v1.2.1 From 8cfe5f1cd5dabc3a21bc792327747deefeff6dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Thu, 2 Jul 2009 00:01:43 +0200 Subject: userdiff: add xdiff_clear_find_func() xdiff_set_find_func() is used to set user defined regular expressions for finding function signatures. Add xdiff_clear_find_func(), which frees the memory allocated by the former, making the API complete. Also, use the new function in diff.c (the only call site of xdiff_set_find_func()) to clean up after ourselves. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- diff.c | 1 + 1 file changed, 1 insertion(+) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 43835d756..892921cdc 100644 --- a/diff.c +++ b/diff.c @@ -1603,6 +1603,7 @@ static void builtin_diff(const char *name_a, free(mf1.ptr); if (textconv_two) free(mf2.ptr); + xdiff_clear_find_func(&xecfg); } free_ab_and_return: -- cgit v1.2.1