]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'nd/the-index'
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Oct 2018 04:34:02 +0000 (13:34 +0900)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Oct 2018 04:34:02 +0000 (13:34 +0900)
Various codepaths in the core-ish part learn to work on an
arbitrary in-core index structure, not necessarily the default
instance "the_index".

* nd/the-index: (23 commits)
  revision.c: reduce implicit dependency the_repository
  revision.c: remove implicit dependency on the_index
  ws.c: remove implicit dependency on the_index
  tree-diff.c: remove implicit dependency on the_index
  submodule.c: remove implicit dependency on the_index
  line-range.c: remove implicit dependency on the_index
  userdiff.c: remove implicit dependency on the_index
  rerere.c: remove implicit dependency on the_index
  sha1-file.c: remove implicit dependency on the_index
  patch-ids.c: remove implicit dependency on the_index
  merge.c: remove implicit dependency on the_index
  merge-blobs.c: remove implicit dependency on the_index
  ll-merge.c: remove implicit dependency on the_index
  diff-lib.c: remove implicit dependency on the_index
  read-cache.c: remove implicit dependency on the_index
  diff.c: remove implicit dependency on the_index
  grep.c: remove implicit dependency on the_index
  diff.c: remove the_index dependency in textconv() functions
  blame.c: rename "repo" argument to "r"
  combine-diff.c: remove implicit dependency on the_index
  ...

52 files changed:
1  2 
archive.c
bisect.c
blame.c
builtin/add.c
builtin/am.c
builtin/checkout.c
builtin/commit.c
builtin/describe.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fmt-merge-msg.c
builtin/log.c
builtin/merge-tree.c
builtin/merge.c
builtin/pack-objects.c
builtin/pull.c
builtin/range-diff.c
builtin/replace.c
builtin/rerere.c
builtin/rev-list.c
builtin/submodule--helper.c
builtin/update-index.c
bundle.c
cache.h
combine-diff.c
diff-lib.c
diff.c
diff.h
diffcore-break.c
diffcore-rename.c
http-push.c
ll-merge.c
merge-recursive.c
notes-merge.c
pack-bitmap-write.c
patch-ids.c
read-cache.c
ref-filter.c
remote.c
rerere.c
revision.c
revision.h
sequencer.c
sha1-file.c
shallow.c
submodule.c
transport.c
tree-diff.c
userdiff.c
ws.c
wt-status.c

diff --cc archive.c
Simple merge
diff --cc bisect.c
Simple merge
diff --cc blame.c
index d5f7b7237c4e9f9dcb6ad37504a98a0fcff66f80,c229a10c0e3c9e809286ee8af8694dc135c340f2..d84c937780801f51b8eb98b7e367c8017701ba5c
+++ b/blame.c
@@@ -1454,10 -1457,10 +1457,10 @@@ static void pass_blame(struct blame_sco
                                continue;
                        if (parse_commit(p))
                                continue;
-                       porigin = find(p, origin);
+                       porigin = find(sb->repo, p, origin);
                        if (!porigin)
                                continue;
 -                      if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
 +                      if (oideq(&porigin->blob_oid, &origin->blob_oid)) {
                                pass_whole_blame(sb, origin, porigin);
                                blame_origin_decref(porigin);
                                goto finish;
diff --cc builtin/add.c
Simple merge
diff --cc builtin/am.c
Simple merge
Simple merge
index 1d5292e4d827312d3a08ceb44f6a120cbd0c4576,9d8ce6cb3baf8ab20fc1220758e815987db6c609..79d557fb2bb84a0174d40fe73e9137f5089c018a
@@@ -1654,10 -1651,7 +1654,10 @@@ int cmd_commit(int argc, const char **a
                      "new_index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git reset HEAD\" to recover."));
  
-       rerere(0);
 +      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 +              write_commit_graph_reachable(get_object_directory(), 0, 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) {
Simple merge
diff --cc builtin/diff.c
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc builtin/log.c
Simple merge
Simple merge
diff --cc builtin/merge.c
Simple merge
Simple merge
diff --cc builtin/pull.c
Simple merge
index 96af5374937e5b8b99343e8d324e54e98fb11743,1c477c4dc5ee80d29c7e01f0a88b466782810fc2..f01a0be8513412789ca49e28b0cc2519b7c04760
@@@ -28,7 -34,12 +28,7 @@@ int cmd_range_diff(int argc, const cha
  
        git_config(git_diff_ui_config, NULL);
  
-       diff_setup(&diffopt);
+       repo_diff_setup(the_repository, &diffopt);
 -      diffopt.output_format = DIFF_FORMAT_PATCH;
 -      diffopt.flags.suppress_diff_headers = 1;
 -      diffopt.output_prefix = output_prefix_cb;
 -      strbuf_addstr(&four_spaces, "    ");
 -      diffopt.output_prefix_data = &four_spaces;
  
        argc = parse_options(argc, argv, NULL, options,
                             builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN |
Simple merge
index 5ed941b91f2c1498a3f28f0c6415437f79133432,1ca271b7114566a5884fe49968cdd6bb4aeed43a..e89ccbc524a62d5a8760311a2797dfa49fe8525d
@@@ -75,10 -75,10 +75,10 @@@ int cmd_rerere(int argc, const char **a
        if (!strcmp(argv[0], "forget")) {
                struct pathspec pathspec;
                if (argc < 2)
 -                      warning("'git rerere forget' without paths is deprecated");
 +                      warning(_("'git rerere forget' without paths is deprecated"));
                parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD,
                               prefix, argv + 1);
-               return rerere_forget(&pathspec);
+               return rerere_forget(the_repository, &pathspec);
        }
  
        if (!strcmp(argv[0], "clear")) {
Simple merge
Simple merge
Simple merge
diff --cc bundle.c
Simple merge
diff --cc cache.h
Simple merge
diff --cc combine-diff.c
Simple merge
diff --cc diff-lib.c
Simple merge
diff --cc diff.c
index f0c7557b40443da060c3070c602dd03f49b0d689,c5b5e7ac41bf17b16249ce52726fe87b732d71e5..42ba9e48d72a20f2a5961e37f86bb6c4c3b2ba39
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -3580,9 -3578,10 +3589,10 @@@ static void builtin_diffstat(const cha
                return;
        }
  
 -      same_contents = !oidcmp(&one->oid, &two->oid);
 +      same_contents = oideq(&one->oid, &two->oid);
  
-       if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
+       if (diff_filespec_is_binary(o->repo, one) ||
+           diff_filespec_is_binary(o->repo, two)) {
                data->is_binary = 1;
                if (same_contents) {
                        data->added = 0;
@@@ -4385,10 -4401,8 +4412,11 @@@ void repo_diff_setup(struct repository 
        memcpy(options, &default_diff_options, sizeof(*options));
  
        options->file = stdout;
+       options->repo = r;
  
 +      options->output_indicators[OUTPUT_INDICATOR_NEW] = '+';
 +      options->output_indicators[OUTPUT_INDICATOR_OLD] = '-';
 +      options->output_indicators[OUTPUT_INDICATOR_CONTEXT] = ' ';
        options->abbrev = DEFAULT_ABBREV;
        options->line_termination = '\n';
        options->break_opt = -1;
diff --cc diff.h
Simple merge
index e11fcfdb391425ac9efaea829d4756891dcb941e,b580d92154497cec67913143d719a3ac79bf2096..875aefd3febf46771cc56c1f2b9f26318cfc732b
@@@ -58,10 -59,11 +59,11 @@@ static int should_break(struct reposito
        }
  
        if (src->oid_valid && dst->oid_valid &&
 -          !oidcmp(&src->oid, &dst->oid))
 +          oideq(&src->oid, &dst->oid))
                return 0; /* they are the same */
  
-       if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+       if (diff_populate_filespec(r, src, 0) ||
+           diff_populate_filespec(r, dst, 0))
                return 0; /* error but caught downstream */
  
        max_size = ((src->size > dst->size) ? src->size : dst->size);
Simple merge
diff --cc http-push.c
Simple merge
diff --cc ll-merge.c
index 1936fee9e1c54e8c0dc049ba48ef1cd209967d31,c339ef8ae87b316f39ba25a47da42c38d965cabd..3c8fb917e9748b1950d5df8c85aa310149f1f8df
@@@ -371,12 -372,13 +372,12 @@@ int ll_merge(mmbuffer_t *result_buf
        if (!check)
                check = attr_check_initl("merge", "conflict-marker-size", NULL);
  
-       git_check_attr(&the_index, path, check);
 -      if (!git_check_attr(istate, path, check)) {
 -              ll_driver_name = check->items[0].value;
 -              if (check->items[1].value) {
 -                      marker_size = atoi(check->items[1].value);
 -                      if (marker_size <= 0)
 -                              marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 -              }
++      git_check_attr(istate, path, check);
 +      ll_driver_name = check->items[0].value;
 +      if (check->items[1].value) {
 +              marker_size = atoi(check->items[1].value);
 +              if (marker_size <= 0)
 +                      marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        }
        driver = find_ll_merge_driver(ll_driver_name);
  
@@@ -397,8 -399,7 +398,8 @@@ int ll_merge_marker_size(struct index_s
  
        if (!check)
                check = attr_check_initl("conflict-marker-size", NULL);
-       git_check_attr(&the_index, path, check);
 -      if (!git_check_attr(istate, path, check) && check->items[0].value) {
++      git_check_attr(istate, path, check);
 +      if (check->items[0].value) {
                marker_size = atoi(check->items[0].value);
                if (marker_size <= 0)
                        marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
Simple merge
diff --cc notes-merge.c
Simple merge
Simple merge
diff --cc patch-ids.c
index 960ea2405412cc5100e1bda4cb179248d04284df,7da86047d926694775756c601f96362bf8a8490c..c262e1be9c9c912cc12c5b60a08e69f8cf9a30c9
@@@ -53,13 -53,13 +53,13 @@@ static int patch_id_neq(const void *cmp
            commit_patch_id(b->commit, opt, &b->patch_id, 0))
                return error("Could not get patch ID for %s",
                        oid_to_hex(&b->commit->object.oid));
 -      return oidcmp(&a->patch_id, &b->patch_id);
 +      return !oideq(&a->patch_id, &b->patch_id);
  }
  
- int init_patch_ids(struct patch_ids *ids)
+ int init_patch_ids(struct repository *r, struct patch_ids *ids)
  {
        memset(ids, 0, sizeof(*ids));
-       diff_setup(&ids->diffopts);
+       repo_diff_setup(r, &ids->diffopts);
        ids->diffopts.detect_rename = 0;
        ids->diffopts.flags.recursive = 1;
        diff_setup_done(&ids->diffopts);
diff --cc read-cache.c
index 8d04d78a5877aab74dd35e24a31bcfe3e0a3417d,b707edd044d7234ec5a2569ba227b01c5a3290eb..01859b77036e00eba1ab04b02ad6bf77cfb0da51
@@@ -212,8 -214,8 +214,8 @@@ static int ce_compare_data(struct index
  
        if (fd >= 0) {
                struct object_id oid;
-               if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
+               if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0))
 -                      match = oidcmp(&oid, &ce->oid);
 +                      match = !oideq(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
        return match;
@@@ -254,10 -256,12 +256,12 @@@ static int ce_compare_gitlink(const str
         */
        if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
                return 0;
 -      return oidcmp(&oid, &ce->oid);
 +      return !oideq(&oid, &ce->oid);
  }
  
- static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
+ static int ce_modified_check_fs(struct index_state *istate,
+                               const struct cache_entry *ce,
+                               struct stat *st)
  {
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
diff --cc ref-filter.c
Simple merge
diff --cc remote.c
Simple merge
diff --cc rerere.c
index 7aa149e849775f110231a54cda7b71f60e75cb4f,8d4ac8426be80ed9dd8721db883c75fbb9630621..887e26d45bfa1071736ef016d82c69de90b14f8d
+++ b/rerere.c
@@@ -462,11 -474,12 +462,12 @@@ static int handle_path(unsigned char *s
   * Scan the path for conflicts, do the "handle_path()" thing above, and
   * return the number of conflict hunks found.
   */
- static int handle_file(const char *path, unsigned char *sha1, const char *output)
+ static int handle_file(struct index_state *istate, const char *path,
+                      unsigned char *sha1, const char *output)
  {
 -      int hunk_no = 0;
 +      int has_conflicts = 0;
        struct rerere_io_file io;
-       int marker_size = ll_merge_marker_size(path);
+       int marker_size = ll_merge_marker_size(istate, path);
  
        memset(&io, 0, sizeof(io));
        io.io.getline = rerere_file_getline;
@@@ -521,7 -534,7 +522,7 @@@ static int check_one_conflict(struct in
        }
  
        *type = PUNTED;
-       while (i < active_nr && ce_stage(active_cache[i]) == 1)
 -      while (ce_stage(istate->cache[i]) == 1)
++      while (i < istate->cache_nr && ce_stage(istate->cache[i]) == 1)
                i++;
  
        /* Only handle regular files with both stages #2 and #3 */
   * are identical to the previous round, might want to be handled,
   * though.
   */
- static int find_conflict(struct string_list *conflict)
+ static int find_conflict(struct repository *r, struct string_list *conflict)
  {
        int i;
-       if (read_cache() < 0)
+       if (read_index(r->index) < 0)
 -              return error("Could not read index");
 +              return error(_("index file corrupt"));
  
-       for (i = 0; i < active_nr;) {
+       for (i = 0; i < r->index->cache_nr;) {
                int conflict_type;
-               const struct cache_entry *e = active_cache[i];
-               i = check_one_conflict(i, &conflict_type);
+               const struct cache_entry *e = r->index->cache[i];
+               i = check_one_conflict(r->index, i, &conflict_type);
                if (conflict_type == THREE_STAGED)
                        string_list_insert(conflict, (const char *)e->name);
        }
   * NEEDSWORK: we may want to fix the caller that implements "rerere
   * remaining" to do this without abusing merge_rr.
   */
- int rerere_remaining(struct string_list *merge_rr)
+ int rerere_remaining(struct repository *r, struct string_list *merge_rr)
  {
        int i;
        if (setup_rerere(merge_rr, RERERE_READONLY))
                return 0;
-       if (read_cache() < 0)
+       if (read_index(r->index) < 0)
 -              return error("Could not read index");
 +              return error(_("index file corrupt"));
  
-       for (i = 0; i < active_nr;) {
+       for (i = 0; i < r->index->cache_nr;) {
                int conflict_type;
-               const struct cache_entry *e = active_cache[i];
-               i = check_one_conflict(i, &conflict_type);
+               const struct cache_entry *e = r->index->cache[i];
+               i = check_one_conflict(r->index, i, &conflict_type);
                if (conflict_type == PUNTED)
                        string_list_insert(merge_rr, (const char *)e->name);
                else if (conflict_type == RESOLVED) {
@@@ -701,15 -718,15 +706,15 @@@ static void update_paths(struct reposit
  
        for (i = 0; i < update->nr; i++) {
                struct string_list_item *item = &update->items[i];
-               if (add_file_to_cache(item->string, 0))
+               if (add_file_to_index(r->index, item->string, 0))
                        exit(128);
 -              fprintf(stderr, "Staged '%s' using previous resolution.\n",
 +              fprintf_ln(stderr, _("Staged '%s' using previous resolution."),
                        item->string);
        }
  
-       if (write_locked_index(&the_index, &index_lock,
+       if (write_locked_index(r->index, &index_lock,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
 -              die("Unable to write new index file");
 +              die(_("unable to write new index file"));
  }
  
  static void remove_variant(struct rerere_id *id)
@@@ -738,10 -756,10 +744,10 @@@ static void do_rerere_one_path(struct i
  
        /* Has the user resolved it already? */
        if (variant >= 0) {
-               if (!handle_file(path, NULL, NULL)) {
+               if (!handle_file(istate, path, NULL, NULL)) {
                        copy_file(rerere_path(id, "postimage"), path, 0666);
                        id->collection->status[variant] |= RR_HAS_POSTIMAGE;
 -                      fprintf(stderr, "Recorded resolution for '%s'.\n", path);
 +                      fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
                        free_rerere_id(rr_item);
                        rr_item->util = NULL;
                        return;
                id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
        }
        id->collection->status[variant] |= RR_HAS_PREIMAGE;
 -      fprintf(stderr, "Recorded preimage for '%s'\n", path);
 +      fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
  }
  
- static int do_plain_rerere(struct string_list *rr, int fd)
+ static int do_plain_rerere(struct repository *r,
+                          struct string_list *rr, int fd)
  {
        struct string_list conflict = STRING_LIST_INIT_DUP;
        struct string_list update = STRING_LIST_INIT_DUP;
                 * conflict ID.  No need to write anything out
                 * yet.
                 */
-               ret = handle_file(path, sha1, NULL);
+               ret = handle_file(r->index, path, sha1, NULL);
 +              if (ret != 0 && string_list_has_string(rr, path)) {
 +                      remove_variant(string_list_lookup(rr, path)->util);
 +                      string_list_remove(rr, path, 1);
 +              }
                if (ret < 1)
                        continue;
  
@@@ -947,9 -966,9 +955,9 @@@ static int handle_cache(struct index_st
        mmfile_t mmfile[3] = {{NULL}};
        mmbuffer_t result = {NULL, 0};
        const struct cache_entry *ce;
 -      int pos, len, i, hunk_no;
 +      int pos, len, i, has_conflicts;
        struct rerere_io_mem io;
-       int marker_size = ll_merge_marker_size(path);
+       int marker_size = ll_merge_marker_size(istate, path);
  
        /*
         * Reproduce the conflicted merge in-core
        strbuf_release(&io.input);
        if (io.io.output)
                fclose(io.io.output);
 -      return hunk_no;
 +      return has_conflicts;
  }
  
- static int rerere_forget_one_path(const char *path, struct string_list *rr)
+ static int rerere_forget_one_path(struct index_state *istate,
+                                 const char *path,
+                                 struct string_list *rr)
  {
        const char *filename;
        struct rerere_id *id;
         * Recreate the original conflict from the stages in the
         * index and compute the conflict ID
         */
-       ret = handle_cache(path, sha1, NULL);
+       ret = handle_cache(istate, path, sha1, NULL);
        if (ret < 1)
 -              return error("Could not parse conflict hunks in '%s'", path);
 +              return error(_("could not parse conflict hunks in '%s'"), path);
  
        /* Nuke the recorded resolution for the conflict */
        id = new_rerere_id(sha1);
                if (!has_rerere_resolution(id))
                        continue;
  
-               handle_cache(path, sha1, rerere_path(id, "thisimage"));
+               handle_cache(istate, path, sha1, rerere_path(id, "thisimage"));
                if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                        free(cur.ptr);
 -                      error("Failed to update conflicted state in '%s'", path);
 +                      error(_("failed to update conflicted state in '%s'"), path);
                        goto fail_exit;
                }
-               cleanly_resolved = !try_merge(id, path, &cur, &result);
+               cleanly_resolved = !try_merge(istate, id, path, &cur, &result);
                free(result.ptr);
                free(cur.ptr);
                if (cleanly_resolved)
         * conflict in the working tree, run us again to record
         * the postimage.
         */
-       handle_cache(path, sha1, rerere_path(id, "preimage"));
+       handle_cache(istate, path, sha1, rerere_path(id, "preimage"));
 -      fprintf(stderr, "Updated preimage for '%s'\n", path);
 +      fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
  
        /*
         * And remember that we can record resolution for this
@@@ -1093,8 -1115,8 +1104,8 @@@ int rerere_forget(struct repository *r
        struct string_list conflict = STRING_LIST_INIT_DUP;
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
  
-       if (read_cache() < 0)
+       if (read_index(r->index) < 0)
 -              return error("Could not read index");
 +              return error(_("index file corrupt"));
  
        fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
        if (fd < 0)
diff --cc revision.c
Simple merge
diff --cc revision.h
Simple merge
diff --cc sequencer.c
Simple merge
diff --cc sha1-file.c
Simple merge
diff --cc shallow.c
Simple merge
diff --cc submodule.c
Simple merge
diff --cc transport.c
Simple merge
diff --cc tree-diff.c
Simple merge
diff --cc userdiff.c
index f565f6731d1336c761b527a8ce8a62a21b453dcc,c913232396d92c62e5cd8b6a8cb632d4e7b7ce80..46d34cc2a436c6084531d39628d013785700e2ba
@@@ -278,7 -279,8 +279,7 @@@ struct userdiff_driver *userdiff_find_b
                check = attr_check_initl("diff", NULL);
        if (!path)
                return NULL;
-       git_check_attr(&the_index, path, check);
 -      if (git_check_attr(istate, path, check))
 -              return NULL;
++      git_check_attr(istate, path, check);
  
        if (ATTR_TRUE(check->items[0].value))
                return &driver_true;
diff --cc ws.c
index a64ab51e09a99e190e5f1912de19b1e093feb360,55349b4c5d08597e38ab76d10cd0d11b944f4f37..6e69877f25791632d98bf7b109a2eaebd04c96af
--- 1/ws.c
--- 2/ws.c
+++ b/ws.c
@@@ -71,34 -70,38 +70,34 @@@ unsigned parse_whitespace_rule(const ch
        return rule;
  }
  
- unsigned whitespace_rule(const char *pathname)
+ unsigned whitespace_rule(struct index_state *istate, const char *pathname)
  {
        static struct attr_check *attr_whitespace_rule;
 +      const char *value;
  
        if (!attr_whitespace_rule)
                attr_whitespace_rule = attr_check_initl("whitespace", NULL);
  
-       git_check_attr(&the_index, pathname, attr_whitespace_rule);
 -      if (!git_check_attr(istate, pathname, attr_whitespace_rule)) {
 -              const char *value;
 -
 -              value = attr_whitespace_rule->items[0].value;
 -              if (ATTR_TRUE(value)) {
 -                      /* true (whitespace) */
 -                      unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
 -                      int i;
 -                      for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
 -                              if (!whitespace_rule_names[i].loosens_error &&
 -                                  !whitespace_rule_names[i].exclude_default)
 -                                      all_rule |= whitespace_rule_names[i].rule_bits;
 -                      return all_rule;
 -              } else if (ATTR_FALSE(value)) {
 -                      /* false (-whitespace) */
 -                      return ws_tab_width(whitespace_rule_cfg);
 -              } else if (ATTR_UNSET(value)) {
 -                      /* reset to default (!whitespace) */
 -                      return whitespace_rule_cfg;
 -              } else {
 -                      /* string */
 -                      return parse_whitespace_rule(value);
 -              }
 -      } else {
++      git_check_attr(istate, pathname, attr_whitespace_rule);
 +      value = attr_whitespace_rule->items[0].value;
 +      if (ATTR_TRUE(value)) {
 +              /* true (whitespace) */
 +              unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
 +              int i;
 +              for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
 +                      if (!whitespace_rule_names[i].loosens_error &&
 +                          !whitespace_rule_names[i].exclude_default)
 +                              all_rule |= whitespace_rule_names[i].rule_bits;
 +              return all_rule;
 +      } else if (ATTR_FALSE(value)) {
 +              /* false (-whitespace) */
 +              return ws_tab_width(whitespace_rule_cfg);
 +      } else if (ATTR_UNSET(value)) {
 +              /* reset to default (!whitespace) */
                return whitespace_rule_cfg;
 +      } else {
 +              /* string */
 +              return parse_whitespace_rule(value);
        }
  }
  
diff --cc wt-status.c
Simple merge