diff options
-rw-r--r-- | Documentation/git-clone.txt | 4 | ||||
-rw-r--r-- | Documentation/git-merge.txt | 2 | ||||
-rw-r--r-- | Documentation/git-push.txt | 2 | ||||
-rw-r--r-- | Documentation/merge-config.txt | 4 | ||||
-rw-r--r-- | builtin/branch.c | 75 | ||||
-rw-r--r-- | builtin/grep.c | 164 | ||||
-rw-r--r-- | config.c | 59 | ||||
-rw-r--r-- | grep.c | 188 | ||||
-rw-r--r-- | grep.h | 10 | ||||
-rw-r--r-- | mergetools/p4merge | 25 | ||||
-rw-r--r-- | revision.c | 10 | ||||
-rwxr-xr-x | t/t3200-branch.sh | 19 | ||||
-rwxr-xr-x | t/t4202-log.sh | 6 | ||||
-rwxr-xr-x | t/t7008-grep-binary.sh | 22 | ||||
-rw-r--r-- | t/test-lib.sh | 2 |
15 files changed, 384 insertions, 208 deletions
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 6d98ef3d2a..7fefdb0384 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -196,9 +196,9 @@ objects from the source repository into a pack in the cloned repository. `--no-single-branch` is given to fetch the histories near the tips of all branches. Further fetches into the resulting repository will only update the - remote tracking branch for the branch this option was used for the + remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any - branch when `--single-branch` clone was made, no remote tracking + branch when `--single-branch` clone was made, no remote-tracking branch is created. --recursive:: diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 20f9228511..d34ea3c50b 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -99,7 +99,7 @@ commit or stash your changes before running 'git merge'. more than two parents (affectionately called an Octopus merge). + If no commit is given from the command line, and if `merge.defaultToUpstream` -configuration variable is set, merge the remote tracking branches +configuration variable is set, merge the remote-tracking branches that the current branch is configured to use as its upstream. See also the configuration section of this manual page. diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 22d2580129..fe46c4258a 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -175,7 +175,7 @@ useful if you write an alias or script around 'git push'. --recurse-submodules=check|on-demand:: Make sure all submodule commits used by the revisions to be - pushed are available on a remote tracking branch. If 'check' is + pushed are available on a remote-tracking branch. If 'check' is used git will verify that all submodule commits that changed in the revisions to be pushed are available on at least one remote of the submodule. If any commits are missing the push will be diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt index 861bd6f553..9bb4956ccd 100644 --- a/Documentation/merge-config.txt +++ b/Documentation/merge-config.txt @@ -9,11 +9,11 @@ merge.conflictstyle:: merge.defaultToUpstream:: If merge is called without any commit argument, merge the upstream branches configured for the current branch by using their last - observed values stored in their remote tracking branches. + observed values stored in their remote-tracking branches. The values of the `branch.<current branch>.merge` that name the branches at the remote named by `branch.<current branch>.remote` are consulted, and then they are mapped via `remote.<remote>.fetch` - to their corresponding remote tracking branches, and the tips of + to their corresponding remote-tracking branches, and the tips of these tracking branches are merged. merge.ff:: diff --git a/builtin/branch.c b/builtin/branch.c index ffd26849c7..1ec9c02612 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -154,10 +154,37 @@ static int branch_merged(int kind, const char *name, return merged; } +static int check_branch_commit(const char *branchname, const char *refname, + unsigned char *sha1, struct commit *head_rev, + int kinds, int force) +{ + struct commit *rev = lookup_commit_reference(sha1); + if (!rev) { + error(_("Couldn't look up commit object for '%s'"), refname); + return -1; + } + if (!force && !branch_merged(kinds, branchname, rev, head_rev)) { + error(_("The branch '%s' is not fully merged.\n" + "If you are sure you want to delete it, " + "run 'git branch -D %s'."), branchname, branchname); + return -1; + } + return 0; +} + +static void delete_branch_config(const char *branchname) +{ + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "branch.%s", branchname); + if (git_config_rename_section(buf.buf, NULL) < 0) + warning(_("Update of config-file failed")); + strbuf_release(&buf); +} + static int delete_branches(int argc, const char **argv, int force, int kinds, int quiet) { - struct commit *rev, *head_rev = NULL; + struct commit *head_rev = NULL; unsigned char sha1[20]; char *name = NULL; const char *fmt; @@ -187,6 +214,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, die(_("Couldn't look up commit object for HEAD")); } for (i = 0; i < argc; i++, strbuf_release(&bname)) { + const char *target; + int flags = 0; + strbuf_branchname(&bname, argv[i]); if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { error(_("Cannot delete the branch '%s' " @@ -198,7 +228,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, free(name); name = mkpathdup(fmt, bname.buf); - if (read_ref(name, sha1)) { + target = resolve_ref_unsafe(name, sha1, 0, &flags); + if (!target || + (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) { error(remote_branch ? _("remote branch '%s' not found.") : _("branch '%s' not found."), bname.buf); @@ -206,40 +238,31 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, continue; } - rev = lookup_commit_reference(sha1); - if (!rev) { - error(_("Couldn't look up commit object for '%s'"), name); - ret = 1; - continue; - } - - if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) { - error(_("The branch '%s' is not fully merged.\n" - "If you are sure you want to delete it, " - "run 'git branch -D %s'."), bname.buf, bname.buf); + if (!(flags & REF_ISSYMREF) && + check_branch_commit(bname.buf, name, sha1, head_rev, kinds, + force)) { ret = 1; continue; } - if (delete_ref(name, sha1, 0)) { + if (delete_ref(name, sha1, REF_NODEREF)) { error(remote_branch ? _("Error deleting remote branch '%s'") : _("Error deleting branch '%s'"), bname.buf); ret = 1; - } else { - struct strbuf buf = STRBUF_INIT; - if (!quiet) - printf(remote_branch - ? _("Deleted remote branch %s (was %s).\n") - : _("Deleted branch %s (was %s).\n"), - bname.buf, - find_unique_abbrev(sha1, DEFAULT_ABBREV)); - strbuf_addf(&buf, "branch.%s", bname.buf); - if (git_config_rename_section(buf.buf, NULL) < 0) - warning(_("Update of config-file failed")); - strbuf_release(&buf); + continue; + } + if (!quiet) { + printf(remote_branch + ? _("Deleted remote branch %s (was %s).\n") + : _("Deleted branch %s (was %s).\n"), + bname.buf, + (flags & REF_ISSYMREF) + ? target + : find_unique_abbrev(sha1, DEFAULT_ABBREV)); } + delete_branch_config(bname.buf); } free(name); diff --git a/builtin/grep.c b/builtin/grep.c index 82530a61b4..0e1b6c860e 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -86,7 +86,7 @@ static pthread_cond_t cond_result; static int skip_first_line; static void add_work(struct grep_opt *opt, enum grep_source_type type, - const char *name, const void *id) + const char *name, const char *path, const void *id) { grep_lock(); @@ -94,7 +94,7 @@ static void add_work(struct grep_opt *opt, enum grep_source_type type, pthread_cond_wait(&cond_write, &grep_mutex); } - grep_source_init(&todo[todo_end].source, type, name, id); + grep_source_init(&todo[todo_end].source, type, name, path, id); if (opt->binary != GREP_BINARY_TEXT) grep_source_load_driver(&todo[todo_end].source); todo[todo_end].done = 0; @@ -261,103 +261,12 @@ static int wait_all(void) } #endif -static int parse_pattern_type_arg(const char *opt, const char *arg) +static int grep_cmd_config(const char *var, const char *value, void *cb) { - if (!strcmp(arg, "default")) - return GREP_PATTERN_TYPE_UNSPECIFIED; - else if (!strcmp(arg, "basic")) - return GREP_PATTERN_TYPE_BRE; - else if (!strcmp(arg, "extended")) - return GREP_PATTERN_TYPE_ERE; - else if (!strcmp(arg, "fixed")) - return GREP_PATTERN_TYPE_FIXED; - else if (!strcmp(arg, "perl")) - return GREP_PATTERN_TYPE_PCRE; - die("bad %s argument: %s", opt, arg); -} - -static void grep_pattern_type_options(const int pattern_type, struct grep_opt *opt) -{ - switch (pattern_type) { - case GREP_PATTERN_TYPE_UNSPECIFIED: - /* fall through */ - - case GREP_PATTERN_TYPE_BRE: - opt->fixed = 0; - opt->pcre = 0; - opt->regflags &= ~REG_EXTENDED; - break; - - case GREP_PATTERN_TYPE_ERE: - opt->fixed = 0; - opt->pcre = 0; - opt->regflags |= REG_EXTENDED; - break; - - case GREP_PATTERN_TYPE_FIXED: - opt->fixed = 1; - opt->pcre = 0; - opt->regflags &= ~REG_EXTENDED; - break; - - case GREP_PATTERN_TYPE_PCRE: - opt->fixed = 0; - opt->pcre = 1; - opt->regflags &= ~REG_EXTENDED; - break; - } -} - -static int grep_config(const char *var, const char *value, void *cb) -{ - struct grep_opt *opt = cb; - char *color = NULL; - - if (userdiff_config(var, value) < 0) - return -1; - - if (!strcmp(var, "grep.extendedregexp")) { - if (git_config_bool(var, value)) - opt->extended_regexp_option = 1; - else - opt->extended_regexp_option = 0; - return 0; - } - - if (!strcmp(var, "grep.patterntype")) { - opt->pattern_type_option = parse_pattern_type_arg(var, value); - return 0; - } - - if (!strcmp(var, "grep.linenumber")) { - opt->linenum = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "color.grep")) - opt->color = git_config_colorbool(var, value); - else if (!strcmp(var, "color.grep.context")) - color = opt->color_context; - else if (!strcmp(var, "color.grep.filename")) - color = opt->color_filename; - else if (!strcmp(var, "color.grep.function")) - color = opt->color_function; - else if (!strcmp(var, "color.grep.linenumber")) - color = opt->color_lineno; - else if (!strcmp(var, "color.grep.match")) - color = opt->color_match; - else if (!strcmp(var, "color.grep.selected")) - color = opt->color_selected; - else if (!strcmp(var, "color.grep.separator")) - color = opt->color_sep; - else - return git_color_default_config(var, value, cb); - if (color) { - if (!value) - return config_error_nonbool(var); - color_parse(value, var, color); - } - return 0; + int st = grep_config(var, value, cb); + if (git_color_default_config(var, value, cb) < 0) + st = -1; + return st; } static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) @@ -371,7 +280,8 @@ static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type } static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, - const char *filename, int tree_name_len) + const char *filename, int tree_name_len, + const char *path) { struct strbuf pathbuf = STRBUF_INIT; @@ -385,7 +295,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, #ifndef NO_PTHREADS if (use_threads) { - add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, sha1); + add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); strbuf_release(&pathbuf); return 0; } else @@ -394,7 +304,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, sha1); + grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); strbuf_release(&pathbuf); hit = grep_source(opt, &gs); @@ -414,7 +324,7 @@ static int grep_file(struct grep_opt *opt, const char *filename) #ifndef NO_PTHREADS if (use_threads) { - add_work(opt, GREP_SOURCE_FILE, buf.buf, filename); + add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename); strbuf_release(&buf); return 0; } else @@ -423,7 +333,7 @@ static int grep_file(struct grep_opt *opt, const char *filename) struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename); + grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); strbuf_release(&buf); hit = grep_source(opt, &gs); @@ -479,7 +389,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) { if (ce_stage(ce)) continue; - hit |= grep_sha1(opt, ce->sha1, ce->name, 0); + hit |= grep_sha1(opt, ce->sha1, ce->name, 0, ce->name); } else hit |= grep_file(opt, ce->name); @@ -497,7 +407,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int } static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, - struct tree_desc *tree, struct strbuf *base, int tn_len) + struct tree_desc *tree, struct strbuf *base, int tn_len, + int check_attr) { int hit = 0; enum interesting match = entry_not_interesting; @@ -518,7 +429,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_add(base, entry.path, te_len); if (S_ISREG(entry.mode)) { - hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len); + hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len, + check_attr ? base->buf + tn_len : NULL); } else if (S_ISDIR(entry.mode)) { enum object_type type; @@ -533,7 +445,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); - hit |= grep_tree(opt, pathspec, &sub, base, tn_len); + hit |= grep_tree(opt, pathspec, &sub, base, tn_len, + check_attr); free(data); } strbuf_setlen(base, old_baselen); @@ -548,7 +461,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct object *obj, const char *name) { if (obj->type == OBJ_BLOB) - return grep_sha1(opt, obj->sha1, name, 0); + return grep_sha1(opt, obj->sha1, name, 0, NULL); if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { struct tree_desc tree; void *data; @@ -571,7 +484,8 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_addch(&base, ':'); } init_tree_desc(&tree, data, size); - hit = grep_tree(opt, pathspec, &tree, &base, base.len); + hit = grep_tree(opt, pathspec, &tree, &base, base.len, + obj->type == OBJ_COMMIT); strbuf_release(&base); free(data); return hit; @@ -839,27 +753,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(grep_usage, options); - memset(&opt, 0, sizeof(opt)); - opt.prefix = prefix; - opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; - opt.relative = 1; - opt.pathname = 1; - opt.pattern_tail = &opt.pattern_list; - opt.header_tail = &opt.header_list; - opt.regflags = REG_NEWLINE; - opt.max_depth = -1; - opt.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED; - opt.extended_regexp_option = 0; - - strcpy(opt.color_context, ""); - strcpy(opt.color_filename, ""); - strcpy(opt.color_function, ""); - strcpy(opt.color_lineno, ""); - strcpy(opt.color_match, GIT_COLOR_BOLD_RED); - strcpy(opt.color_selected, ""); - strcpy(opt.color_sep, GIT_COLOR_CYAN); - opt.color = -1; - git_config(grep_config, &opt); + init_grep_defaults(); + git_config(grep_cmd_config, NULL); + grep_init(&opt, prefix); /* * If there is no -- then the paths must exist in the working @@ -875,13 +771,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION | PARSE_OPT_NO_INTERNAL_HELP); - - if (pattern_type_arg != GREP_PATTERN_TYPE_UNSPECIFIED) - grep_pattern_type_options(pattern_type_arg, &opt); - else if (opt.pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED) - grep_pattern_type_options(opt.pattern_type_option, &opt); - else if (opt.extended_regexp_option) - grep_pattern_type_options(GREP_PATTERN_TYPE_ERE, &opt); + grep_commit_pattern_type(pattern_type_arg, &opt); if (use_index && !startup_info->have_repository) /* die the same way as if we did it at the beginning */ @@ -10,8 +10,6 @@ #include "strbuf.h" #include "quote.h" -#define MAXNAME (256) - typedef struct config_file { struct config_file *prev; FILE *f; @@ -19,7 +17,7 @@ typedef struct config_file { int linenr; int eof; struct strbuf value; - char var[MAXNAME]; + struct strbuf var; } config_file; static config_file *cf; @@ -260,7 +258,7 @@ static inline int iskeychar(int c) return isalnum(c) || c == '-'; } -static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) +static int get_value(config_fn_t fn, void *data, struct strbuf *name) { int c; char *value; @@ -272,11 +270,9 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) break; if (!iskeychar(c)) break; - name[len++] = tolower(c); - if (len >= MAXNAME) - return -1; + strbuf_addch(name, tolower(c)); } - name[len] = 0; + while (c == ' ' || c == '\t') c = get_next_char(); @@ -288,10 +284,10 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) if (!value) return -1; } - return fn(name, value, data); + return fn(name->buf, value, data); } -static int get_extended_base_var(char *name, int baselen, int c) +static int get_extended_base_var(struct strbuf *name, int c) { do { if (c == '\n') @@ -302,7 +298,7 @@ static int get_extended_base_var(char *name, int baselen, int c) /* We require the format to be '[base "extension"]' */ if (c != '"') return -1; - name[baselen++] = '.'; + strbuf_addch(name, '.'); for (;;) { int c = get_next_char(); @@ -315,37 +311,31 @@ static int get_extended_base_var(char *name, int baselen, int c) if (c == '\n') goto error_incomplete_line; } - name[baselen++] = c; - if (baselen > MAXNAME / 2) - return -1; + strbuf_addch(name, c); } /* Final ']' */ if (get_next_char() != ']') return -1; - return baselen; + return 0; error_incomplete_line: cf->linenr--; return -1; } -static int get_base_var(char *name) +static int get_base_var(struct strbuf *name) { - int baselen = 0; - for (;;) { int c = get_next_char(); if (cf->eof) return -1; if (c == ']') - return baselen; + return 0; if (isspace(c)) - return get_extended_base_var(name, baselen, c); + return get_extended_base_var(name, c); if (!iskeychar(c) && c != '.') return -1; - if (baselen > MAXNAME / 2) - return -1; - name[baselen++] = tolower(c); + strbuf_addch(name, tolower(c)); } } @@ -353,7 +343,7 @@ static int git_parse_file(config_fn_t fn, void *data) { int comment = 0; int baselen = 0; - char *var = cf->var; + struct strbuf *var = &cf->var; /* U+FEFF Byte Order Mark in UTF8 */ static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; @@ -389,17 +379,24 @@ static int git_parse_file(config_fn_t fn, void *data) continue; } if (c == '[') { - baselen = get_base_var(var); - if (baselen <= 0) + /* Reset prior to determining a new stem */ + strbuf_reset(var); + if (get_base_var(var) < 0 || var->len < 1) break; - var[baselen++] = '.'; - var[baselen] = 0; + strbuf_addch(var, '.'); + baselen = var->len; continue; } if (!isalpha(c)) break; - var[baselen] = tolower(c); - if (get_value(fn, data, var, baselen+1) < 0) + /* + * Truncate the var name back to the section header + * stem prior to grabbing the suffix part of the name + * and the value. + */ + strbuf_setlen(var, baselen); + strbuf_addch(var, tolower(c)); + if (get_value(fn, data, var) < 0) break; } die("bad config file line %d in %s", cf->linenr, cf->name); @@ -899,12 +896,14 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data) top.linenr = 1; top.eof = 0; strbuf_init(&top.value, 1024); + strbuf_init(&top.var, 1024); cf = ⊤ ret = git_parse_file(fn, data); /* pop config-file parsing state stack */ strbuf_release(&top.value); + strbuf_release(&top.var); cf = top.prev; fclose(f); @@ -6,6 +6,183 @@ static int grep_source_load(struct grep_source *gs); static int grep_source_is_binary(struct grep_source *gs); +static struct grep_opt grep_defaults; + +/* + * Initialize the grep_defaults template with hardcoded defaults. + * We could let the compiler do this, but without C99 initializers + * the code gets unwieldy and unreadable, so... + */ +void init_grep_defaults(void) +{ + struct grep_opt *opt = &grep_defaults; + static int run_once; + + if (run_once) + return; + run_once++; + + memset(opt, 0, sizeof(*opt)); + opt->relative = 1; + opt->pathname = 1; + opt->regflags = REG_NEWLINE; + opt->max_depth = -1; + opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED; + opt->extended_regexp_option = 0; + strcpy(opt->color_context, ""); + strcpy(opt->color_filename, ""); + strcpy(opt->color_function, ""); + strcpy(opt->color_lineno, ""); + strcpy(opt->color_match, GIT_COLOR_BOLD_RED); + strcpy(opt->color_selected, ""); + strcpy(opt->color_sep, GIT_COLOR_CYAN); + opt->color = -1; +} + +static int parse_pattern_type_arg(const char *opt, const char *arg) +{ + if (!strcmp(arg, "default")) + return GREP_PATTERN_TYPE_UNSPECIFIED; + else if (!strcmp(arg, "basic")) + return GREP_PATTERN_TYPE_BRE; + else if (!strcmp(arg, "extended")) + return GREP_PATTERN_TYPE_ERE; + else if (!strcmp(arg, "fixed")) + return GREP_PATTERN_TYPE_FIXED; + else if (!strcmp(arg, "perl")) + return GREP_PATTERN_TYPE_PCRE; + die("bad %s argument: %s", opt, arg); +} + +/* + * Read the configuration file once and store it in + * the grep_defaults template. + */ +int grep_config(const char *var, const char *value, void *cb) +{ + struct grep_opt *opt = &grep_defaults; + char *color = NULL; + + if (userdiff_config(var, value) < 0) + return -1; + + if (!strcmp(var, "grep.extendedregexp")) { + if (git_config_bool(var, value)) + opt->extended_regexp_option = 1; + else + opt->extended_regexp_option = 0; + return 0; + } + + if (!strcmp(var, "grep.patterntype")) { + opt->pattern_type_option = parse_pattern_type_arg(var, value); + return 0; + } + + if (!strcmp(var, "grep.linenumber")) { + opt->linenum = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "color.grep")) + opt->color = git_config_colorbool(var, value); + else if (!strcmp(var, "color.grep.context")) + color = opt->color_context; + else if (!strcmp(var, "color.grep.filename")) + color = opt->color_filename; + else if (!strcmp(var, "color.grep.function")) + color = opt->color_function; + else if (!strcmp(var, "color.grep.linenumber")) + color = opt->color_lineno; + else if (!strcmp(var, "color.grep.match")) + color = opt->color_match; + else if (!strcmp(var, "color.grep.selected")) + color = opt->color_selected; + else if (!strcmp(var, "color.grep.separator")) + color = opt->color_sep; + + if (color) { + if (!value) + return config_error_nonbool(var); + color_parse(value, var, color); + } + return 0; +} + +/* + * Initialize one instance of grep_opt and copy the + * default values from the template we read the configuration + * information in an earlier call to git_config(grep_config). + */ +void grep_init(struct grep_opt *opt, const char *prefix) +{ + struct grep_opt *def = &grep_defaults; + + memset(opt, 0, sizeof(*opt)); + opt->prefix = prefix; + opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; + opt->pattern_tail = &opt->pattern_list; + opt->header_tail = &opt->header_list; + + opt->color = def->color; + opt->extended_regexp_option = def->extended_regexp_option; + opt->pattern_type_option = def->pattern_type_option; + opt->linenum = def->linenum; + opt->max_depth = def->max_depth; + opt->pathname = def->pathname; + opt->regflags = def->regflags; + opt->relative = def->relative; + + strcpy(opt->color_context, def->color_context); + strcpy(opt->color_filename, def->color_filename); + strcpy(opt->color_function, def->color_function); + strcpy(opt->color_lineno, def->color_lineno); + strcpy(opt->color_match, def->color_match); + strcpy(opt->color_selected, def->color_selected); + strcpy(opt->color_sep, def->color_sep); +} + +void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt) +{ + if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED) + grep_set_pattern_type_option(pattern_type, opt); + else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED) + grep_set_pattern_type_option(opt->pattern_type_option, opt); + else if (opt->extended_regexp_option) + grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt); +} + +void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt) +{ + switch (pattern_type) { + case GREP_PATTERN_TYPE_UNSPECIFIED: + /* fall through */ + + case GREP_PATTERN_TYPE_BRE: + opt->fixed = 0; + opt->pcre = 0; + opt->regflags &= ~REG_EXTENDED; + break; + + case GREP_PATTERN_TYPE_ERE: + opt->fixed = 0; + opt->pcre = 0; + opt->regflags |= REG_EXTENDED; + break; + + case GREP_PATTERN_TYPE_FIXED: + opt->fixed = 1; + opt->pcre = 0; + opt->regflags &= ~REG_EXTENDED; + break; + + case GREP_PATTERN_TYPE_PCRE: + opt->fixed = 0; + opt->pcre = 1; + opt->regflags &= ~REG_EXTENDED; + break; + } +} static struct grep_pat *create_grep_pat(const char *pat, size_t patlen, const char *origin, int no, @@ -1373,7 +1550,7 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size) struct grep_source gs; int r; - grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL); + grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL, NULL); gs.buf = buf; gs.size = size; @@ -1384,10 +1561,12 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size) } void grep_source_init(struct grep_source *gs, enum grep_source_type type, - const char *name, const void *identifier) + const char *name, const char *path, + const void *identifier) { gs->type = type; gs->name = name ? xstrdup(name) : NULL; + gs->path = path ? xstrdup(path) : NULL; gs->buf = NULL; gs->size = 0; gs->driver = NULL; @@ -1409,6 +1588,8 @@ void grep_source_clear(struct grep_source *gs) { free(gs->name); gs->name = NULL; + free(gs->path); + gs->path = NULL; free(gs->identifier); gs->identifier = NULL; grep_source_clear_data(gs); @@ -1501,7 +1682,8 @@ void grep_source_load_driver(struct grep_source *gs) return; grep_attr_lock(); - gs->driver = userdiff_find_by_path(gs->name); + if (gs->path) + gs->driver = userdiff_find_by_path(gs->path); if (!gs->driver) gs->driver = userdiff_find_by_name("default"); grep_attr_unlock(); @@ -138,6 +138,12 @@ struct grep_opt { void *output_priv; }; +extern void init_grep_defaults(void); +extern int grep_config(const char *var, const char *value, void *); +extern void grep_init(struct grep_opt *, const char *prefix); +void grep_set_pattern_type_option(enum grep_pattern_type, struct grep_opt *opt); +void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt); + extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t); extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t); extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *); @@ -158,11 +164,13 @@ struct grep_source { char *buf; unsigned long size; + char *path; /* for attribute lookups */ struct userdiff_driver *driver; }; void grep_source_init(struct grep_source *gs, enum grep_source_type type, - const char *name, const void *identifier); + const char *name, const char *path, + const void *identifier); void grep_source_clear_data(struct grep_source *gs); void grep_source_clear(struct grep_source *gs); void grep_source_load_driver(struct grep_source *gs); diff --git a/mergetools/p4merge b/mergetools/p4merge index 1a45c1b0c5..295361a8aa 100644 --- a/mergetools/p4merge +++ b/mergetools/p4merge @@ -1,5 +1,30 @@ diff_cmd () { + # p4merge does not like /dev/null + rm_local= + rm_remote= + if test "/dev/null" = "$LOCAL" + then + LOCAL="./p4merge-dev-null.LOCAL.$$" + >"$LOCAL" + rm_local=true + fi + if test "/dev/null" = "$REMOTE" + then + REMOTE="./p4merge-dev-null.REMOTE.$$" + >"$REMOTE" + rm_remote=true + fi + "$merge_tool_path" "$LOCAL" "$REMOTE" + + if test -n "$rm_local" + then + rm -f "$LOCAL" + fi + if test -n "$rm_remote" + then + rm -f "$REMOTE" + fi } merge_cmd () { diff --git a/revision.c b/revision.c index a09e60bedb..68545c8015 100644 --- a/revision.c +++ b/revision.c @@ -1048,9 +1048,9 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->commit_format = CMIT_FMT_DEFAULT; + init_grep_defaults(); + grep_init(&revs->grep_filter, prefix); revs->grep_filter.status_only = 1; - revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list); - revs->grep_filter.header_tail = &(revs->grep_filter.header_list); revs->grep_filter.regflags = REG_NEWLINE; diff_setup(&revs->diffopt); @@ -1604,12 +1604,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--grep-debug")) { revs->grep_filter.debug = 1; } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) { - revs->grep_filter.regflags |= REG_EXTENDED; + grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter); } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) { revs->grep_filter.regflags |= REG_ICASE; DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE); } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) { - revs->grep_filter.fixed = 1; + grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter); } else if (!strcmp(arg, "--all-match")) { revs->grep_filter.all_match = 1; } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) { @@ -1893,6 +1893,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s revs->diffopt.abbrev = revs->abbrev; diff_setup_done(&revs->diffopt); + grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED, + &revs->grep_filter); compile_grep_patterns(&revs->grep_filter); if (revs->reverse && revs->reflog_info) diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 79c8d0142e..80e6be39d7 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -262,6 +262,25 @@ test_expect_success 'config information was renamed, too' \ "test $(git config branch.s.dummy) = Hello && test_must_fail git config branch.s/s/dummy" +test_expect_success 'deleting a symref' ' + git branch target && + git symbolic-ref refs/heads/symref refs/heads/target && + echo "Deleted branch symref (was refs/heads/target)." >expect && + git branch -d symref >actual && + test_path_is_file .git/refs/heads/target && + test_path_is_missing .git/refs/heads/symref && + test_i18ncmp expect actual +' + +test_expect_success 'deleting a dangling symref' ' + git symbolic-ref refs/heads/dangling-symref nowhere && + test_path_is_file .git/refs/heads/dangling-symref && + echo "Deleted branch dangling-symref (was nowhere)." >expect && + git branch -d dangling-symref >actual && + test_path_is_missing .git/refs/heads/dangling-symref && + test_i18ncmp expect actual +' + test_expect_success 'renaming a symref is not allowed' \ ' git symbolic-ref refs/heads/master2 refs/heads/master && diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 924ba536ca..e6537abe1d 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -230,6 +230,12 @@ test_expect_success 'log --grep -i' ' test_cmp expect actual ' +test_expect_success 'log -F -E --grep=<ere> uses ere' ' + echo second >expect && + git log -1 --pretty="tformat:%s" -F -E --grep=s.c.nd >actual && + test_cmp expect actual +' + cat > expect <<EOF * Second * sixth diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index fd6410fc71..26f831984d 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -111,6 +111,28 @@ test_expect_success 'grep respects binary diff attribute' ' test_cmp expect actual ' +test_expect_success 'grep --cached respects binary diff attribute' ' + git grep --cached text t >actual && + test_cmp expect actual +' + +test_expect_success 'grep --cached respects binary diff attribute (2)' ' + git add .gitattributes && + rm .gitattributes && + git grep --cached text t >actual && + test_when_finished "git rm --cached .gitattributes" && + test_when_finished "git checkout .gitattributes" && + test_cmp expect actual +' + +test_expect_success 'grep revision respects binary diff attribute' ' + git commit -m new && + echo "Binary file HEAD:t matches" >expect && + git grep text HEAD -- t >actual && + test_when_finished "git reset HEAD^" && + test_cmp expect actual +' + test_expect_success 'grep respects not-binary diff attribute' ' echo binQary | q_to_nul >b && git add b && diff --git a/t/test-lib.sh b/t/test-lib.sh index 514282cbdf..489bc80fc1 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -230,7 +230,7 @@ else say_color() { test -z "$1" && test -n "$quiet" && return shift - echo "$*" + printf "%s\n" "$*" } fi |