Allow the test suite to pass in a directory whose name contains spaces
[gitweb.git] / wt-status.c
index a2e9d332d8332bb1dbeea26b683c5e3f032a822e..068de38b510f51ba216e7bfdfe969c526064aef7 100644 (file)
@@ -121,7 +121,7 @@ static void status_printf_more(struct wt_status *s, const char *color,
 
 void wt_status_prepare(struct wt_status *s)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        memset(s, 0, sizeof(*s));
        memcpy(s->color_palette, default_wt_status_colors,
@@ -129,7 +129,7 @@ void wt_status_prepare(struct wt_status *s)
        s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        s->use_color = -1;
        s->relative_paths = 1;
-       s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
+       s->branch = resolve_refdup("HEAD", 0, oid.hash, NULL);
        s->reference = "HEAD";
        s->fp = stdout;
        s->index_file = get_index_file();
@@ -407,6 +407,16 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
        strbuf_release(&twobuf);
 }
 
+static char short_submodule_status(struct wt_status_change_data *d) {
+       if (d->new_submodule_commits)
+               return 'M';
+       if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+               return 'm';
+       if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
+               return '?';
+       return d->worktree_status;
+}
+
 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
                                         struct diff_options *options,
                                         void *data)
@@ -431,10 +441,13 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
                }
                if (!d->worktree_status)
                        d->worktree_status = p->status;
-               d->dirty_submodule = p->two->dirty_submodule;
-               if (S_ISGITLINK(p->two->mode))
+               if (S_ISGITLINK(p->two->mode)) {
+                       d->dirty_submodule = p->two->dirty_submodule;
                        d->new_submodule_commits = !!oidcmp(&p->one->oid,
                                                            &p->two->oid);
+                       if (s->status_format == STATUS_FORMAT_SHORT)
+                               d->worktree_status = short_submodule_status(d);
+               }
 
                switch (p->status) {
                case DIFF_STATUS_ADDED:
@@ -628,7 +641,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
                        d->index_status = DIFF_STATUS_ADDED;
                        /* Leave {mode,oid}_head zero for adds. */
                        d->mode_index = ce->ce_mode;
-                       hashcpy(d->oid_index.hash, ce->oid.hash);
+                       oidcpy(&d->oid_index, &ce->oid);
                }
        }
 }
@@ -883,17 +896,18 @@ static void wt_longstatus_print_other(struct wt_status *s,
        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
 }
 
-void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
+size_t wt_status_locate_end(const char *s, size_t len)
 {
        const char *p;
        struct strbuf pattern = STRBUF_INIT;
 
        strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
-       if (starts_with(buf->buf, pattern.buf + 1))
-               strbuf_setlen(buf, 0);
-       else if ((p = strstr(buf->buf, pattern.buf)))
-               strbuf_setlen(buf, p - buf->buf + 1);
+       if (starts_with(s, pattern.buf + 1))
+               len = 0;
+       else if ((p = strstr(s, pattern.buf)))
+               len = p - s + 1;
        strbuf_release(&pattern);
+       return len;
 }
 
 void wt_status_add_cut_line(FILE *fp)
@@ -1069,29 +1083,29 @@ static char *read_line_from_git_path(const char *filename)
 static int split_commit_in_progress(struct wt_status *s)
 {
        int split_in_progress = 0;
-       char *head = read_line_from_git_path("HEAD");
-       char *orig_head = read_line_from_git_path("ORIG_HEAD");
-       char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
-       char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
+       char *head, *orig_head, *rebase_amend, *rebase_orig_head;
 
-       if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
+       if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
            !s->branch || strcmp(s->branch, "HEAD"))
-               return split_in_progress;
+               return 0;
 
-       if (!strcmp(rebase_amend, rebase_orig_head)) {
-               if (strcmp(head, rebase_amend))
-                       split_in_progress = 1;
-       } else if (strcmp(orig_head, rebase_orig_head)) {
-               split_in_progress = 1;
-       }
+       head = read_line_from_git_path("HEAD");
+       orig_head = read_line_from_git_path("ORIG_HEAD");
+       rebase_amend = read_line_from_git_path("rebase-merge/amend");
+       rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
 
-       if (!s->amend && !s->nowarn && !s->workdir_dirty)
-               split_in_progress = 0;
+       if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+               ; /* fall through, no split in progress */
+       else if (!strcmp(rebase_amend, rebase_orig_head))
+               split_in_progress = !!strcmp(head, rebase_amend);
+       else if (strcmp(orig_head, rebase_orig_head))
+               split_in_progress = 1;
 
        free(head);
        free(orig_head);
        free(rebase_amend);
        free(rebase_orig_head);
+
        return split_in_progress;
 }
 
@@ -1115,16 +1129,16 @@ static void abbrev_sha1_in_line(struct strbuf *line)
 
        split = strbuf_split_max(line, ' ', 3);
        if (split[0] && split[1]) {
-               unsigned char sha1[20];
+               struct object_id oid;
 
                /*
                 * strbuf_split_max left a space. Trim it and re-add
                 * it after abbreviation.
                 */
                strbuf_trim(split[1]);
-               if (!get_sha1(split[1]->buf, sha1)) {
+               if (!get_oid(split[1]->buf, &oid)) {
                        strbuf_reset(split[1]);
-                       strbuf_add_unique_abbrev(split[1], sha1,
+                       strbuf_add_unique_abbrev(split[1], oid.hash,
                                                 DEFAULT_ABBREV);
                        strbuf_addch(split[1], ' ');
                        strbuf_reset(line);
@@ -1135,14 +1149,17 @@ static void abbrev_sha1_in_line(struct strbuf *line)
        strbuf_list_free(split);
 }
 
-static void read_rebase_todolist(const char *fname, struct string_list *lines)
+static int read_rebase_todolist(const char *fname, struct string_list *lines)
 {
        struct strbuf line = STRBUF_INIT;
        FILE *f = fopen(git_path("%s", fname), "r");
 
-       if (!f)
+       if (!f) {
+               if (errno == ENOENT)
+                       return -1;
                die_errno("Could not open file %s for reading",
                          git_path("%s", fname));
+       }
        while (!strbuf_getline_lf(&line, f)) {
                if (line.len && line.buf[0] == comment_line_char)
                        continue;
@@ -1152,6 +1169,8 @@ static void read_rebase_todolist(const char *fname, struct string_list *lines)
                abbrev_sha1_in_line(&line);
                string_list_append(lines, line.buf);
        }
+       fclose(f);
+       return 0;
 }
 
 static void show_rebase_information(struct wt_status *s,
@@ -1166,8 +1185,10 @@ static void show_rebase_information(struct wt_status *s,
                struct string_list yet_to_do = STRING_LIST_INIT_DUP;
 
                read_rebase_todolist("rebase-merge/done", &have_done);
-               read_rebase_todolist("rebase-merge/git-rebase-todo", &yet_to_do);
-
+               if (read_rebase_todolist("rebase-merge/git-rebase-todo",
+                                        &yet_to_do))
+                       status_printf_ln(s, color,
+                               _("git-rebase-todo is missing."));
                if (have_done.nr == 0)
                        status_printf_ln(s, color, _("No commands done."));
                else {
@@ -1334,7 +1355,7 @@ static void show_bisect_in_progress(struct wt_status *s,
 static char *get_branch(const struct worktree *wt, const char *path)
 {
        struct strbuf sb = STRBUF_INIT;
-       unsigned char sha1[20];
+       struct object_id oid;
        const char *branch_name;
 
        if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
@@ -1348,9 +1369,9 @@ static char *get_branch(const struct worktree *wt, const char *path)
                strbuf_remove(&sb, 0, branch_name - sb.buf);
        else if (starts_with(sb.buf, "refs/"))
                ;
-       else if (!get_sha1_hex(sb.buf, sha1)) {
+       else if (!get_oid_hex(sb.buf, &oid)) {
                strbuf_reset(&sb);
-               strbuf_add_unique_abbrev(&sb, sha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&sb, oid.hash, DEFAULT_ABBREV);
        } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
                goto got_nothing;
        else                    /* bisect */
@@ -1364,10 +1385,10 @@ static char *get_branch(const struct worktree *wt, const char *path)
 
 struct grab_1st_switch_cbdata {
        struct strbuf buf;
-       unsigned char nsha1[20];
+       struct object_id noid;
 };
 
-static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
+static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
                           const char *email, unsigned long timestamp, int tz,
                           const char *message, void *cb_data)
 {
@@ -1381,13 +1402,13 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
                return 0;
        target += strlen(" to ");
        strbuf_reset(&cb->buf);
-       hashcpy(cb->nsha1, nsha1);
+       oidcpy(&cb->noid, noid);
        end = strchrnul(target, '\n');
        strbuf_add(&cb->buf, target, end - target);
        if (!strcmp(cb->buf.buf, "HEAD")) {
                /* HEAD is relative. Resolve it to the right reflog entry. */
                strbuf_reset(&cb->buf);
-               strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&cb->buf, noid->hash, DEFAULT_ABBREV);
        }
        return 1;
 }
@@ -1396,7 +1417,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 {
        struct grab_1st_switch_cbdata cb;
        struct commit *commit;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *ref = NULL;
 
        strbuf_init(&cb.buf, 0);
@@ -1405,22 +1426,22 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
                return;
        }
 
-       if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
+       if (dwim_ref(cb.buf.buf, cb.buf.len, oid.hash, &ref) == 1 &&
            /* sha1 is a commit? match without further lookup */
-           (!hashcmp(cb.nsha1, sha1) ||
+           (!oidcmp(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
-            ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
-             !hashcmp(cb.nsha1, commit->object.oid.hash)))) {
+            ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL &&
+             !oidcmp(&cb.noid, &commit->object.oid)))) {
                const char *from = ref;
                if (!skip_prefix(from, "refs/tags/", &from))
                        skip_prefix(from, "refs/remotes/", &from);
                state->detached_from = xstrdup(from);
        } else
                state->detached_from =
-                       xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
-       hashcpy(state->detached_sha1, cb.nsha1);
-       state->detached_at = !get_sha1("HEAD", sha1) &&
-                            !hashcmp(sha1, state->detached_sha1);
+                       xstrdup(find_unique_abbrev(cb.noid.hash, DEFAULT_ABBREV));
+       hashcpy(state->detached_sha1, cb.noid.hash);
+       state->detached_at = !get_oid("HEAD", &oid) &&
+                            !hashcmp(oid.hash, state->detached_sha1);
 
        free(ref);
        strbuf_release(&cb.buf);
@@ -1470,22 +1491,22 @@ void wt_status_get_state(struct wt_status_state *state,
                         int get_detached_from)
 {
        struct stat st;
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (!stat(git_path_merge_head(), &st)) {
                state->merge_in_progress = 1;
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
        } else if (!stat(git_path_cherry_pick_head(), &st) &&
-                       !get_sha1("CHERRY_PICK_HEAD", sha1)) {
+                       !get_oid("CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
-               hashcpy(state->cherry_pick_head_sha1, sha1);
+               hashcpy(state->cherry_pick_head_sha1, oid.hash);
        }
        wt_status_check_bisect(NULL, state);
        if (!stat(git_path_revert_head(), &st) &&
-           !get_sha1("REVERT_HEAD", sha1)) {
+           !get_oid("REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
-               hashcpy(state->revert_head_sha1, sha1);
+               hashcpy(state->revert_head_sha1, oid.hash);
        }
 
        if (get_detached_from)
@@ -1724,12 +1745,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
                return;
        branch_name = s->branch;
 
+#define LABEL(string) (s->no_gettext ? (string) : _(string))
+
        if (s->is_initial)
-               color_fprintf(s->fp, header_color, _("Initial commit on "));
+               color_fprintf(s->fp, header_color, LABEL(N_("Initial commit on ")));
 
        if (!strcmp(s->branch, "HEAD")) {
                color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s",
-                             _("HEAD (no branch)"));
+                             LABEL(N_("HEAD (no branch)")));
                goto conclude;
        }
 
@@ -1754,8 +1777,6 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
        if (!upstream_is_gone && !num_ours && !num_theirs)
                goto conclude;
 
-#define LABEL(string) (s->no_gettext ? (string) : _(string))
-
        color_fprintf(s->fp, header_color, " [");
        if (upstream_is_gone) {
                color_fprintf(s->fp, header_color, LABEL(N_("gone")));
@@ -1779,34 +1800,24 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
 
 static void wt_shortstatus_print(struct wt_status *s)
 {
-       int i;
+       struct string_list_item *it;
 
        if (s->show_branch)
                wt_shortstatus_print_tracking(s);
 
-       for (i = 0; i < s->change.nr; i++) {
-               struct wt_status_change_data *d;
-               struct string_list_item *it;
+       for_each_string_list_item(it, &s->change) {
+               struct wt_status_change_data *d = it->util;
 
-               it = &(s->change.items[i]);
-               d = it->util;
                if (d->stagemask)
                        wt_shortstatus_unmerged(it, s);
                else
                        wt_shortstatus_status(it, s);
        }
-       for (i = 0; i < s->untracked.nr; i++) {
-               struct string_list_item *it;
-
-               it = &(s->untracked.items[i]);
+       for_each_string_list_item(it, &s->untracked)
                wt_shortstatus_other(it, s, "??");
-       }
-       for (i = 0; i < s->ignored.nr; i++) {
-               struct string_list_item *it;
 
-               it = &(s->ignored.items[i]);
+       for_each_string_list_item(it, &s->ignored)
                wt_shortstatus_other(it, s, "!!");
-       }
 }
 
 static void wt_porcelain_print(struct wt_status *s)
@@ -2096,7 +2107,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
                if (strcmp(ce->name, it->string) || !stage)
                        break;
                stages[stage - 1].mode = ce->ce_mode;
-               hashcpy(stages[stage - 1].oid.hash, ce->oid.hash);
+               oidcpy(&stages[stage - 1].oid, &ce->oid);
                sum |= (1 << (stage - 1));
        }
        if (sum != d->stagemask)
@@ -2258,11 +2269,12 @@ int has_uncommitted_changes(int ignore_submodules)
 int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
 {
        struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
-       int err = 0;
+       int err = 0, fd;
 
-       hold_locked_index(lock_file, 0);
+       fd = hold_locked_index(lock_file, 0);
        refresh_cache(REFRESH_QUIET);
-       update_index_if_able(&the_index, lock_file);
+       if (0 <= fd)
+               update_index_if_able(&the_index, lock_file);
        rollback_lock_file(lock_file);
 
        if (has_unstaged_changes(ignore_submodules)) {