Merge branch 'cb/openbsd-allows-reading-directory'
[gitweb.git] / builtin / commit.c
index 37fcb55ab0a03a5fdabaca1913bc700201fd8e10..004b816635bf1628bfe7697f737a22cbcd27622b 100644 (file)
@@ -32,6 +32,9 @@
 #include "column.h"
 #include "sequencer.h"
 #include "mailmap.h"
+#include "help.h"
+#include "commit-reach.h"
+#include "commit-graph.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [<options>] [--] <pathspec>..."),
@@ -66,6 +69,18 @@ N_("If you wish to skip this commit, use:\n"
 "Then \"git cherry-pick --continue\" will resume cherry-picking\n"
 "the remaining commits.\n");
 
+static const char *color_status_slots[] = {
+       [WT_STATUS_HEADER]        = "header",
+       [WT_STATUS_UPDATED]       = "updated",
+       [WT_STATUS_CHANGED]       = "changed",
+       [WT_STATUS_UNTRACKED]     = "untracked",
+       [WT_STATUS_NOBRANCH]      = "noBranch",
+       [WT_STATUS_UNMERGED]      = "unmerged",
+       [WT_STATUS_LOCAL_BRANCH]  = "localBranch",
+       [WT_STATUS_REMOTE_BRANCH] = "remoteBranch",
+       [WT_STATUS_ONBRANCH]      = "branch",
+};
+
 static const char *use_message_buffer;
 static struct lock_file index_lock; /* real index */
 static struct lock_file false_lock; /* used only for partial commits */
@@ -143,11 +158,24 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset)
+{
+       const char **value = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+
+       if (arg != NULL && *arg == '=')
+               arg = arg + 1;
+
+       *value = arg;
+       return 0;
+}
+
 static void determine_whence(struct wt_status *s)
 {
-       if (file_exists(git_path_merge_head()))
+       if (file_exists(git_path_merge_head(the_repository)))
                whence = FROM_MERGE;
-       else if (file_exists(git_path_cherry_pick_head())) {
+       else if (file_exists(git_path_cherry_pick_head(the_repository))) {
                whence = FROM_CHERRY_PICK;
                if (file_exists(git_path_seq_dir()))
                        sequencer_in_use = 1;
@@ -160,10 +188,10 @@ static void determine_whence(struct wt_status *s)
 
 static void status_init_config(struct wt_status *s, config_fn_t fn)
 {
-       wt_status_prepare(s);
+       wt_status_prepare(the_repository, s);
+       init_diff_ui_defaults();
        git_config(fn, s);
        determine_whence(s);
-       init_diff_ui_defaults();
        s->hints = advice_status_hints; /* must come after git_config() */
 }
 
@@ -218,8 +246,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
        if (with_tree) {
                char *max_prefix = common_prefix(pattern);
-               overlay_tree_on_index(&the_index, with_tree,
-                                     max_prefix ? max_prefix : prefix);
+               overlay_tree_on_index(&the_index, with_tree, max_prefix);
                free(max_prefix);
        }
 
@@ -229,7 +256,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
                if (ce->ce_flags & CE_UPDATE)
                        continue;
-               if (!ce_path_match(ce, pattern, m))
+               if (!ce_path_match(&the_index, ce, pattern, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@ -484,8 +511,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 
        wt_status_collect(s);
        wt_status_print(s);
+       wt_status_collect_free_buffers(s);
 
-       return s->commitable;
+       return s->committable;
 }
 
 static int is_a_merge(const struct commit *current_head)
@@ -496,7 +524,7 @@ static int is_a_merge(const struct commit *current_head)
 static void assert_split_ident(struct ident_split *id, const struct strbuf *buf)
 {
        if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin)
-               die("BUG: unable to parse our own ident: %s", buf->buf);
+               BUG("unable to parse our own ident: %s", buf->buf);
 }
 
 static void export_one(const char *var, const char *s, const char *e, int hack)
@@ -631,7 +659,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 {
        struct stat statbuf;
        struct strbuf committer_ident = STRBUF_INIT;
-       int commitable;
+       int committable;
        struct strbuf sb = STRBUF_INIT;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
@@ -696,21 +724,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (have_option_m)
                        strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
-       } else if (!stat(git_path_merge_msg(), &statbuf)) {
+       } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
                /*
                 * prepend SQUASH_MSG here if it exists and a
                 * "merge --squash" was originally performed
                 */
-               if (!stat(git_path_squash_msg(), &statbuf)) {
-                       if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
+               if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                                die_errno(_("could not read SQUASH_MSG"));
                        hook_arg1 = "squash";
                } else
                        hook_arg1 = "merge";
-               if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0)
+               if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
-       } else if (!stat(git_path_squash_msg(), &statbuf)) {
-               if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
+       } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
+               if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
                hook_arg1 = "squash";
        } else if (template_file) {
@@ -791,8 +819,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                        "       %s\n"
                                        "and try again.\n"),
                                whence == FROM_MERGE ?
-                                       git_path_merge_head() :
-                                       git_path_cherry_pick_head());
+                                       git_path_merge_head(the_repository) :
+                                       git_path_cherry_pick_head(the_repository));
                }
 
                fprintf(s->fp, "\n");
@@ -848,8 +876,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
                saved_color_setting = s->use_color;
                s->use_color = 0;
-               commitable = run_status(s->fp, index_file, prefix, 1, s);
+               committable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
+               string_list_clear(&s->change, 1);
        } else {
                struct object_id oid;
                const char *parent = "HEAD";
@@ -866,7 +895,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        for (i = 0; i < active_nr; i++)
                                if (ce_intent_to_add(active_cache[i]))
                                        ita_nr++;
-                       commitable = active_nr - ita_nr > 0;
+                       committable = active_nr - ita_nr > 0;
                } else {
                        /*
                         * Unless the user did explicitly request a submodule
@@ -882,7 +911,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        if (ignore_submodule_arg &&
                            !strcmp(ignore_submodule_arg, "all"))
                                flags.ignore_submodules = 1;
-                       commitable = index_differs_from(parent, &flags, 1);
+                       committable = index_differs_from(the_repository,
+                                                        parent, &flags, 1);
                }
        }
        strbuf_release(&committer_ident);
@@ -894,7 +924,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
         * explicit --allow-empty. In the cherry-pick case, it may be
         * empty due to conflict resolution, which the user should okay.
         */
-       if (!commitable && whence != FROM_MERGE && !allow_empty &&
+       if (!committable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(current_head))) {
                s->display_comment_prefix = old_display_comment_prefix;
                run_status(stdout, index_file, prefix, 0, s);
@@ -958,7 +988,7 @@ static const char *find_author_by_nickname(const char *name)
        const char *av[20];
        int ac = 0;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        strbuf_addf(&buf, "--author=%s", name);
        av[++ac] = "--all";
        av[++ac] = "-i";
@@ -1164,37 +1194,24 @@ static int parse_and_validate_options(int argc, const char *argv[],
 static int dry_run_commit(int argc, const char **argv, const char *prefix,
                          const struct commit *current_head, struct wt_status *s)
 {
-       int commitable;
+       int committable;
        const char *index_file;
 
        index_file = prepare_index(argc, argv, prefix, current_head, 1);
-       commitable = run_status(stdout, index_file, prefix, 0, s);
+       committable = run_status(stdout, index_file, prefix, 0, s);
        rollback_index_files();
 
-       return commitable ? 0 : 1;
+       return committable ? 0 : 1;
 }
 
+define_list_config_array_extra(color_status_slots, {"added"});
+
 static int parse_status_slot(const char *slot)
 {
-       if (!strcasecmp(slot, "header"))
-               return WT_STATUS_HEADER;
-       if (!strcasecmp(slot, "branch"))
-               return WT_STATUS_ONBRANCH;
-       if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
+       if (!strcasecmp(slot, "added"))
                return WT_STATUS_UPDATED;
-       if (!strcasecmp(slot, "changed"))
-               return WT_STATUS_CHANGED;
-       if (!strcasecmp(slot, "untracked"))
-               return WT_STATUS_UNTRACKED;
-       if (!strcasecmp(slot, "nobranch"))
-               return WT_STATUS_NOBRANCH;
-       if (!strcasecmp(slot, "unmerged"))
-               return WT_STATUS_UNMERGED;
-       if (!strcasecmp(slot, "localBranch"))
-               return WT_STATUS_LOCAL_BRANCH;
-       if (!strcasecmp(slot, "remoteBranch"))
-               return WT_STATUS_REMOTE_BRANCH;
-       return -1;
+
+       return LOOKUP_CONFIG(color_status_slots, slot);
 }
 
 static int git_status_config(const char *k, const char *v, void *cb)
@@ -1260,12 +1277,33 @@ static int git_status_config(const char *k, const char *v, void *cb)
                        return error(_("Invalid untracked files mode '%s'"), v);
                return 0;
        }
+       if (!strcmp(k, "diff.renamelimit")) {
+               if (s->rename_limit == -1)
+                       s->rename_limit = git_config_int(k, v);
+               return 0;
+       }
+       if (!strcmp(k, "status.renamelimit")) {
+               s->rename_limit = git_config_int(k, v);
+               return 0;
+       }
+       if (!strcmp(k, "diff.renames")) {
+               if (s->detect_rename == -1)
+                       s->detect_rename = git_config_rename(k, v);
+               return 0;
+       }
+       if (!strcmp(k, "status.renames")) {
+               s->detect_rename = git_config_rename(k, v);
+               return 0;
+       }
        return git_diff_ui_config(k, v, NULL);
 }
 
 int cmd_status(int argc, const char **argv, const char *prefix)
 {
+       static int no_renames = -1;
+       static const char *rename_score_arg = (const char *)-1;
        static struct wt_status s;
+       unsigned int progress_flag = 0;
        int fd;
        struct object_id oid;
        static struct option builtin_status_options[] = {
@@ -1298,6 +1336,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                  N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
                OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
+               OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
+               { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg,
+                 N_("n"), N_("detect renames, optionally set similarity index"),
+                 PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score },
                OPT_END(),
        };
 
@@ -1322,8 +1364,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                       PATHSPEC_PREFER_FULL,
                       prefix, argv);
 
-       read_cache_preload(&s.pathspec);
-       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
+       if (status_format != STATUS_FORMAT_PORCELAIN &&
+           status_format != STATUS_FORMAT_PORCELAIN_V2)
+               progress_flag = REFRESH_PROGRESS;
+       read_index(&the_index);
+       refresh_index(&the_index,
+                     REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
+                     &s.pathspec, NULL, NULL);
 
        if (use_optional_locks())
                fd = hold_locked_index(&index_lock, 0);
@@ -1337,6 +1384,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        s.ignore_submodule_arg = ignore_submodule_arg;
        s.status_format = status_format;
        s.verbose = verbose;
+       if (no_renames != -1)
+               s.detect_rename = !no_renames;
+       if ((intptr_t)rename_score_arg != -1) {
+               if (s.detect_rename < DIFF_DETECT_RENAME)
+                       s.detect_rename = DIFF_DETECT_RENAME;
+               if (rename_score_arg)
+                       s.rename_score = parse_rename_score(&rename_score_arg);
+       }
 
        wt_status_collect(&s);
 
@@ -1347,6 +1402,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                s.prefix = prefix;
 
        wt_status_print(&s);
+       wt_status_collect_free_buffers(&s);
+
        return 0;
 }
 
@@ -1523,7 +1580,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (!reflog_msg)
                        reflog_msg = "commit (merge)";
                pptr = commit_list_append(current_head, pptr);
-               fp = xfopen(git_path_merge_head(), "r");
+               fp = xfopen(git_path_merge_head(the_repository), "r");
                while (strbuf_getline_lf(&m, fp) != EOF) {
                        struct commit *parent;
 
@@ -1534,8 +1591,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                }
                fclose(fp);
                strbuf_release(&m);
-               if (!stat(git_path_merge_mode(), &statbuf)) {
-                       if (strbuf_read_file(&sb, git_path_merge_mode(), 0) < 0)
+               if (!stat(git_path_merge_mode(the_repository), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path_merge_mode(the_repository), 0) < 0)
                                die_errno(_("could not read MERGE_MODE"));
                        if (!strcmp(sb.buf, "no-ff"))
                                allow_fast_forward = 0;
@@ -1598,19 +1655,22 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                die("%s", err.buf);
        }
 
-       unlink(git_path_cherry_pick_head());
-       unlink(git_path_revert_head());
-       unlink(git_path_merge_head());
-       unlink(git_path_merge_msg());
-       unlink(git_path_merge_mode());
-       unlink(git_path_squash_msg());
+       unlink(git_path_cherry_pick_head(the_repository));
+       unlink(git_path_revert_head(the_repository));
+       unlink(git_path_merge_head(the_repository));
+       unlink(git_path_merge_msg(the_repository));
+       unlink(git_path_merge_mode(the_repository));
+       unlink(git_path_squash_msg(the_repository));
 
        if (commit_index_files())
-               die (_("Repository has been updated, but unable to write\n"
-                    "new_index file. Check that disk is not full and quota is\n"
-                    "not exceeded, and then \"git reset HEAD\" to recover."));
+               die(_("repository has been updated, but unable to write\n"
+                     "new_index file. Check that disk is not full and quota is\n"
+                     "not exceeded, and then \"git reset HEAD\" to recover."));
+
+       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
+               write_commit_graph_reachable(get_object_directory(), 0, 0);
 
-       rerere(0);
+       repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
        if (amend && !no_post_rewrite) {
@@ -1623,7 +1683,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                        flags |= SUMMARY_INITIAL_COMMIT;
                if (author_date_is_interesting())
                        flags |= SUMMARY_SHOW_AUTHOR_DATE;
-               print_commit_summary(prefix, &oid, flags);
+               print_commit_summary(the_repository, prefix,
+                                    &oid, flags);
        }
 
        UNLEAK(err);