diff --git a/git.git-authors b/git.git-authors index 7d4c95bb3..7c72c4bf6 100644 --- a/git.git-authors +++ b/git.git-authors @@ -39,27 +39,34 @@ # but has otherwise not contributed to git.) # ok Adam Simpkins (http transport) +ok Adrian Johnson +ok Alexey Shumkin ok Andreas Ericsson ok Boyd Lynn Gerber +ok Brandon Casey ok Brian Downing ok Brian Gernhardt ok Christian Couder ok Daniel Barkalow ok Florian Forster +ok Gustaf Hendeby ok Holger Weiss ok Jeff King ok Johannes Schindelin ok Johannes Sixt +ask Jonathan Nieder ok Junio C Hamano ok Kristian Høgsberg ok Linus Torvalds ok Lukas Sandström ok Matthieu Moy +ok Michael Haggerty ign Mike McCormack (imap-send) ok Nicolas Pitre ok Paolo Bonzini ok Paul Kocher ok Peter Hagervall +ok Petr Onderka ok Pierre Habouzit ok Pieter de Bie ok René Scharfe @@ -68,4 +75,5 @@ ok Sebastian Schuberth ok Shawn O. Pearce ok Steffen Prohaska ok Sven Verdoolaege +ask Thomas Rast (ok before 6-Oct-2013) ok Torsten Bögershausen diff --git a/src/diff_driver.c b/src/diff_driver.c index 167c0cc5a..4c9a0af65 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -26,10 +26,13 @@ typedef enum { DIFF_DRIVER_PATTERNLIST = 3, } git_diff_driver_t; +typedef struct { + regex_t re; + int flags; +} git_diff_driver_pattern; + enum { - DIFF_CONTEXT_FIND_NORMAL = 0, - DIFF_CONTEXT_FIND_ICASE = (1 << 0), - DIFF_CONTEXT_FIND_EXT = (1 << 1), + REG_NEGATE = (1 << 15) /* get out of the way of existing flags */ }; /* data for finding function context for a given file type */ @@ -37,11 +40,13 @@ struct git_diff_driver { git_diff_driver_t type; uint32_t binary_flags; uint32_t other_flags; - git_array_t(regex_t) fn_patterns; + git_array_t(git_diff_driver_pattern) fn_patterns; regex_t word_pattern; char name[GIT_FLEX_ARRAY]; }; +#include "userdiff.h" + struct git_diff_driver_registry { git_strmap *drivers; }; @@ -81,34 +86,59 @@ void git_diff_driver_registry_free(git_diff_driver_registry *reg) git__free(reg); } -static int diff_driver_add_funcname( - git_diff_driver *drv, const char *name, int regex_flags) +static int diff_driver_add_patterns( + git_diff_driver *drv, const char *regex_str, int regex_flags) { - int error; - regex_t re, *re_ptr; + int error = 0; + const char *scan, *end; + git_diff_driver_pattern *pat = NULL; + git_buf buf = GIT_BUF_INIT; - if ((error = regcomp(&re, name, regex_flags)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&re, error); - regfree(&re); - return error; + for (scan = regex_str; scan; scan = end) { + /* get pattern to fill in */ + if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) { + error = -1; + break; + } + + pat->flags = regex_flags; + if (*scan == '!') { + pat->flags |= REG_NEGATE; + ++scan; + } + + if ((end = strchr(scan, '\n')) != NULL) { + error = git_buf_set(&buf, scan, end - scan); + end++; + } else { + error = git_buf_sets(&buf, scan); + } + if (error < 0) + break; + + if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) < 0) { + /* if regex fails to compile, warn? fail? */ + error = giterr_set_regex(&pat->re, error); + regfree(&pat->re); + break; + } } - re_ptr = git_array_alloc(drv->fn_patterns); - GITERR_CHECK_ALLOC(re_ptr); + if (error && pat != NULL) + (void)git_array_pop(drv->fn_patterns); /* release last item */ + git_buf_free(&buf); - memcpy(re_ptr, &re, sizeof(re)); - return 0; + return error; } static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) { - return diff_driver_add_funcname(payload, entry->value, REG_EXTENDED); + return diff_driver_add_patterns(payload, entry->value, REG_EXTENDED); } static int diff_driver_funcname(const git_config_entry *entry, void *payload) { - return diff_driver_add_funcname(payload, entry->value, 0); + return diff_driver_add_patterns(payload, entry->value, 0); } static git_diff_driver_registry *git_repository_driver_registry( @@ -128,12 +158,65 @@ static git_diff_driver_registry *git_repository_driver_registry( return repo->diff_drivers; } +static int git_diff_driver_builtin( + git_diff_driver **out, + git_diff_driver_registry *reg, + const char *driver_name) +{ + int error = 0; + git_diff_driver_definition *ddef = NULL; + git_diff_driver *drv = NULL; + size_t namelen, idx; + + for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) { + if (!strcasecmp(driver_name, builtin_defs[idx].name)) { + ddef = &builtin_defs[idx]; + break; + } + } + if (!ddef) + goto done; + + namelen = strlen(ddef->name); + + drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); + GITERR_CHECK_ALLOC(drv); + + drv->type = DIFF_DRIVER_PATTERNLIST; + memcpy(drv->name, ddef->name, namelen); + + if (ddef->fns && + (error = diff_driver_add_patterns( + drv, ddef->fns, ddef->flags | REG_EXTENDED)) < 0) + goto done; + + if (ddef->words && + (error = regcomp( + &drv->word_pattern, ddef->words, ddef->flags | REG_EXTENDED))) + { + error = giterr_set_regex(&drv->word_pattern, error); + goto done; + } + + git_strmap_insert(reg->drivers, drv->name, drv, error); + if (error > 0) + error = 0; + +done: + if (error && drv) + git_diff_driver_free(drv); + else + *out = drv; + + return error; +} + static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { int error = 0; git_diff_driver_registry *reg; - git_diff_driver *drv; + git_diff_driver *drv = NULL; size_t namelen = strlen(driver_name); khiter_t pos; git_config *cfg; @@ -141,21 +224,19 @@ static int git_diff_driver_load( const git_config_entry *ce; bool found_driver = false; - reg = git_repository_driver_registry(repo); - if (!reg) + if ((reg = git_repository_driver_registry(repo)) == NULL) return -1; - else { - pos = git_strmap_lookup_index(reg->drivers, driver_name); - if (git_strmap_valid_index(reg->drivers, pos)) { - *out = git_strmap_value_at(reg->drivers, pos); - return 0; - } + + pos = git_strmap_lookup_index(reg->drivers, driver_name); + if (git_strmap_valid_index(reg->drivers, pos)) { + *out = git_strmap_value_at(reg->drivers, pos); + return 0; } /* if you can't read config for repo, just use default driver */ if (git_repository_config__weakptr(&cfg, repo) < 0) { giterr_clear(); - return GIT_ENOTFOUND; + goto done; } drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); @@ -178,7 +259,7 @@ static int git_diff_driver_load( found_driver = true; break; default: - /* diff..binary unspecified, so just continue */ + /* diff..binary unspecified or "auto", so just continue */ break; } @@ -234,14 +315,18 @@ static int git_diff_driver_load( git_strmap_insert(reg->drivers, drv->name, drv, error); if (error < 0) goto done; + error = 0; *out = drv; done: git_buf_free(&name); - if (!*out) - *out = &global_drivers[DIFF_DRIVER_AUTO]; + if (!*out) { + int error2 = git_diff_driver_builtin(out, reg, driver_name); + if (!error) + error = error2; + } if (drv && drv != *out) git_diff_driver_free(drv); @@ -256,14 +341,13 @@ int git_diff_driver_lookup( const char *value; assert(out); + *out = NULL; if (!repo || !path || !strlen(path)) - goto use_auto; - - if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) - return error; - - if (GIT_ATTR_UNSPECIFIED(value)) + /* just use the auto value */; + else if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) + /* return error below */; + else if (GIT_ATTR_UNSPECIFIED(value)) /* just use the auto value */; else if (GIT_ATTR_FALSE(value)) *out = &global_drivers[DIFF_DRIVER_BINARY]; @@ -272,17 +356,16 @@ int git_diff_driver_lookup( /* otherwise look for driver information in config and build driver */ else if ((error = git_diff_driver_load(out, repo, value)) < 0) { - if (error != GIT_ENOTFOUND) - return error; - else + if (error == GIT_ENOTFOUND) { + error = 0; giterr_clear(); + } } -use_auto: if (!*out) *out = &global_drivers[DIFF_DRIVER_AUTO]; - return 0; + return error; } void git_diff_driver_free(git_diff_driver *driver) @@ -293,7 +376,7 @@ void git_diff_driver_free(git_diff_driver *driver) return; for (i = 0; i < git_array_size(driver->fn_patterns); ++i) - regfree(git_array_get(driver->fn_patterns, i)); + regfree(& git_array_get(driver->fn_patterns, i)->re); git_array_clear(driver->fn_patterns); regfree(&driver->word_pattern); @@ -330,23 +413,34 @@ int git_diff_driver_content_is_binary( } static int diff_context_line__simple( - git_diff_driver *driver, const char *line, size_t line_len) + git_diff_driver *driver, git_buf *line) { + char firstch = line->ptr[0]; GIT_UNUSED(driver); - GIT_UNUSED(line_len); - return (git__isalpha(*line) || *line == '_' || *line == '$'); + return (git__isalpha(firstch) || firstch == '_' || firstch == '$'); } static int diff_context_line__pattern_match( - git_diff_driver *driver, const char *line, size_t line_len) + git_diff_driver *driver, git_buf *line) { - size_t i; + size_t i, maxi = git_array_size(driver->fn_patterns); + regmatch_t pmatch[2]; - GIT_UNUSED(line_len); + for (i = 0; i < maxi; ++i) { + git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i); + + if (!regexec(&pat->re, line->ptr, 2, pmatch, 0)) { + if (pat->flags & REG_NEGATE) + return false; + + /* use pmatch data to trim line data */ + i = (pmatch[1].rm_so >= 0) ? 1 : 0; + git_buf_consume(line, git_buf_cstr(line) + pmatch[i].rm_so); + git_buf_truncate(line, pmatch[i].rm_eo - pmatch[i].rm_so); + git_buf_rtrim(line); - for (i = 0; i < git_array_size(driver->fn_patterns); ++i) { - if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0)) return true; + } } return false; @@ -368,8 +462,7 @@ static long diff_context_find( if (!ctxt->line.size) return -1; - if (!ctxt->match_line || - !ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size)) + if (!ctxt->match_line || !ctxt->match_line(ctxt->driver, &ctxt->line)) return -1; if (out_size > (long)ctxt->line.size) diff --git a/src/diff_driver.h b/src/diff_driver.h index 9d3f18660..0706dcfc5 100644 --- a/src/diff_driver.h +++ b/src/diff_driver.h @@ -31,7 +31,7 @@ typedef long (*git_diff_find_context_fn)( const char *, long, char *, long, void *); typedef int (*git_diff_find_context_line)( - git_diff_driver *, const char *, size_t); + git_diff_driver *, git_buf *); typedef struct { git_diff_driver *driver; diff --git a/src/userdiff.h b/src/userdiff.h new file mode 100644 index 000000000..9318b5476 --- /dev/null +++ b/src/userdiff.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_userdiff_h__ +#define INCLUDE_userdiff_h__ + +/* + * This file isolates the built in diff driver function name patterns. + * Most of these patterns are taken from Git (with permission from the + * original authors for relicensing to libgit2). + */ + +typedef struct { + const char *name; + const char *fns; + const char *words; + int flags; +} git_diff_driver_definition; + +#define WORD_DEFAULT "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" + +/* + * These builtin driver definition macros have same signature as in core + * git userdiff.c so that the data can be extracted verbatim + */ +#define PATTERNS(NAME, FN_PATS, WORD_PAT) \ + { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, 0 } +#define IPATTERN(NAME, FN_PATS, WORD_PAT) \ + { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, REG_ICASE } + +/* + * The table of diff driver patterns + * + * Function name patterns are a list of newline separated patterns that + * match a function declaration (i.e. the line you want in the hunk header), + * or a negative pattern prefixed with a '!' to reject a pattern (such as + * rejecting goto labels in C code). + * + * Word boundary patterns are just a simple pattern that will be OR'ed with + * the default value above (i.e. whitespace or non-ASCII characters). + */ +static git_diff_driver_definition builtin_defs[] = { + +IPATTERN("ada", + "!^(.*[ \t])?(is new|renames|is separate)([ \t].*)?$\n" + "!^[ \t]*with[ \t].*$\n" + "^[ \t]*((procedure|function)[ \t]+.*)$\n" + "^[ \t]*((package|protected|task)[ \t]+.*)$", + /* -- */ + "[a-zA-Z][a-zA-Z0-9_]*" + "|[0-9][-+0-9#_.eE]" + "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"), + +IPATTERN("fortran", + "!^([C*]|[ \t]*!)\n" + "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n" + "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA" + "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$", + /* -- */ + "[a-zA-Z][a-zA-Z0-9_]*" + "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\." + /* numbers and format statements like 2E14.4, or ES12.6, 9X. + * Don't worry about format statements without leading digits since + * they would have been matched above as a variable anyway. */ + "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?" + "|//|\\*\\*|::|[/<>=]="), + +PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", + "[^<>= \t]+"), + +PATTERNS("java", + "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n" + "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=" + "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"), + +PATTERNS("matlab", + "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$", + "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"), + +PATTERNS("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]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n" + /* Objective-C class/protocol definitions */ + "^(@(implementation|interface|protocol)[ \t].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("pascal", + "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|" + "implementation|initialization|finalization)[ \t]*.*)$" + "\n" + "^(.*=[ \t]*(class|record).*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+" + "|<>|<=|>=|:=|\\.\\."), + +PATTERNS("perl", + "^package .*\n" + "^sub [[:alnum:]_':]+[ \t]*" + "(\\([^)]*\\)[ \t]*)?" /* prototype */ + /* + * Attributes. A regex can't count nested parentheses, + * so just slurp up whatever we see, taking care not + * to accept lines like "sub foo; # defined elsewhere". + * + * An attribute could contain a semicolon, but at that + * point it seems reasonable enough to give up. + */ + "(:[^;#]*)?" + "(\\{[ \t]*)?" /* brace can come here or on the next line */ + "(#.*)?$\n" /* comment */ + "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*" + "(\\{[ \t]*)?" /* brace can come here or on the next line */ + "(#.*)?$\n" + "^=head[0-9] .*", /* POD */ + /* -- */ + "[[:alpha:]_'][[:alnum:]_']*" + "|0[xb]?[0-9a-fA-F_]*" + /* taking care not to interpret 3..5 as (3.)(.5) */ + "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?" + "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::" + "|&&=|\\|\\|=|//=|\\*\\*=" + "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?" + "|[-+*/%.^&<>=!|]=" + "|=~|!~" + "|<<|<>|<=>|>>"), + +PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"), + +PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$", + /* -- */ + "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?." + "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"), + +PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + "[={}\"]|[^={}\" \t]+"), + +PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", + "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"), + +PATTERNS("cpp", + /* Jump targets or access declarations */ + "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n" + /* C/++ functions/methods at top level */ + "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n" + /* compound type at top level */ + "^((struct|class|enum)[^;]*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("csharp", + /* Keywords */ + "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n" + /* Methods and constructors */ + "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n" + /* Properties */ + "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n" + /* Type definitions */ + "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n" + /* Namespace */ + "^[ \t]*(namespace[ \t]+.*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("php", + "^[ \t]*(((public|private|protected|static|final)[ \t]+)*((class|function)[ \t].*))$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("javascript", + "^[ \t]*(function[ \t][a-zA-Z_][^\{]*)\n" + "^[ \t]*(var[ \t]+[a-zA-Z_][a-zA-Z0-9_]*[ \t]*=[ \t]*function[ \t\(][^\{]*)\n" + "^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*[ \t]*:[ \t]*function[ \t\(][^\{]*)", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), +}; + +#undef IPATTERN +#undef PATTERNS +#undef WORD_DEFAULT + +#endif + diff --git a/tests/diff/blob.c b/tests/diff/blob.c index 17f64c915..74df3cf85 100644 --- a/tests/diff/blob.c +++ b/tests/diff/blob.c @@ -861,15 +861,15 @@ void test_diff_blob__using_path_and_attributes(void) cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1)); cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0)); cl_git_pass(git_config_set_string( - cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]")); + cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z].*$")); cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0)); cl_git_pass(git_config_set_string( - cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]")); + cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z].*$")); cl_git_pass(git_config_set_string( - cfg, "diff.iam_numctx.funcname", "^[0-9]")); + cfg, "diff.iam_numctx.funcname", "^[0-9][0-9]*")); cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0)); cl_git_pass(git_config_set_string( - cfg, "diff.iam_textnum.funcname", "^[0-9]")); + cfg, "diff.iam_textnum.funcname", "^[0-9][0-9]*")); git_config_free(cfg); cl_git_append2file( diff --git a/tests/diff/drivers.c b/tests/diff/drivers.c index c80fad4c2..8b12368ea 100644 --- a/tests/diff/drivers.c +++ b/tests/diff/drivers.c @@ -15,6 +15,23 @@ void test_diff_drivers__cleanup(void) g_repo = NULL; } +static void overwrite_filemode(const char *expected, git_buf *actual) +{ + size_t offset; + char *found; + + found = strstr(expected, "100644"); + if (!found) + return; + + offset = ((const char *)found) - expected; + if (actual->size < offset + 6) + return; + + if (memcmp(&actual->ptr[offset], "100644", 6) != 0) + memcpy(&actual->ptr[offset], "100644", 6); +} + void test_diff_drivers__patterns(void) { git_config *cfg; @@ -22,7 +39,7 @@ void test_diff_drivers__patterns(void) git_tree *one; git_diff *diff; git_patch *patch; - git_buf buf = GIT_BUF_INIT; + git_buf actual = GIT_BUF_INIT; const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n"; const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; @@ -45,10 +62,10 @@ void test_diff_drivers__patterns(void) cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected0, buf.ptr); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected0, actual.ptr); - git_buf_free(&buf); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -60,10 +77,10 @@ void test_diff_drivers__patterns(void) cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected1, buf.ptr); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected1, actual.ptr); - git_buf_free(&buf); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -75,10 +92,10 @@ void test_diff_drivers__patterns(void) cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected0, buf.ptr); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected0, actual.ptr); - git_buf_free(&buf); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -92,10 +109,10 @@ void test_diff_drivers__patterns(void) cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected1, buf.ptr); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected1, actual.ptr); - git_buf_free(&buf); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -106,17 +123,17 @@ void test_diff_drivers__patterns(void) cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0)); - cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H")); + cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H.*$")); git_config_free(cfg); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected2, buf.ptr); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected2, actual.ptr); - git_buf_free(&buf); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -129,7 +146,7 @@ void test_diff_drivers__long_lines(void) git_index *idx; git_diff *diff; git_patch *patch; - git_buf buf = GIT_BUF_INIT; + git_buf actual = GIT_BUF_INIT; const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n"; g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -145,18 +162,91 @@ void test_diff_drivers__long_lines(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL)); cl_assert_equal_sz(1, git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_git_pass(git_patch_to_buf(&actual, patch)); /* if chmod not supported, overwrite mode bits since anything is possible */ - if (!cl_is_chmod_supported()) { - if (buf.size > 72 && memcmp(&buf.ptr[66], "100644", 6) != 0) - memcpy(&buf.ptr[66], "100644", 6); - } + overwrite_filemode(expected, &actual); - cl_assert_equal_s(expected, buf.ptr); + cl_assert_equal_s(expected, actual.ptr); - git_buf_free(&buf); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); } +void test_diff_drivers__builtins(void) +{ + git_diff *diff; + git_patch *patch; + git_buf file = GIT_BUF_INIT, actual = GIT_BUF_INIT, expected = GIT_BUF_INIT; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_vector files = GIT_VECTOR_INIT; + size_t i; + char *path, *extension; + + g_repo = cl_git_sandbox_init("userdiff"); + + cl_git_pass(git_path_dirload("userdiff/files", 9, 0, 0, &files)); + + opts.interhunk_lines = 1; + opts.context_lines = 1; + opts.pathspec.count = 1; + + git_vector_foreach(&files, i, path) { + if (git__prefixcmp(path, "files/file.")) + continue; + extension = path + strlen("files/file."); + opts.pathspec.strings = &path; + + /* do diff with no special driver */ + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_assert_equal_sz(1, git_diff_num_deltas(diff)); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_to_buf(&actual, patch)); + + git_buf_sets(&expected, "userdiff/expected/nodriver/diff."); + git_buf_puts(&expected, extension); + cl_git_pass(git_futils_readbuffer(&expected, expected.ptr)); + + overwrite_filemode(expected.ptr, &actual); + + cl_assert_equal_s(expected.ptr, actual.ptr); + + git_buf_clear(&actual); + git_patch_free(patch); + git_diff_free(diff); + + /* do diff with driver */ + + { + FILE *fp = fopen("userdiff/.gitattributes", "w"); + fprintf(fp, "*.%s diff=%s\n", extension, extension); + fclose(fp); + } + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_assert_equal_sz(1, git_diff_num_deltas(diff)); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_to_buf(&actual, patch)); + + git_buf_sets(&expected, "userdiff/expected/driver/diff."); + git_buf_puts(&expected, extension); + cl_git_pass(git_futils_readbuffer(&expected, expected.ptr)); + + overwrite_filemode(expected.ptr, &actual); + + cl_assert_equal_s(expected.ptr, actual.ptr); + + git_buf_clear(&actual); + git_patch_free(patch); + git_diff_free(diff); + + git__free(path); + } + + git_buf_free(&file); + git_buf_free(&actual); + git_buf_free(&expected); + git_vector_free(&files); +} diff --git a/tests/resources/userdiff/.gitted/HEAD b/tests/resources/userdiff/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 Binary files /dev/null and b/tests/resources/userdiff/.gitted/HEAD differ diff --git a/tests/resources/userdiff/.gitted/config b/tests/resources/userdiff/.gitted/config new file mode 100644 index 000000000..6c9406b7d Binary files /dev/null and b/tests/resources/userdiff/.gitted/config differ diff --git a/tests/resources/userdiff/.gitted/description b/tests/resources/userdiff/.gitted/description new file mode 100644 index 000000000..498b267a8 Binary files /dev/null and b/tests/resources/userdiff/.gitted/description differ diff --git a/tests/resources/userdiff/.gitted/index b/tests/resources/userdiff/.gitted/index new file mode 100644 index 000000000..b69d5cfb1 Binary files /dev/null and b/tests/resources/userdiff/.gitted/index differ diff --git a/tests/resources/userdiff/.gitted/info/refs b/tests/resources/userdiff/.gitted/info/refs new file mode 100644 index 000000000..b0743141d Binary files /dev/null and b/tests/resources/userdiff/.gitted/info/refs differ diff --git a/tests/resources/userdiff/.gitted/objects/info/packs b/tests/resources/userdiff/.gitted/objects/info/packs new file mode 100644 index 000000000..0c5fc2a30 Binary files /dev/null and b/tests/resources/userdiff/.gitted/objects/info/packs differ diff --git a/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx new file mode 100644 index 000000000..6f4381cc7 Binary files /dev/null and b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx differ diff --git a/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack new file mode 100644 index 000000000..39bd1d8f0 Binary files /dev/null and b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack differ diff --git a/tests/resources/userdiff/.gitted/packed-refs b/tests/resources/userdiff/.gitted/packed-refs new file mode 100644 index 000000000..802f67ce0 Binary files /dev/null and b/tests/resources/userdiff/.gitted/packed-refs differ diff --git a/tests/resources/userdiff/.gitted/refs/dummy-marker.txt b/tests/resources/userdiff/.gitted/refs/dummy-marker.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/userdiff/after/file.html b/tests/resources/userdiff/after/file.html new file mode 100644 index 000000000..2320e2f1e Binary files /dev/null and b/tests/resources/userdiff/after/file.html differ diff --git a/tests/resources/userdiff/after/file.javascript b/tests/resources/userdiff/after/file.javascript new file mode 100644 index 000000000..7cd3c5a8a Binary files /dev/null and b/tests/resources/userdiff/after/file.javascript differ diff --git a/tests/resources/userdiff/after/file.php b/tests/resources/userdiff/after/file.php new file mode 100644 index 000000000..967d6466c Binary files /dev/null and b/tests/resources/userdiff/after/file.php differ diff --git a/tests/resources/userdiff/before/file.html b/tests/resources/userdiff/before/file.html new file mode 100644 index 000000000..872d19663 Binary files /dev/null and b/tests/resources/userdiff/before/file.html differ diff --git a/tests/resources/userdiff/before/file.javascript b/tests/resources/userdiff/before/file.javascript new file mode 100644 index 000000000..b9f1286e5 Binary files /dev/null and b/tests/resources/userdiff/before/file.javascript differ diff --git a/tests/resources/userdiff/before/file.php b/tests/resources/userdiff/before/file.php new file mode 100644 index 000000000..63250ad01 Binary files /dev/null and b/tests/resources/userdiff/before/file.php differ diff --git a/tests/resources/userdiff/expected/driver/diff.html b/tests/resources/userdiff/expected/driver/diff.html new file mode 100644 index 000000000..5a428e7dc Binary files /dev/null and b/tests/resources/userdiff/expected/driver/diff.html differ diff --git a/tests/resources/userdiff/expected/driver/diff.javascript b/tests/resources/userdiff/expected/driver/diff.javascript new file mode 100644 index 000000000..4e65d0746 Binary files /dev/null and b/tests/resources/userdiff/expected/driver/diff.javascript differ diff --git a/tests/resources/userdiff/expected/driver/diff.php b/tests/resources/userdiff/expected/driver/diff.php new file mode 100644 index 000000000..9711b5b3e Binary files /dev/null and b/tests/resources/userdiff/expected/driver/diff.php differ diff --git a/tests/resources/userdiff/expected/nodriver/diff.html b/tests/resources/userdiff/expected/nodriver/diff.html new file mode 100644 index 000000000..2ea4b8a16 Binary files /dev/null and b/tests/resources/userdiff/expected/nodriver/diff.html differ diff --git a/tests/resources/userdiff/expected/nodriver/diff.javascript b/tests/resources/userdiff/expected/nodriver/diff.javascript new file mode 100644 index 000000000..69afe4fd8 Binary files /dev/null and b/tests/resources/userdiff/expected/nodriver/diff.javascript differ diff --git a/tests/resources/userdiff/expected/nodriver/diff.php b/tests/resources/userdiff/expected/nodriver/diff.php new file mode 100644 index 000000000..e77c094aa Binary files /dev/null and b/tests/resources/userdiff/expected/nodriver/diff.php differ diff --git a/tests/resources/userdiff/files/file.html b/tests/resources/userdiff/files/file.html new file mode 100644 index 000000000..2320e2f1e Binary files /dev/null and b/tests/resources/userdiff/files/file.html differ diff --git a/tests/resources/userdiff/files/file.javascript b/tests/resources/userdiff/files/file.javascript new file mode 100644 index 000000000..7cd3c5a8a Binary files /dev/null and b/tests/resources/userdiff/files/file.javascript differ diff --git a/tests/resources/userdiff/files/file.php b/tests/resources/userdiff/files/file.php new file mode 100644 index 000000000..967d6466c Binary files /dev/null and b/tests/resources/userdiff/files/file.php differ