]> git.ipfire.org Git - thirdparty/git.git/blobdiff - wt-status.c
object-name.h: move declarations for object-name.c functions from cache.h
[thirdparty/git.git] / wt-status.c
index e4f29b2b4c9f7493076ee904f4c5e2b476841d6c..97b9c1c035974c269f5b9d042155e196d8193b10 100644 (file)
@@ -1,9 +1,14 @@
 #include "cache.h"
+#include "advice.h"
 #include "wt-status.h"
 #include "object.h"
 #include "dir.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "quote.h"
 #include "refs.h"
 #include "submodule.h"
 #include "column.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "trace.h"
+#include "trace2.h"
 #include "utf8.h"
 #include "worktree.h"
 #include "lockfile.h"
 #include "sequencer.h"
+#include "fsmonitor-settings.h"
 
 #define AB_DELAY_WARNING_IN_MS (2 * 1000)
+#define UF_DELAY_WARNING_IN_MS (2 * 1000)
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -438,7 +448,7 @@ static char short_submodule_status(struct wt_status_change_data *d)
 }
 
 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
-                                        struct diff_options *options,
+                                        struct diff_options *options UNUSED,
                                         void *data)
 {
        struct wt_status *s = data;
@@ -525,7 +535,7 @@ static int unmerged_mask(struct index_state *istate, const char *path)
 }
 
 static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
-                                        struct diff_options *options,
+                                        struct diff_options *options UNUSED,
                                         void *data)
 {
        struct wt_status *s = data;
@@ -616,7 +626,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
        rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
        copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_files(&rev, 0);
-       clear_pathspec(&rev.prune_data);
+       release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
@@ -651,10 +661,18 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
        rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
        rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
+
+       /*
+        * The `recursive` option must be enabled to allow the diff to recurse
+        * into subdirectories of sparse directory index entries. If it is not
+        * enabled, a subdirectory containing file(s) with changes is reported
+        * as "modified", rather than the modified files themselves.
+        */
+       rev.diffopt.flags.recursive = 1;
+
        copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_index(&rev, 1);
-       object_array_clear(&rev.pending);
-       clear_pathspec(&rev.prune_data);
+       release_revisions(&rev);
 }
 
 static int add_file_to_list(const struct object_id *oid,
@@ -939,20 +957,28 @@ static void wt_longstatus_print_changed(struct wt_status *s)
        wt_longstatus_print_trailer(s);
 }
 
-static int stash_count_refs(struct object_id *ooid, struct object_id *noid,
-                           const char *email, timestamp_t timestamp, int tz,
-                           const char *message, void *cb_data)
+static int stash_count_refs(struct object_id *ooid UNUSED,
+                           struct object_id *noid UNUSED,
+                           const char *email UNUSED,
+                           timestamp_t timestamp UNUSED, int tz UNUSED,
+                           const char *message UNUSED, void *cb_data)
 {
        int *c = cb_data;
        (*c)++;
        return 0;
 }
 
+static int count_stash_entries(void)
+{
+       int n = 0;
+       for_each_reflog_ent("refs/stash", stash_count_refs, &n);
+       return n;
+}
+
 static void wt_longstatus_print_stash_summary(struct wt_status *s)
 {
-       int stash_count = 0;
+       int stash_count = count_stash_entries();
 
-       for_each_reflog_ent("refs/stash", stash_count_refs, &stash_count);
        if (stash_count > 0)
                status_printf_ln(s, GIT_COLOR_NORMAL,
                                 Q_("Your stash currently has %d entry",
@@ -967,7 +993,7 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom
        struct strbuf summary = STRBUF_INIT;
        char *summary_content;
 
-       strvec_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s", s->index_file);
+       strvec_pushf(&sm_summary.env, "GIT_INDEX_FILE=%s", s->index_file);
 
        strvec_push(&sm_summary.args, "submodule");
        strvec_push(&sm_summary.args, "summary");
@@ -1137,6 +1163,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
                rev.diffopt.b_prefix = "w/";
                run_diff_files(&rev, 0);
        }
+       release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
@@ -1188,6 +1215,13 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
        strbuf_release(&sb);
 }
 
+static int uf_was_slow(struct wt_status *s)
+{
+       if (getenv("GIT_TEST_UF_DELAY_WARNING"))
+               s->untracked_in_ms = 3250;
+       return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
+}
+
 static void show_merge_in_progress(struct wt_status *s,
                                   const char *color)
 {
@@ -1212,17 +1246,23 @@ static void show_merge_in_progress(struct wt_status *s,
 static void show_am_in_progress(struct wt_status *s,
                                const char *color)
 {
+       int am_empty_patch;
+
        status_printf_ln(s, color,
                _("You are in the middle of an am session."));
        if (s->state.am_empty_patch)
                status_printf_ln(s, color,
                        _("The current patch is empty."));
        if (s->hints) {
-               if (!s->state.am_empty_patch)
+               am_empty_patch = s->state.am_empty_patch;
+               if (!am_empty_patch)
                        status_printf_ln(s, color,
                                _("  (fix conflicts and then run \"git am --continue\")"));
                status_printf_ln(s, color,
                        _("  (use \"git am --skip\" to skip this patch)"));
+               if (am_empty_patch)
+                       status_printf_ln(s, color,
+                               _("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
                status_printf_ln(s, color,
                        _("  (use \"git am --abort\" to restore the original branch)"));
        }
@@ -1305,7 +1345,7 @@ static void abbrev_oid_in_line(struct strbuf *line)
                 * it after abbreviation.
                 */
                strbuf_trim(split[1]);
-               if (!get_oid(split[1]->buf, &oid)) {
+               if (!repo_get_oid(the_repository, split[1]->buf, &oid)) {
                        strbuf_reset(split[1]);
                        strbuf_add_unique_abbrev(split[1], &oid,
                                                 DEFAULT_ABBREV);
@@ -1362,10 +1402,10 @@ static void show_rebase_information(struct wt_status *s,
                        status_printf_ln(s, color, _("No commands done."));
                else {
                        status_printf_ln(s, color,
-                               Q_("Last command done (%d command done):",
-                                       "Last commands done (%d commands done):",
+                               Q_("Last command done (%"PRIuMAX" command done):",
+                                       "Last commands done (%"PRIuMAX" commands done):",
                                        have_done.nr),
-                               have_done.nr);
+                               (uintmax_t)have_done.nr);
                        for (i = (have_done.nr > nr_lines_to_show)
                                ? have_done.nr - nr_lines_to_show : 0;
                                i < have_done.nr;
@@ -1381,10 +1421,10 @@ static void show_rebase_information(struct wt_status *s,
                                         _("No commands remaining."));
                else {
                        status_printf_ln(s, color,
-                               Q_("Next command to do (%d remaining command):",
-                                       "Next commands to do (%d remaining commands):",
+                               Q_("Next command to do (%"PRIuMAX" remaining command):",
+                                       "Next commands to do (%"PRIuMAX" remaining commands):",
                                        yet_to_do.nr),
-                               yet_to_do.nr);
+                               (uintmax_t)yet_to_do.nr);
                        for (i = 0; i < nr_lines_to_show && i < yet_to_do.nr; i++)
                                status_printf_ln(s, color, "   %s", yet_to_do.items[i].string);
                        if (s->hints)
@@ -1471,8 +1511,8 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
        else
                status_printf_ln(s, color,
                        _("You are currently cherry-picking commit %s."),
-                       find_unique_abbrev(&s->state.cherry_pick_head_oid,
-                                          DEFAULT_ABBREV));
+                       repo_find_unique_abbrev(the_repository, &s->state.cherry_pick_head_oid,
+                                               DEFAULT_ABBREV));
 
        if (s->hints) {
                if (has_unmerged(s))
@@ -1501,8 +1541,8 @@ static void show_revert_in_progress(struct wt_status *s,
        else
                status_printf_ln(s, color,
                        _("You are currently reverting commit %s."),
-                       find_unique_abbrev(&s->state.revert_head_oid,
-                                          DEFAULT_ABBREV));
+                       repo_find_unique_abbrev(the_repository, &s->state.revert_head_oid,
+                                               DEFAULT_ABBREV));
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
@@ -1591,8 +1631,10 @@ struct grab_1st_switch_cbdata {
        struct object_id noid;
 };
 
-static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-                          const char *email, timestamp_t timestamp, int tz,
+static int grab_1st_switch(struct object_id *ooid UNUSED,
+                          struct object_id *noid,
+                          const char *email UNUSED,
+                          timestamp_t timestamp UNUSED, int tz UNUSED,
                           const char *message, void *cb_data)
 {
        struct grab_1st_switch_cbdata *cb = cb_data;
@@ -1630,7 +1672,8 @@ static void wt_status_get_detached_from(struct repository *r,
                return;
        }
 
-       if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref, 1) == 1 &&
+       if (repo_dwim_ref(r, cb.buf.buf, cb.buf.len, &oid, &ref,
+                         1) == 1 &&
            /* oid is a commit? match without further lookup */
            (oideq(&cb.noid, &oid) ||
             /* perhaps oid is a tag, try to dereference to a commit */
@@ -1642,9 +1685,9 @@ static void wt_status_get_detached_from(struct repository *r,
                state->detached_from = xstrdup(from);
        } else
                state->detached_from =
-                       xstrdup(find_unique_abbrev(&cb.noid, DEFAULT_ABBREV));
+                       xstrdup(repo_find_unique_abbrev(r, &cb.noid, DEFAULT_ABBREV));
        oidcpy(&state->detached_oid, &cb.noid);
-       state->detached_at = !get_oid("HEAD", &oid) &&
+       state->detached_at = !repo_get_oid(r, "HEAD", &oid) &&
                             oideq(&oid, &state->detached_oid);
 
        free(ref);
@@ -1735,13 +1778,13 @@ void wt_status_get_state(struct repository *r,
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
        } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
-                  !get_oid("CHERRY_PICK_HEAD", &oid)) {
+                  !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
                oidcpy(&state->cherry_pick_head_oid, &oid);
        }
        wt_status_check_bisect(NULL, state);
        if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
-           !get_oid("REVERT_HEAD", &oid)) {
+           !repo_get_oid(r, "REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
                oidcpy(&state->revert_head_oid, &oid);
        }
@@ -1789,6 +1832,7 @@ static void wt_longstatus_print(struct wt_status *s)
 {
        const char *branch_color = color(WT_STATUS_ONBRANCH, s);
        const char *branch_status_color = color(WT_STATUS_HEADER, s);
+       enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
 
        if (s->branch) {
                const char *on_what = _("On branch ");
@@ -1845,13 +1889,21 @@ static void wt_longstatus_print(struct wt_status *s)
                wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
                if (s->show_ignored_mode)
                        wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
-               if (advice_enabled(ADVICE_STATUS_U_OPTION) && 2000 < s->untracked_in_ms) {
+               if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
                        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
+                       if (fsm_mode > FSMONITOR_MODE_DISABLED) {
+                               status_printf_ln(s, GIT_COLOR_NORMAL,
+                                               _("It took %.2f seconds to enumerate untracked files,\n"
+                                               "but the results were cached, and subsequent runs may be faster."),
+                                               s->untracked_in_ms / 1000.0);
+                       } else {
+                               status_printf_ln(s, GIT_COLOR_NORMAL,
+                                               _("It took %.2f seconds to enumerate untracked files."),
+                                               s->untracked_in_ms / 1000.0);
+                       }
                        status_printf_ln(s, GIT_COLOR_NORMAL,
-                                        _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-                                          "may speed it up, but you have to be careful not to forget to add\n"
-                                          "new files yourself (see 'git help status')."),
-                                        s->untracked_in_ms / 1000.0);
+                                       _("See 'git help status' for information on how to improve this."));
+                       status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
                }
        } else if (s->committable)
                status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
@@ -2176,6 +2228,18 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
        }
 }
 
+/*
+ * Print the stash count in a porcelain-friendly format
+ */
+static void wt_porcelain_v2_print_stash(struct wt_status *s)
+{
+       int stash_count = count_stash_entries();
+       char eol = s->null_termination ? '\0' : '\n';
+
+       if (stash_count > 0)
+               fprintf(s->fp, "# stash %d%c", stash_count, eol);
+}
+
 /*
  * Convert various submodule status values into a
  * fixed-length string of characters in the buffer provided.
@@ -2437,6 +2501,9 @@ static void wt_porcelain_v2_print(struct wt_status *s)
        if (s->show_branch)
                wt_porcelain_v2_print_tracking(s);
 
+       if (s->show_stash)
+               wt_porcelain_v2_print_stash(s);
+
        for (i = 0; i < s->change.nr; i++) {
                it = &(s->change.items[i]);
                d = it->util;
@@ -2509,7 +2576,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
        rev_info.diffopt.flags.quick = 1;
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_files(&rev_info, 0);
-       return diff_result_code(&rev_info.diffopt, result);
+       result = diff_result_code(&rev_info.diffopt, result);
+       release_revisions(&rev_info);
+       return result;
 }
 
 /**
@@ -2541,8 +2610,9 @@ int has_uncommitted_changes(struct repository *r,
 
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_index(&rev_info, 1);
-       object_array_clear(&rev_info.pending);
-       return diff_result_code(&rev_info.diffopt, result);
+       result = diff_result_code(&rev_info.diffopt, result);
+       release_revisions(&rev_info);
+       return result;
 }
 
 /**