diff options
Diffstat (limited to 'builtin')
-rw-r--r-- | builtin/add.c | 6 | ||||
-rw-r--r-- | builtin/branch.c | 2 | ||||
-rw-r--r-- | builtin/checkout.c | 264 | ||||
-rw-r--r-- | builtin/clone.c | 6 | ||||
-rw-r--r-- | builtin/commit.c | 10 | ||||
-rw-r--r-- | builtin/config.c | 6 | ||||
-rw-r--r-- | builtin/diff-files.c | 2 | ||||
-rw-r--r-- | builtin/diff.c | 23 | ||||
-rw-r--r-- | builtin/fast-export.c | 2 | ||||
-rw-r--r-- | builtin/fsck.c | 31 | ||||
-rw-r--r-- | builtin/grep.c | 196 | ||||
-rw-r--r-- | builtin/hash-object.c | 2 | ||||
-rw-r--r-- | builtin/init-db.c | 2 | ||||
-rw-r--r-- | builtin/log.c | 2 | ||||
-rw-r--r-- | builtin/merge.c | 47 | ||||
-rw-r--r-- | builtin/rerere.c | 14 | ||||
-rw-r--r-- | builtin/revert.c | 29 | ||||
-rw-r--r-- | builtin/tag.c | 6 | ||||
-rw-r--r-- | builtin/update-index.c | 8 |
19 files changed, 340 insertions, 318 deletions
diff --git a/builtin/add.c b/builtin/add.c index 12b964e642..f7a17e43f6 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -86,7 +86,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags) struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = pathspec; + init_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; data.flags = flags; @@ -317,12 +317,12 @@ static struct option builtin_add_options[] = { OPT__VERBOSE(&verbose, "be verbose"), OPT_GROUP(""), OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), - OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), + OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"), OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"), OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"), - OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"), + OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"), diff --git a/builtin/branch.c b/builtin/branch.c index 9e546e4a83..fe8f2fcd52 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -134,7 +134,7 @@ static int branch_merged(int kind, const char *name, in_merge_bases(rev, &head_rev, 1) != merged) { if (merged) warning("deleting branch '%s' that has been merged to\n" - " '%s', but it is not yet merged to HEAD.", + " '%s', but not yet been merged to HEAD.", name, reference_name); else warning("not deleting branch '%s' that is not yet merged to\n" diff --git a/builtin/checkout.c b/builtin/checkout.c index 757f9a08dd..cc97dbc30f 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -30,6 +30,7 @@ struct checkout_opts { int quiet; int merge; int force; + int force_detach; int writeout_stage; int writeout_error; @@ -297,7 +298,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts) run_diff_index(&rev, 0); } -static void describe_detached_head(char *msg, struct commit *commit) +static void describe_detached_head(const char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; struct pretty_print_context ctx = {0}; @@ -404,7 +405,7 @@ static int merge_working_tree(struct checkout_opts *opts, topts.dir->exclude_per_dir = ".gitignore"; tree = parse_tree_indirect(old->commit ? old->commit->object.sha1 : - (unsigned char *)EMPTY_TREE_SHA1_BIN); + EMPTY_TREE_SHA1_BIN); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(new->commit->object.sha1); init_tree_desc(&trees[1], tree->buffer, tree->size); @@ -541,7 +542,17 @@ static void update_refs_for_switch(struct checkout_opts *opts, strbuf_addf(&msg, "checkout: moving from %s to %s", old_desc ? old_desc : "(invalid)", new->name); - if (new->path) { + if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) { + /* Nothing to do. */ + } else if (opts->force_detach || !new->path) { /* No longer on any branch. */ + update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL, + REF_NODEREF, DIE_ON_ERR); + if (!opts->quiet) { + if (old->path && advice_detached_head) + detach_advice(old->path, new->name); + describe_detached_head("HEAD is now at", new->commit); + } + } else if (new->path) { /* Switch branches. */ create_symref("HEAD", new->path, msg.buf); if (!opts->quiet) { if (old->path && !strcmp(new->path, old->path)) @@ -563,18 +574,11 @@ static void update_refs_for_switch(struct checkout_opts *opts, if (!file_exists(ref_file) && file_exists(log_file)) remove_path(log_file); } - } else if (strcmp(new->name, "HEAD")) { - update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL, - REF_NODEREF, DIE_ON_ERR); - if (!opts->quiet) { - if (old->path && advice_detached_head) - detach_advice(old->path, new->name); - describe_detached_head("HEAD is now at", new->commit); - } } remove_branch_state(); strbuf_release(&msg); - if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD"))) + if (!opts->quiet && + (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD")))) report_tracking(new); } @@ -675,11 +679,123 @@ static const char *unique_tracking_name(const char *name) return NULL; } +static int parse_branchname_arg(int argc, const char **argv, + int dwim_new_local_branch_ok, + struct branch_info *new, + struct tree **source_tree, + unsigned char rev[20], + const char **new_branch) +{ + int argcount = 0; + unsigned char branch_rev[20]; + const char *arg; + int has_dash_dash; + + /* + * case 1: git checkout <ref> -- [<paths>] + * + * <ref> must be a valid tree, everything after the '--' must be + * a path. + * + * case 2: git checkout -- [<paths>] + * + * everything after the '--' must be paths. + * + * case 3: git checkout <something> [<paths>] + * + * With no paths, if <something> is a commit, that is to + * switch to the branch or detach HEAD at it. As a special case, + * if <something> is A...B (missing A or B means HEAD but you can + * omit at most one side), and if there is a unique merge base + * between A and B, A...B names that merge base. + * + * With no paths, if <something> is _not_ a commit, no -t nor -b + * was given, and there is a tracking branch whose name is + * <something> in one and only one remote, then this is a short-hand + * to fork local <something> from that remote-tracking branch. + * + * Otherwise <something> shall not be ambiguous. + * - If it's *only* a reference, treat it like case (1). + * - If it's only a path, treat it like case (2). + * - else: fail. + * + */ + if (!argc) + return 0; + + if (!strcmp(argv[0], "--")) /* case (2) */ + return 1; + + arg = argv[0]; + has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + + if (!strcmp(arg, "-")) + arg = "@{-1}"; + + if (get_sha1_mb(arg, rev)) { + if (has_dash_dash) /* case (1) */ + die("invalid reference: %s", arg); + if (dwim_new_local_branch_ok && + !check_filename(NULL, arg) && + argc == 1) { + const char *remote = unique_tracking_name(arg); + if (!remote || get_sha1(remote, rev)) + return argcount; + *new_branch = arg; + arg = remote; + /* DWIMmed to create local branch */ + } else { + return argcount; + } + } + + /* we can't end up being in (2) anymore, eat the argument */ + argcount++; + argv++; + argc--; + + new->name = arg; + setup_branch_path(new); + + if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK && + resolve_ref(new->path, branch_rev, 1, NULL)) + hashcpy(rev, branch_rev); + else + new->path = NULL; /* not an existing branch */ + + new->commit = lookup_commit_reference_gently(rev, 1); + if (!new->commit) { + /* not a commit */ + *source_tree = parse_tree_indirect(rev); + } else { + parse_commit(new->commit); + *source_tree = new->commit->tree; + } + + if (!*source_tree) /* case (1): want a tree */ + die("reference is not a tree: %s", arg); + if (!has_dash_dash) {/* case (3 -> 1) */ + /* + * Do not complain the most common case + * git checkout branch + * even if there happen to be a file called 'branch'; + * it would be extremely annoying. + */ + if (argc) + verify_non_filename(NULL, arg); + } else { + argcount++; + argv++; + argc--; + } + + return argcount; +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; unsigned char rev[20]; - const char *arg; struct branch_info new; struct tree *source_tree = NULL; char *conflict_style = NULL; @@ -692,6 +808,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_STRING('B', NULL, &opts.new_branch_force, "branch", "create/reset and checkout a branch"), OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"), + OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"), OPT_SET_INT('t', "track", &opts.track, "set upstream info for new branch", BRANCH_TRACK_EXPLICIT), OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"), @@ -709,7 +826,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END(), }; - int has_dash_dash; memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); @@ -731,9 +847,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.new_branch = opts.new_branch_force; if (patch_mode && (opts.track > 0 || opts.new_branch - || opts.new_branch_log || opts.merge || opts.force)) + || opts.new_branch_log || opts.merge || opts.force + || opts.force_detach)) die ("--patch is incompatible with all other options"); + if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) + die("--detach cannot be used with -b/-B/--orphan"); + if (opts.force_detach && 0 < opts.track) + die("--detach cannot be used with -t"); + /* --track without -b should DWIM */ if (0 < opts.track && !opts.new_branch) { const char *argv0 = argv[0]; @@ -766,105 +888,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) die("git checkout: -f and -m are incompatible"); /* - * case 1: git checkout <ref> -- [<paths>] - * - * <ref> must be a valid tree, everything after the '--' must be - * a path. - * - * case 2: git checkout -- [<paths>] - * - * everything after the '--' must be paths. - * - * case 3: git checkout <something> [<paths>] - * - * With no paths, if <something> is a commit, that is to - * switch to the branch or detach HEAD at it. As a special case, - * if <something> is A...B (missing A or B means HEAD but you can - * omit at most one side), and if there is a unique merge base - * between A and B, A...B names that merge base. + * Extract branch name from command line arguments, so + * all that is left is pathspecs. * - * With no paths, if <something> is _not_ a commit, no -t nor -b - * was given, and there is a remote-tracking branch whose name is - * <something> in one and only one remote, then this is a short-hand - * to fork local <something> from that remote-tracking branch. + * Handle * - * Otherwise <something> shall not be ambiguous. - * - If it's *only* a reference, treat it like case (1). - * - If it's only a path, treat it like case (2). - * - else: fail. + * 1) git checkout <tree> -- [<paths>] + * 2) git checkout -- [<paths>] + * 3) git checkout <something> [<paths>] * + * including "last branch" syntax and DWIM-ery for names of + * remote branches, erroring out for invalid or ambiguous cases. */ if (argc) { - if (!strcmp(argv[0], "--")) { /* case (2) */ - argv++; - argc--; - goto no_reference; - } - - arg = argv[0]; - has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); - - if (!strcmp(arg, "-")) - arg = "@{-1}"; - - if (get_sha1_mb(arg, rev)) { - if (has_dash_dash) /* case (1) */ - die("invalid reference: %s", arg); - if (!patch_mode && - dwim_new_local_branch && - opts.track == BRANCH_TRACK_UNSPECIFIED && - !opts.new_branch && - !check_filename(NULL, arg) && - argc == 1) { - const char *remote = unique_tracking_name(arg); - if (!remote || get_sha1(remote, rev)) - goto no_reference; - opts.new_branch = arg; - arg = remote; - /* DWIMmed to create local branch */ - } - else - goto no_reference; - } - - /* we can't end up being in (2) anymore, eat the argument */ - argv++; - argc--; - - new.name = arg; - if ((new.commit = lookup_commit_reference_gently(rev, 1))) { - setup_branch_path(&new); - - if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) && - resolve_ref(new.path, rev, 1, NULL)) - ; - else - new.path = NULL; - parse_commit(new.commit); - source_tree = new.commit->tree; - } else - source_tree = parse_tree_indirect(rev); - - if (!source_tree) /* case (1): want a tree */ - die("reference is not a tree: %s", arg); - if (!has_dash_dash) {/* case (3 -> 1) */ - /* - * Do not complain the most common case - * git checkout branch - * even if there happen to be a file called 'branch'; - * it would be extremely annoying. - */ - if (argc) - verify_non_filename(NULL, arg); - } - else { - argv++; - argc--; - } + int dwim_ok = + !patch_mode && + dwim_new_local_branch && + opts.track == BRANCH_TRACK_UNSPECIFIED && + !opts.new_branch; + int n = parse_branchname_arg(argc, argv, dwim_ok, + &new, &source_tree, rev, &opts.new_branch); + argv += n; + argc -= n; } -no_reference: - if (opts.track == BRANCH_TRACK_UNSPECIFIED) opts.track = git_branch_track; @@ -886,6 +933,9 @@ no_reference: } } + if (opts.force_detach) + die("git checkout: --detach does not take a path argument"); + if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."); diff --git a/builtin/clone.c b/builtin/clone.c index 61e0989b5a..60d9a64280 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -66,10 +66,10 @@ static struct option builtin_clone_options[] = { "setup as shared repository"), OPT_BOOLEAN(0, "recursive", &option_recursive, "initialize submodules in the clone"), - OPT_BOOLEAN(0, "recurse_submodules", &option_recursive, + OPT_BOOLEAN(0, "recurse-submodules", &option_recursive, "initialize submodules in the clone"), - OPT_STRING(0, "template", &option_template, "path", - "path the template repository"), + OPT_STRING(0, "template", &option_template, "template-directory", + "directory from which templates will be used"), OPT_STRING(0, "reference", &option_reference, "repo", "reference repository"), OPT_STRING('o', "origin", &option_origin, "branch", diff --git a/builtin/commit.c b/builtin/commit.c index 03cff5af63..d7f55e3d46 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -118,10 +118,10 @@ static struct option builtin_commit_options[] = { OPT__VERBOSE(&verbose, "show diff in commit message template"), OPT_GROUP("Commit message options"), - OPT_FILENAME('F', "file", &logfile, "read log from file"), + OPT_FILENAME('F', "file", &logfile, "read message from file"), OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"), OPT_STRING(0, "date", &force_date, "DATE", "override date for commit"), - OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m), + OPT_CALLBACK('m', "message", &message, "MESSAGE", "commit message", opt_parse_m), OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"), OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"), OPT_STRING(0, "fixup", &fixup_message, "COMMIT", "use autosquash formatted message to fixup specified commit"), @@ -145,12 +145,12 @@ static struct option builtin_commit_options[] = { STATUS_FORMAT_SHORT), OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"), OPT_SET_INT(0, "porcelain", &status_format, - "show porcelain output format", STATUS_FORMAT_PORCELAIN), + "machine-readable output", STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &null_termination, "terminate entries with NUL"), OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"), - { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, /* end commit contents options */ { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL, @@ -1092,7 +1092,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('b', "branch", &status_show_branch, "show branch information"), OPT_SET_INT(0, "porcelain", &status_format, - "show porcelain output format", + "machine-readable output", STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &null_termination, "terminate entries with NUL"), diff --git a/builtin/config.c b/builtin/config.c index ca4a0db4a7..dad86fecfe 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -500,3 +500,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 0; } + +int cmd_repo_config(int argc, const char **argv, const char *prefix) +{ + fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n"); + return cmd_config(argc, argv, prefix); +} diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 951c7c8994..46085f862f 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; - if (read_cache_preload(rev.diffopt.paths) < 0) { + if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/diff.c b/builtin/diff.c index 945e7583a8..4c9deb28ec 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -135,7 +135,7 @@ static int builtin_diff_index(struct rev_info *revs, revs->max_count != -1 || revs->min_age != -1 || revs->max_age != -1) usage(builtin_diff_usage); - if (read_cache_preload(revs->diffopt.paths) < 0) { + if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } @@ -237,7 +237,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); - if (read_cache_preload(revs->diffopt.paths) < 0) { + if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } @@ -330,8 +330,11 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); - if (!rev.pending.nr) - die("No HEAD commit to compare with (yet)"); + if (!rev.pending.nr) { + struct tree *tree; + tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN); + add_pending_object(&rev, &tree->object, "HEAD"); + } break; } } @@ -371,14 +374,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } die("unhandled object '%s' given.", name); } - if (rev.prune_data) { - const char **pathspec = rev.prune_data; - while (*pathspec) { - if (!path) - path = *pathspec; - paths++; - pathspec++; - } + if (rev.prune_data.nr) { + if (!path) + path = rev.prune_data.items[0].match; + paths += rev.prune_data.nr; } /* diff --git a/builtin/fast-export.c b/builtin/fast-export.c index c8fd46b872..ba57457cc5 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -651,7 +651,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (import_filename) import_marks(import_filename); - if (import_filename && revs.prune_data) + if (import_filename && revs.prune_data.nr) full_tree = 1; get_tags_and_duplicates(&revs.pending, &extra_refs); diff --git a/builtin/fsck.c b/builtin/fsck.c index 6d5ebca7a9..795aba087f 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -74,7 +74,13 @@ static int mark_object(struct object *obj, int type, void *data) { struct object *parent = data; + /* + * The only case data is NULL or type is OBJ_ANY is when + * mark_object_reachable() calls us. All the callers of + * that function has non-NULL obj hence ... + */ if (!obj) { + /* ... these references to parent->fld are safe here */ printf("broken link from %7s %s\n", typename(parent->type), sha1_to_hex(parent->sha1)); printf("broken link from %7s %s\n", @@ -84,6 +90,7 @@ static int mark_object(struct object *obj, int type, void *data) } if (type != OBJ_ANY && obj->type != type) + /* ... and the reference to parent is safe here */ objerror(parent, "wrong object type in link"); if (obj->flags & REACHABLE) @@ -109,7 +116,7 @@ static void mark_object_reachable(struct object *obj) mark_object(obj, OBJ_ANY, NULL); } -static int traverse_one_object(struct object *obj, struct object *parent) +static int traverse_one_object(struct object *obj) { int result; struct tree *tree = NULL; @@ -138,7 +145,7 @@ static int traverse_reachable(void) entry = pending.objects + --pending.nr; obj = entry->item; parent = (struct object *) entry->name; - result |= traverse_one_object(obj, parent); + result |= traverse_one_object(obj); } return !!result; } @@ -385,10 +392,20 @@ static void add_sha1_list(unsigned char *sha1, unsigned long ino) sha1_list.nr = ++nr; } +static inline int is_loose_object_file(struct dirent *de, + char *name, unsigned char *sha1) +{ + if (strlen(de->d_name) != 38) + return 0; + memcpy(name + 2, de->d_name, 39); + return !get_sha1_hex(name, sha1); +} + static void fsck_dir(int i, char *path) { DIR *dir = opendir(path); struct dirent *de; + char name[100]; if (!dir) return; @@ -396,17 +413,13 @@ static void fsck_dir(int i, char *path) if (verbose) fprintf(stderr, "Checking directory %s\n", path); + sprintf(name, "%02x", i); while ((de = readdir(dir)) != NULL) { - char name[100]; unsigned char sha1[20]; if (is_dot_or_dotdot(de->d_name)) continue; - if (strlen(de->d_name) == 38) { - sprintf(name, "%02x", i); - memcpy(name+2, de->d_name, 39); - if (get_sha1_hex(name, sha1) < 0) - break; + if (is_loose_object_file(de, name, sha1)) { add_sha1_list(sha1, DIRENT_SORT_HINT(de)); continue; } @@ -556,8 +569,8 @@ static int fsck_cache_tree(struct cache_tree *it) sha1_to_hex(it->sha1)); return 1; } - mark_object_reachable(obj); obj->used = 1; + mark_object_reachable(obj); if (obj->type != OBJ_TREE) err |= objerror(obj, "non-tree in cache-tree"); } diff --git a/builtin/grep.c b/builtin/grep.c index fdf7131efd..c3af8760cc 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -329,106 +329,6 @@ static int grep_config(const char *var, const char *value, void *cb) return 0; } -/* - * Return non-zero if max_depth is negative or path has no more then max_depth - * slashes. - */ -static int accept_subdir(const char *path, int max_depth) -{ - if (max_depth < 0) - return 1; - - while ((path = strchr(path, '/')) != NULL) { - max_depth--; - if (max_depth < 0) - return 0; - path++; - } - return 1; -} - -/* - * Return non-zero if name is a subdirectory of match and is not too deep. - */ -static int is_subdir(const char *name, int namelen, - const char *match, int matchlen, int max_depth) -{ - if (matchlen > namelen || strncmp(name, match, matchlen)) - return 0; - - if (name[matchlen] == '\0') /* exact match */ - return 1; - - if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/') - return accept_subdir(name + matchlen + 1, max_depth); - - return 0; -} - -/* - * git grep pathspecs are somewhat different from diff-tree pathspecs; - * pathname wildcards are allowed. - */ -static int pathspec_matches(const char **paths, const char *name, int max_depth) -{ - int namelen, i; - if (!paths || !*paths) - return accept_subdir(name, max_depth); - namelen = strlen(name); - for (i = 0; paths[i]; i++) { - const char *match = paths[i]; - int matchlen = strlen(match); - const char *cp, *meta; - - if (is_subdir(name, namelen, match, matchlen, max_depth)) - return 1; - if (!fnmatch(match, name, 0)) - return 1; - if (name[namelen-1] != '/') - continue; - - /* We are being asked if the directory ("name") is worth - * descending into. - * - * Find the longest leading directory name that does - * not have metacharacter in the pathspec; the name - * we are looking at must overlap with that directory. - */ - for (cp = match, meta = NULL; cp - match < matchlen; cp++) { - char ch = *cp; - if (ch == '*' || ch == '[' || ch == '?') { - meta = cp; - break; - } - } - if (!meta) - meta = cp; /* fully literal */ - - if (namelen <= meta - match) { - /* Looking at "Documentation/" and - * the pattern says "Documentation/howto/", or - * "Documentation/diff*.txt". The name we - * have should match prefix. - */ - if (!memcmp(match, name, namelen)) - return 1; - continue; - } - - if (meta - match < namelen) { - /* Looking at "Documentation/howto/" and - * the pattern says "Documentation/h*"; - * match up to "Do.../h"; this avoids descending - * into "Documentation/technical/". - */ - if (!memcmp(match, name, meta - match)) - return 1; - continue; - } - } - return 0; -} - static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { void *data; @@ -581,7 +481,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix) free(argv); } -static int grep_cache(struct grep_opt *opt, const char **paths, int cached) +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached) { int hit = 0; int nr; @@ -591,7 +491,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name, opt->max_depth)) + if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -618,44 +518,29 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) return hit; } -static int grep_tree(struct grep_opt *opt, const char **paths, - struct tree_desc *tree, - const char *tree_name, const char *base) +static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, + struct tree_desc *tree, struct strbuf *base, int tn_len) { - int len; - int hit = 0; + int hit = 0, matched = 0; struct name_entry entry; - char *down; - int tn_len = strlen(tree_name); - struct strbuf pathbuf; - - strbuf_init(&pathbuf, PATH_MAX + tn_len); - - if (tn_len) { - strbuf_add(&pathbuf, tree_name, tn_len); - strbuf_addch(&pathbuf, ':'); - tn_len = pathbuf.len; - } - strbuf_addstr(&pathbuf, base); - len = pathbuf.len; + int old_baselen = base->len; while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(entry.path, entry.sha1); - pathbuf.len = len; - strbuf_add(&pathbuf, entry.path, te_len); - - if (S_ISDIR(entry.mode)) - /* Match "abc/" against pathspec to - * decide if we want to descend into "abc" - * directory. - */ - strbuf_addch(&pathbuf, '/'); - - down = pathbuf.buf + tn_len; - if (!pathspec_matches(paths, down, opt->max_depth)) - ; - else if (S_ISREG(entry.mode)) - hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); + + if (matched != 2) { + matched = tree_entry_interesting(&entry, base, tn_len, pathspec); + if (matched == -1) + break; /* no more matches */ + if (!matched) + continue; + } + + strbuf_add(base, entry.path, te_len); + + if (S_ISREG(entry.mode)) { + hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len); + } else if (S_ISDIR(entry.mode)) { enum object_type type; struct tree_desc sub; @@ -666,18 +551,21 @@ static int grep_tree(struct grep_opt *opt, const char **paths, if (!data) die("unable to read tree (%s)", sha1_to_hex(entry.sha1)); + + strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); - hit |= grep_tree(opt, paths, &sub, tree_name, down); + hit |= grep_tree(opt, pathspec, &sub, base, tn_len); free(data); } + strbuf_setlen(base, old_baselen); + if (hit && opt->status_only) break; } - strbuf_release(&pathbuf); return hit; } -static int grep_object(struct grep_opt *opt, const char **paths, +static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct object *obj, const char *name) { if (obj->type == OBJ_BLOB) @@ -686,20 +574,30 @@ static int grep_object(struct grep_opt *opt, const char **paths, struct tree_desc tree; void *data; unsigned long size; - int hit; + struct strbuf base; + int hit, len; + data = read_object_with_reference(obj->sha1, tree_type, &size, NULL); if (!data) die("unable to read tree (%s)", sha1_to_hex(obj->sha1)); + + len = name ? strlen(name) : 0; + strbuf_init(&base, PATH_MAX + len + 1); + if (len) { + strbuf_add(&base, name, len); + strbuf_addch(&base, ':'); + } init_tree_desc(&tree, data, size); - hit = grep_tree(opt, paths, &tree, name, ""); + hit = grep_tree(opt, pathspec, &tree, &base, base.len); + strbuf_release(&base); free(data); return hit; } die("unable to grep from object of type %s", typename(obj->type)); } -static int grep_objects(struct grep_opt *opt, const char **paths, +static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_array *list) { unsigned int i; @@ -709,7 +607,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths, for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); - if (grep_object(opt, paths, real_obj, list->objects[i].name)) { + if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) { hit = 1; if (opt->status_only) break; @@ -718,7 +616,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths, return hit; } -static int grep_directory(struct grep_opt *opt, const char **paths) +static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec) { struct dir_struct dir; int i, hit = 0; @@ -726,7 +624,7 @@ static int grep_directory(struct grep_opt *opt, const char **paths) memset(&dir, 0, sizeof(dir)); setup_standard_excludes(&dir); - fill_directory(&dir, paths); + fill_directory(&dir, pathspec->raw); for (i = 0; i < dir.nr; i++) { hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) @@ -832,6 +730,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; const char **paths = NULL; + struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; int dummy; @@ -1059,6 +958,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) paths[0] = prefix; paths[1] = NULL; } + init_pathspec(&pathspec, paths); + pathspec.max_depth = opt.max_depth; + pathspec.recursive = 1; if (show_in_pager && (cached || list.nr)) die("--open-files-in-pager only works on the worktree"); @@ -1089,16 +991,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix) die("--cached cannot be used with --no-index."); if (list.nr) die("--no-index cannot be used with revs."); - hit = grep_directory(&opt, paths); + hit = grep_directory(&opt, &pathspec); } else if (!list.nr) { if (!cached) setup_work_tree(); - hit = grep_cache(&opt, paths, cached); + hit = grep_cache(&opt, &pathspec, cached); } else { if (cached) die("both --cached and trees are given."); - hit = grep_objects(&opt, paths, &list); + hit = grep_objects(&opt, &pathspec, &list); } if (use_threads) diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 080af1a01b..c90acddcb2 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -15,7 +15,7 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path struct stat st; unsigned char sha1[20]; if (fstat(fd, &st) < 0 || - index_fd(sha1, fd, &st, write_object, type_from_string(type), path)) + index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1)) die(write_object ? "Unable to add %s to database" : "Unable to hash %s", path); diff --git a/builtin/init-db.c b/builtin/init-db.c index e3af9eaa87..4f5348eec6 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -419,7 +419,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) unsigned int flags = 0; const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, "template-directory", - "provide the directory from which templates will be used"), + "directory from which templates will be used"), OPT_SET_INT(0, "bare", &is_bare_repository_cfg, "create a bare repository", 1), { OPTION_CALLBACK, 0, "shared", &init_shared_repository, diff --git a/builtin/log.c b/builtin/log.c index d8c6c28d2f..f5ed690c43 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -89,7 +89,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->always_show_header = 0; if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { rev->always_show_header = 0; - if (rev->diffopt.nr_paths != 1) + if (rev->diffopt.pathspec.nr != 1) usage("git logs can only follow renames on one pathname at a time"); } for (i = 1; i < argc; i++) { diff --git a/builtin/merge.c b/builtin/merge.c index 42fff387e6..9b8c163c89 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -194,8 +194,8 @@ static struct option builtin_merge_options[] = { "merge strategy to use", option_parse_strategy), OPT_CALLBACK('X', "strategy-option", &xopts, "option=value", "option for selected merge strategy", option_parse_x), - OPT_CALLBACK('m', "message", &merge_msg, "message", - "message to be used for the merge commit (if any)", + OPT_CALLBACK('m', "message", &merge_msg, "MESSAGE", + "merge commit message (for a non-fast-forward merge)", option_parse_message), OPT__VERBOSITY(&verbosity), OPT_BOOLEAN(0, "abort", &abort_current_merge, @@ -582,7 +582,8 @@ static void write_tree_trivial(unsigned char *sha1) die("git write-tree failed to write a tree"); } -int try_merge_command(const char *strategy, struct commit_list *common, +int try_merge_command(const char *strategy, size_t xopts_nr, + const char **xopts, struct commit_list *common, const char *head_arg, struct commit_list *remotes) { const char **args; @@ -680,7 +681,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, rollback_lock_file(lock); return clean ? 0 : 1; } else { - return try_merge_command(strategy, common, head_arg, remoteheads); + return try_merge_command(strategy, xopts_nr, xopts, + common, head_arg, remoteheads); } } @@ -795,6 +797,32 @@ static void add_strategies(const char *string, unsigned attr) } +static void write_merge_msg(void) +{ + int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); + if (fd < 0) + die_errno("Could not open '%s' for writing", + git_path("MERGE_MSG")); + if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len) + die_errno("Could not write to '%s'", git_path("MERGE_MSG")); + close(fd); +} + +static void read_merge_msg(void) +{ + strbuf_reset(&merge_msg); + if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0) + die_errno("Could not read from '%s'", git_path("MERGE_MSG")); +} + +static void run_prepare_commit_msg(void) +{ + write_merge_msg(); + run_hook(get_index_file(), "prepare-commit-msg", + git_path("MERGE_MSG"), "merge", NULL, NULL); + read_merge_msg(); +} + static int merge_trivial(void) { unsigned char result_tree[20], result_commit[20]; @@ -806,6 +834,7 @@ static int merge_trivial(void) parent->next = xmalloc(sizeof(*parent->next)); parent->next->item = remoteheads->item; parent->next->next = NULL; + run_prepare_commit_msg(); commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL); finish(result_commit, "In-index merge"); drop_save(); @@ -835,6 +864,7 @@ static int finish_automerge(struct commit_list *common, } free_commit_list(remoteheads); strbuf_addch(&merge_msg, '\n'); + run_prepare_commit_msg(); commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL); strbuf_addf(&buf, "Merge made by %s.", wt_strategy); finish(result_commit, buf.buf); @@ -1316,14 +1346,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) die_errno("Could not write to '%s'", git_path("MERGE_HEAD")); close(fd); strbuf_addch(&merge_msg, '\n'); - fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); - if (fd < 0) - die_errno("Could not open '%s' for writing", - git_path("MERGE_MSG")); - if (write_in_full(fd, merge_msg.buf, merge_msg.len) != - merge_msg.len) - die_errno("Could not write to '%s'", git_path("MERGE_MSG")); - close(fd); + write_merge_msg(); fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) die_errno("Could not open '%s' for writing", diff --git a/builtin/rerere.c b/builtin/rerere.c index 642bf35587..67cbfeb152 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -8,7 +8,7 @@ #include "xdiff-interface.h" static const char * const rerere_usage[] = { - "git rerere [clear | status | diff | gc]", + "git rerere [clear | status | remaining | diff | gc]", NULL, }; @@ -156,7 +156,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[0], "status")) for (i = 0; i < merge_rr.nr; i++) printf("%s\n", merge_rr.items[i].string); - else if (!strcmp(argv[0], "diff")) + else if (!strcmp(argv[0], "remaining")) { + rerere_remaining(&merge_rr); + for (i = 0; i < merge_rr.nr; i++) { + if (merge_rr.items[i].util != RERERE_RESOLVED) + printf("%s\n", merge_rr.items[i].string); + else + /* prepare for later call to + * string_list_clear() */ + merge_rr.items[i].util = NULL; + } + } else if (!strcmp(argv[0], "diff")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff --git a/builtin/revert.c b/builtin/revert.c index bb6e9e83b7..dc1b702edc 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -44,7 +44,11 @@ static const char **commit_argv; static int allow_rerere_auto; static const char *me; + +/* Merge strategy. */ static const char *strategy; +static const char **xopts; +static size_t xopts_nr, xopts_alloc; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void) return action == REVERT ? revert_usage : cherry_pick_usage; } +static int option_parse_x(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + return 0; + + ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); + xopts[xopts_nr++] = xstrdup(arg); + return 0; +} + static void parse_args(int argc, const char **argv) { const char * const * usage_str = revert_or_cherry_pick_usage(); @@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv) OPT_INTEGER('m', "mainline", &mainline, "parent number"), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"), + OPT_CALLBACK('X', "strategy-option", &xopts, "option", + "option for merge strategy", option_parse_x), OPT_END(), OPT_END(), OPT_END(), @@ -311,18 +328,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next, struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; int clean, index_fd; + const char **xopt; static struct lock_file index_lock; index_fd = hold_locked_index(&index_lock, 1); read_cache(); - /* - * NEEDSWORK: cherry-picking between branches with - * different end-of-line normalization is a pain; - * plumb in an option to set o.renormalize? - * (or better: arbitrary -X options) - */ init_merge_options(&o); o.ancestor = base ? base_label : "(empty tree)"; o.branch1 = "HEAD"; @@ -332,6 +344,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next, next_tree = next ? next->tree : empty_tree(); base_tree = base ? base->tree : empty_tree(); + for (xopt = xopts; xopt != xopts + xopts_nr; xopt++) + parse_merge_opt(&o, *xopt); + clean = merge_trees(&o, head_tree, next_tree, base_tree, &result); @@ -503,7 +518,7 @@ static int do_pick_commit(void) commit_list_insert(base, &common); commit_list_insert(next, &remotes); - res = try_merge_command(strategy, common, + res = try_merge_command(strategy, xopts_nr, xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); diff --git a/builtin/tag.c b/builtin/tag.c index aa1f87d47a..246a2bc72b 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -376,9 +376,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_GROUP("Tag creation options"), OPT_BOOLEAN('a', NULL, &annotate, "annotated tag, needs a message"), - OPT_CALLBACK('m', NULL, &msg, "msg", - "message for the tag", parse_msg_arg), - OPT_FILENAME('F', NULL, &msgfile, "message in a file"), + OPT_CALLBACK('m', NULL, &msg, "MESSAGE", + "tag message", parse_msg_arg), + OPT_FILENAME('F', NULL, &msgfile, "read message from file"), OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"), OPT_STRING('u', NULL, &keyid, "key-id", "use another key to sign the tag"), diff --git a/builtin/update-index.c b/builtin/update-index.c index 56baf27fb7..d7850c6309 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -546,7 +546,10 @@ static int do_reupdate(int ac, const char **av, */ int pos; int has_head = 1; - const char **pathspec = get_pathspec(prefix, av + 1); + const char **paths = get_pathspec(prefix, av + 1); + struct pathspec pathspec; + + init_pathspec(&pathspec, paths); if (read_ref("HEAD", head_sha1)) /* If there is no HEAD, that means it is an initial @@ -559,7 +562,7 @@ static int do_reupdate(int ac, const char **av, struct cache_entry *old = NULL; int save_nr; - if (ce_stage(ce) || !ce_path_match(ce, pathspec)) + if (ce_stage(ce) || !ce_path_match(ce, &pathspec)) continue; if (has_head) old = read_one_ent(NULL, head_sha1, @@ -578,6 +581,7 @@ static int do_reupdate(int ac, const char **av, if (save_nr != active_nr) goto redo; } + free_pathspec(&pathspec); return 0; } |