Merge branch 'jk/fsck-doc'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Mar 2019 06:16:05 +0000 (15:16 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Mar 2019 06:16:06 +0000 (15:16 +0900)
"git fsck --connectivity-only" omits computation necessary to sift
the objects that are not reachable from any of the refs into
unreachable and dangling. This is now enabled when dangling
objects are requested (which is done by default, but can be
overridden with the "--no-dangling" option).

* jk/fsck-doc:
fsck: always compute USED flags for unreachable objects
doc/fsck: clarify --connectivity-only behavior

1  2 
Documentation/git-fsck.txt
builtin/fsck.c
t/t1450-fsck.sh
index 55950d9eea9edb5157352fa3e6316ef3507e99bf,f467119082243fa2f3c3187d8233f8d638f2bcb4..e0eae642c10f75cff57c0a9d07420e3218235423
@@@ -62,9 -62,17 +62,17 @@@ index file, all SHA-1 references in `re
        with --no-full.
  
  --connectivity-only::
-       Check only the connectivity of tags, commits and tree objects. By
-       avoiding to unpack blobs, this speeds up the operation, at the
-       expense of missing corrupt objects or other problematic issues.
+       Check only the connectivity of reachable objects, making sure
+       that any objects referenced by a reachable tag, commit, or tree
+       is present. This speeds up the operation by avoiding reading
+       blobs entirely (though it does still check that referenced blobs
+       exist). This will detect corruption in commits and trees, but
+       not do any semantic checks (e.g., for format errors). Corruption
+       in blob objects will not be detected at all.
+ +
+ Unreachable tags, commits, and trees will also be accessed to find the
+ tips of dangling segments of history. Use `--no-dangling` if you don't
+ care about this output and want to speed it up further.
  
  --strict::
        Enable more strict checking, namely to catch a file mode
@@@ -140,9 -148,9 +148,9 @@@ dangling <type> <object>:
        The <type> object <object>, is present in the database but never
        'directly' used. A dangling commit could be a root node.
  
 -sha1 mismatch <object>::
 -      The database has an object who's sha1 doesn't match the
 -      database value.
 +hash mismatch <object>::
 +      The database has an object whose hash doesn't match the
 +      object database value.
        This indicates a serious data integrity problem.
  
  Environment Variables
diff --combined builtin/fsck.c
index bb4227bebc606dfac8337d7aa3e78705113a79b5,1d982488f03c2a3ebcf1eb2d6ac5317abefb7eb3..d26fb0a04472b18a856658cf2800dd9655a6bf24
@@@ -1,4 -1,3 +1,4 @@@
 +#define USE_THE_INDEX_COMPATIBILITY_MACROS
  #include "builtin.h"
  #include "cache.h"
  #include "repository.h"
@@@ -52,24 -51,16 +52,24 @@@ static int name_objects
  
  static const char *describe_object(struct object *obj)
  {
 -      static struct strbuf buf = STRBUF_INIT;
 -      char *name = name_objects ?
 -              lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
 +      static struct strbuf bufs[] = {
 +              STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
 +      };
 +      static int b = 0;
 +      struct strbuf *buf;
 +      char *name = NULL;
  
 -      strbuf_reset(&buf);
 -      strbuf_addstr(&buf, oid_to_hex(&obj->oid));
 +      if (name_objects)
 +              name = lookup_decoration(fsck_walk_options.object_names, obj);
 +
 +      buf = bufs + b;
 +      b = (b + 1) % ARRAY_SIZE(bufs);
 +      strbuf_reset(buf);
 +      strbuf_addstr(buf, oid_to_hex(&obj->oid));
        if (name)
 -              strbuf_addf(&buf, " (%s)", name);
 +              strbuf_addf(buf, " (%s)", name);
  
 -      return buf.buf;
 +      return buf->buf;
  }
  
  static const char *printable_type(struct object *obj)
@@@ -85,7 -76,7 +85,7 @@@
  
        ret = type_name(obj->type);
        if (!ret)
 -              ret = "unknown";
 +              ret = _("unknown");
  
        return ret;
  }
@@@ -113,32 -104,25 +113,32 @@@ static int fsck_config(const char *var
        return git_default_config(var, value, cb);
  }
  
 -static void objreport(struct object *obj, const char *msg_type,
 -                      const char *err)
 -{
 -      fprintf(stderr, "%s in %s %s: %s\n",
 -              msg_type, printable_type(obj), describe_object(obj), err);
 -}
 -
  static int objerror(struct object *obj, const char *err)
  {
        errors_found |= ERROR_OBJECT;
 -      objreport(obj, "error", err);
 +      /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 +      fprintf_ln(stderr, _("error in %s %s: %s"),
 +                 printable_type(obj), describe_object(obj), err);
        return -1;
  }
  
  static int fsck_error_func(struct fsck_options *o,
        struct object *obj, int type, const char *message)
  {
 -      objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
 -      return (type == FSCK_WARN) ? 0 : 1;
 +      switch (type) {
 +      case FSCK_WARN:
 +              /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
 +              fprintf_ln(stderr, _("warning in %s %s: %s"),
 +                         printable_type(obj), describe_object(obj), message);
 +              return 0;
 +      case FSCK_ERROR:
 +              /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 +              fprintf_ln(stderr, _("error in %s %s: %s"),
 +                         printable_type(obj), describe_object(obj), message);
 +              return 1;
 +      default:
 +              BUG("%d (FSCK_IGNORE?) should never trigger this callback", type);
 +      }
  }
  
  static struct object_array pending;
@@@ -154,18 -138,17 +154,18 @@@ static int mark_object(struct object *o
         */
        if (!obj) {
                /* ... these references to parent->fld are safe here */
 -              printf("broken link from %7s %s\n",
 -                         printable_type(parent), describe_object(parent));
 -              printf("broken link from %7s %s\n",
 -                         (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown");
 +              printf_ln(_("broken link from %7s %s"),
 +                        printable_type(parent), describe_object(parent));
 +              printf_ln(_("broken link from %7s %s"),
 +                        (type == OBJ_ANY ? _("unknown") : type_name(type)),
 +                        _("unknown"));
                errors_found |= ERROR_REACHABLE;
                return 1;
        }
  
        if (type != OBJ_ANY && obj->type != type)
                /* ... and the reference to parent is safe here */
 -              objerror(parent, "wrong object type in link");
 +              objerror(parent, _("wrong object type in link"));
  
        if (obj->flags & REACHABLE)
                return 0;
  
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
 -                      printf("broken link from %7s %s\n",
 -                               printable_type(parent), describe_object(parent));
 -                      printf("              to %7s %s\n",
 -                               printable_type(obj), describe_object(obj));
 +                      printf_ln(_("broken link from %7s %s\n"
 +                                  "              to %7s %s"),
 +                                printable_type(parent),
 +                                describe_object(parent),
 +                                printable_type(obj),
 +                                describe_object(obj));
                        errors_found |= ERROR_REACHABLE;
                }
                return 1;
@@@ -235,6 -216,48 +235,48 @@@ static int mark_used(struct object *obj
        return 0;
  }
  
+ static void mark_unreachable_referents(const struct object_id *oid)
+ {
+       struct fsck_options options = FSCK_OPTIONS_DEFAULT;
+       struct object *obj = lookup_object(the_repository, oid->hash);
+       if (!obj || !(obj->flags & HAS_OBJ))
+               return; /* not part of our original set */
+       if (obj->flags & REACHABLE)
+               return; /* reachable objects already traversed */
+       /*
+        * Avoid passing OBJ_NONE to fsck_walk, which will parse the object
+        * (and we want to avoid parsing blobs).
+        */
+       if (obj->type == OBJ_NONE) {
+               enum object_type type = oid_object_info(the_repository,
+                                                       &obj->oid, NULL);
+               if (type > 0)
+                       object_as_type(the_repository, obj, type, 0);
+       }
+       options.walk = mark_used;
+       fsck_walk(obj, NULL, &options);
+ }
+ static int mark_loose_unreachable_referents(const struct object_id *oid,
+                                           const char *path,
+                                           void *data)
+ {
+       mark_unreachable_referents(oid);
+       return 0;
+ }
+ static int mark_packed_unreachable_referents(const struct object_id *oid,
+                                            struct packed_git *pack,
+                                            uint32_t pos,
+                                            void *data)
+ {
+       mark_unreachable_referents(oid);
+       return 0;
+ }
  /*
   * Check a single reachable object
   */
@@@ -250,8 -273,8 +292,8 @@@ static void check_reachable_object(stru
                        return;
                if (has_object_pack(&obj->oid))
                        return; /* it is in pack - forget about it */
 -              printf("missing %s %s\n", printable_type(obj),
 -                      describe_object(obj));
 +              printf_ln(_("missing %s %s"), printable_type(obj),
 +                        describe_object(obj));
                errors_found |= ERROR_REACHABLE;
                return;
        }
@@@ -276,8 -299,8 +318,8 @@@ static void check_unreachable_object(st
         * since this is something that is prunable.
         */
        if (show_unreachable) {
 -              printf("unreachable %s %s\n", printable_type(obj),
 -                      describe_object(obj));
 +              printf_ln(_("unreachable %s %s"), printable_type(obj),
 +                        describe_object(obj));
                return;
        }
  
         */
        if (!(obj->flags & USED)) {
                if (show_dangling)
 -                      printf("dangling %s %s\n", printable_type(obj),
 -                             describe_object(obj));
 +                      printf_ln(_("dangling %s %s"), printable_type(obj),
 +                                describe_object(obj));
                if (write_lost_and_found) {
                        char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
                        FILE *f;
  
                        if (safe_create_leading_directories_const(filename)) {
 -                              error("Could not create lost-found");
 +                              error(_("could not create lost-found"));
                                free(filename);
                                return;
                        }
                        f = xfopen(filename, "w");
                        if (obj->type == OBJ_BLOB) {
                                if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
 -                                      die_errno("Could not write '%s'", filename);
 +                                      die_errno(_("could not write '%s'"), filename);
                        } else
                                fprintf(f, "%s\n", describe_object(obj));
                        if (fclose(f))
 -                              die_errno("Could not finish '%s'",
 +                              die_errno(_("could not finish '%s'"),
                                          filename);
                        free(filename);
                }
  static void check_object(struct object *obj)
  {
        if (verbose)
 -              fprintf(stderr, "Checking %s\n", describe_object(obj));
 +              fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
  
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
@@@ -347,10 -370,30 +389,30 @@@ static void check_connectivity(void
        /* Traverse the pending reachable objects */
        traverse_reachable();
  
+       /*
+        * With --connectivity-only, we won't have actually opened and marked
+        * unreachable objects with USED. Do that now to make --dangling, etc
+        * accurate.
+        */
+       if (connectivity_only && (show_dangling || write_lost_and_found)) {
+               /*
+                * Even though we already have a "struct object" for each of
+                * these in memory, we must not iterate over the internal
+                * object hash as we do below. Our loop would potentially
+                * resize the hash, making our iteration invalid.
+                *
+                * Instead, we'll just go back to the source list of objects,
+                * and ignore any that weren't present in our earlier
+                * traversal.
+                */
+               for_each_loose_object(mark_loose_unreachable_referents, NULL, 0);
+               for_each_packed_object(mark_packed_unreachable_referents, NULL, 0);
+       }
        /* Look up all the requirements, warn about missing objects.. */
        max = get_max_object_index();
        if (verbose)
 -              fprintf(stderr, "Checking connectivity (%d objects)\n", max);
 +              fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
  
        for (i = 0; i < max; i++) {
                struct object *obj = get_indexed_object(i);
@@@ -369,11 -412,11 +431,11 @@@ static int fsck_obj(struct object *obj
        obj->flags |= SEEN;
  
        if (verbose)
 -              fprintf(stderr, "Checking %s %s\n",
 -                      printable_type(obj), describe_object(obj));
 +              fprintf_ln(stderr, _("Checking %s %s"),
 +                         printable_type(obj), describe_object(obj));
  
        if (fsck_walk(obj, NULL, &fsck_obj_options))
 -              objerror(obj, "broken links");
 +              objerror(obj, _("broken links"));
        err = fsck_object(obj, buffer, size, &fsck_obj_options);
        if (err)
                goto out;
                struct commit *commit = (struct commit *) obj;
  
                if (!commit->parents && show_root)
 -                      printf("root %s\n", describe_object(&commit->object));
 +                      printf_ln(_("root %s"),
 +                                describe_object(&commit->object));
        }
  
        if (obj->type == OBJ_TAG) {
                struct tag *tag = (struct tag *) obj;
  
                if (show_tags && tag->tagged) {
 -                      printf("tagged %s %s", printable_type(tag->tagged),
 -                              describe_object(tag->tagged));
 -                      printf(" (%s) in %s\n", tag->tag,
 -                              describe_object(&tag->object));
 +                      printf_ln(_("tagged %s %s (%s) in %s"),
 +                                printable_type(tag->tagged),
 +                                describe_object(tag->tagged),
 +                                tag->tag,
 +                                describe_object(&tag->object));
                }
        }
  
@@@ -402,8 -443,7 +464,8 @@@ out
        if (obj->type == OBJ_TREE)
                free_tree_buffer((struct tree *)obj);
        if (obj->type == OBJ_COMMIT)
 -              free_commit_buffer((struct commit *)obj);
 +              free_commit_buffer(the_repository->parsed_objects,
 +                                 (struct commit *)obj);
        return err;
  }
  
@@@ -419,8 -459,7 +481,8 @@@ static int fsck_obj_buffer(const struc
                                  eaten);
        if (!obj) {
                errors_found |= ERROR_OBJECT;
 -              return error("%s: object corrupt or missing", oid_to_hex(oid));
 +              return error(_("%s: object corrupt or missing"),
 +                           oid_to_hex(oid));
        }
        obj->flags &= ~(REACHABLE | SEEN);
        obj->flags |= HAS_OBJ;
@@@ -444,8 -483,7 +506,8 @@@ static void fsck_handle_reflog_oid(cons
                        obj->flags |= USED;
                        mark_object_reachable(obj);
                } else if (!is_promisor_object(oid)) {
 -                      error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
 +                      error(_("%s: invalid reflog entry %s"),
 +                            refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
        }
@@@ -458,8 -496,8 +520,8 @@@ static int fsck_handle_reflog_ent(struc
        const char *refname = cb_data;
  
        if (verbose)
 -              fprintf(stderr, "Checking reflog %s->%s\n",
 -                      oid_to_hex(ooid), oid_to_hex(noid));
 +              fprintf_ln(stderr, _("Checking reflog %s->%s"),
 +                         oid_to_hex(ooid), oid_to_hex(noid));
  
        fsck_handle_reflog_oid(refname, ooid, 0);
        fsck_handle_reflog_oid(refname, noid, timestamp);
@@@ -492,14 -530,13 +554,14 @@@ static int fsck_handle_ref(const char *
                         default_refs++;
                         return 0;
                }
 -              error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
 +              error(_("%s: invalid sha1 pointer %s"),
 +                    refname, oid_to_hex(oid));
                errors_found |= ERROR_REACHABLE;
                /* We'll continue with the rest despite the error.. */
                return 0;
        }
        if (obj->type != OBJ_COMMIT && is_branch(refname)) {
 -              error("%s: not a commit", refname);
 +              error(_("%s: not a commit"), refname);
                errors_found |= ERROR_REFS;
        }
        default_refs++;
@@@ -554,7 -591,7 +616,7 @@@ static void get_default_heads(void
         * "show_unreachable" flag.
         */
        if (!default_refs) {
 -              fprintf(stderr, "notice: No default references\n");
 +              fprintf_ln(stderr, _("notice: No default references"));
                show_unreachable = 0;
        }
  }
@@@ -569,7 -606,7 +631,7 @@@ static int fsck_loose(const struct obje
  
        if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
                errors_found |= ERROR_OBJECT;
 -              error("%s: object corrupt or missing: %s",
 +              error(_("%s: object corrupt or missing: %s"),
                      oid_to_hex(oid), path);
                return 0; /* keep checking other objects */
        }
  
        if (!obj) {
                errors_found |= ERROR_OBJECT;
 -              error("%s: object could not be parsed: %s",
 +              error(_("%s: object could not be parsed: %s"),
                      oid_to_hex(oid), path);
                if (!eaten)
                        free(contents);
  static int fsck_cruft(const char *basename, const char *path, void *data)
  {
        if (!starts_with(basename, "tmp_obj_"))
 -              fprintf(stderr, "bad sha1 file: %s\n", path);
 +              fprintf_ln(stderr, _("bad sha1 file: %s"), path);
        return 0;
  }
  
@@@ -617,7 -654,7 +679,7 @@@ static void fsck_object_dir(const char 
        struct progress *progress = NULL;
  
        if (verbose)
 -              fprintf(stderr, "Checking object directory\n");
 +              fprintf_ln(stderr, _("Checking object directory"));
  
        if (show_progress)
                progress = start_progress(_("Checking object directories"), 256);
@@@ -635,30 -672,29 +697,30 @@@ static int fsck_head_link(const char *h
        int null_is_error = 0;
  
        if (verbose)
 -              fprintf(stderr, "Checking %s link\n", head_ref_name);
 +              fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
  
        *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
        if (!*head_points_at) {
                errors_found |= ERROR_REFS;
 -              return error("Invalid %s", head_ref_name);
 +              return error(_("invalid %s"), head_ref_name);
        }
        if (!strcmp(*head_points_at, head_ref_name))
                /* detached HEAD */
                null_is_error = 1;
        else if (!starts_with(*head_points_at, "refs/heads/")) {
                errors_found |= ERROR_REFS;
 -              return error("%s points to something strange (%s)",
 +              return error(_("%s points to something strange (%s)"),
                             head_ref_name, *head_points_at);
        }
        if (is_null_oid(head_oid)) {
                if (null_is_error) {
                        errors_found |= ERROR_REFS;
 -                      return error("%s: detached HEAD points at nothing",
 +                      return error(_("%s: detached HEAD points at nothing"),
                                     head_ref_name);
                }
 -              fprintf(stderr, "notice: %s points to an unborn branch (%s)\n",
 -                      head_ref_name, *head_points_at + 11);
 +              fprintf_ln(stderr,
 +                         _("notice: %s points to an unborn branch (%s)"),
 +                         head_ref_name, *head_points_at + 11);
        }
        return 0;
  }
@@@ -669,12 -705,12 +731,12 @@@ static int fsck_cache_tree(struct cache
        int err = 0;
  
        if (verbose)
 -              fprintf(stderr, "Checking cache tree\n");
 +              fprintf_ln(stderr, _("Checking cache tree"));
  
        if (0 <= it->entry_count) {
                struct object *obj = parse_object(the_repository, &it->oid);
                if (!obj) {
 -                      error("%s: invalid sha1 pointer in cache-tree",
 +                      error(_("%s: invalid sha1 pointer in cache-tree"),
                              oid_to_hex(&it->oid));
                        errors_found |= ERROR_REFS;
                        return 1;
                                obj, xstrdup(":"));
                mark_object_reachable(obj);
                if (obj->type != OBJ_TREE)
 -                      err |= objerror(obj, "non-tree in cache-tree");
 +                      err |= objerror(obj, _("non-tree in cache-tree"));
        }
        for (i = 0; i < it->subtree_nr; i++)
                err |= fsck_cache_tree(it->down[i]->cache_tree);
@@@ -741,7 -777,7 +803,7 @@@ static struct option fsck_opts[] = 
  int cmd_fsck(int argc, const char **argv, const char *prefix)
  {
        int i;
 -      struct alternate_object_database *alt;
 +      struct object_directory *odb;
  
        /* fsck knows how to handle missing promisor objects */
        fetch_if_missing = 0;
                for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
                for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
        } else {
 -              struct alternate_object_database *alt_odb_list;
 -
 -              fsck_object_dir(get_object_directory());
 -
                prepare_alt_odb(the_repository);
 -              alt_odb_list = the_repository->objects->alt_odb_list;
 -              for (alt = alt_odb_list; alt; alt = alt->next)
 -                      fsck_object_dir(alt->path);
 +              for (odb = the_repository->objects->odb; odb; odb = odb->next)
 +                      fsck_object_dir(odb->path);
  
                if (check_full) {
                        struct packed_git *p;
                        for (p = get_all_packs(the_repository); p;
                             p = p->next) {
                                /* verify gives error messages itself */
 -                              if (verify_pack(p, fsck_obj_buffer,
 +                              if (verify_pack(the_repository,
 +                                              p, fsck_obj_buffer,
                                                progress, count))
                                        errors_found |= ERROR_PACK;
                                count += p->num_objects;
                        if (!obj || !(obj->flags & HAS_OBJ)) {
                                if (is_promisor_object(&oid))
                                        continue;
 -                              error("%s: object missing", oid_to_hex(&oid));
 +                              error(_("%s: object missing"), oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
                        }
                        mark_object_reachable(obj);
                        continue;
                }
 -              error("invalid parameter: expected sha1, got '%s'", arg);
 +              error(_("invalid parameter: expected sha1, got '%s'"), arg);
                errors_found |= ERROR_OBJECT;
        }
  
                struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
                const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
  
 -              commit_graph_verify.argv = verify_argv;
 -              commit_graph_verify.git_cmd = 1;
 -              if (run_command(&commit_graph_verify))
 -                      errors_found |= ERROR_COMMIT_GRAPH;
 -
                prepare_alt_odb(the_repository);
 -              for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
 +              for (odb = the_repository->objects->odb; odb; odb = odb->next) {
 +                      child_process_init(&commit_graph_verify);
 +                      commit_graph_verify.argv = verify_argv;
 +                      commit_graph_verify.git_cmd = 1;
                        verify_argv[2] = "--object-dir";
 -                      verify_argv[3] = alt->path;
 +                      verify_argv[3] = odb->path;
                        if (run_command(&commit_graph_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
                struct child_process midx_verify = CHILD_PROCESS_INIT;
                const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
  
 -              midx_verify.argv = midx_argv;
 -              midx_verify.git_cmd = 1;
 -              if (run_command(&midx_verify))
 -                      errors_found |= ERROR_COMMIT_GRAPH;
 -
                prepare_alt_odb(the_repository);
 -              for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
 +              for (odb = the_repository->objects->odb; odb; odb = odb->next) {
 +                      child_process_init(&midx_verify);
 +                      midx_verify.argv = midx_argv;
 +                      midx_verify.git_cmd = 1;
                        midx_argv[2] = "--object-dir";
 -                      midx_argv[3] = alt->path;
 +                      midx_argv[3] = odb->path;
                        if (run_command(&midx_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
diff --combined t/t1450-fsck.sh
index c61f97214137f5fcf5a9d6b7dd98ea8162e9916d,b203f404a0ece5042b9c40281e9d42c4ef60ae8f..49f08d5b9c078412af8ee6343519094521c86c24
@@@ -70,7 -70,7 +70,7 @@@ test_expect_success 'object with bad sh
  
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "$sha.*corrupt" out
 +      test_i18ngrep "$sha.*corrupt" out
  '
  
  test_expect_success 'branch pointing to non-commit' '
@@@ -78,7 -78,7 +78,7 @@@
        test_when_finished "git update-ref -d refs/heads/invalid" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "not a commit" out
 +      test_i18ngrep "not a commit" out
  '
  
  test_expect_success 'HEAD link pointing at a funny object' '
@@@ -88,7 -88,7 +88,7 @@@
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
        cat out &&
 -      grep "detached HEAD points" out
 +      test_i18ngrep "detached HEAD points" out
  '
  
  test_expect_success 'HEAD link pointing at a funny place' '
@@@ -98,7 -98,7 +98,7 @@@
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
        cat out &&
 -      grep "HEAD points to something strange" out
 +      test_i18ngrep "HEAD points to something strange" out
  '
  
  test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
        echo $ZERO_OID >.git/HEAD &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail git -C wt fsck 2>out &&
 -      grep "main-worktree/HEAD: detached HEAD points" out
 +      test_i18ngrep "main-worktree/HEAD: detached HEAD points" out
  '
  
  test_expect_success 'other worktree HEAD link pointing at a funny object' '
        git worktree add other &&
        echo $ZERO_OID >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
 -      grep "worktrees/other/HEAD: detached HEAD points" out
 +      test_i18ngrep "worktrees/other/HEAD: detached HEAD points" out
  '
  
  test_expect_success 'other worktree HEAD link pointing at missing object' '
        git worktree add other &&
        echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
 -      grep "worktrees/other/HEAD: invalid sha1 pointer" out
 +      test_i18ngrep "worktrees/other/HEAD: invalid sha1 pointer" out
  '
  
  test_expect_success 'other worktree HEAD link pointing at a funny place' '
        git worktree add other &&
        echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
 -      grep "worktrees/other/HEAD points to something strange" out
 +      test_i18ngrep "worktrees/other/HEAD points to something strange" out
  '
  
  test_expect_success 'email without @ is okay' '
@@@ -157,7 -157,7 +157,7 @@@ test_expect_success 'email with embedde
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "error in commit $new" out
 +      test_i18ngrep "error in commit $new" out
  '
  
  test_expect_success 'missing < email delimiter is reported nicely' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "error in commit $new.* - bad name" out
 +      test_i18ngrep "error in commit $new.* - bad name" out
  '
  
  test_expect_success 'missing email is reported nicely' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "error in commit $new.* - missing email" out
 +      test_i18ngrep "error in commit $new.* - missing email" out
  '
  
  test_expect_success '> in name is reported' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "error in commit $new" out
 +      test_i18ngrep "error in commit $new" out
  '
  
  # date is 2^64 + 1
@@@ -207,7 -207,7 +207,7 @@@ test_expect_success 'integer overflow i
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "error in commit $new.*integer overflow" out
 +      test_i18ngrep "error in commit $new.*integer overflow" out
  '
  
  test_expect_success 'commit with NUL in header' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
 -      grep "error in commit $new.*unterminated header: NUL at offset" out
 +      test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
  '
  
  test_expect_success 'tree object with duplicate entries' '
                git hash-object -w -t tree --stdin
        ) &&
        test_must_fail git fsck 2>out &&
 -      grep "error in tree .*contains duplicate file entries" out
 +      test_i18ngrep "error in tree .*contains duplicate file entries" out
  '
  
  test_expect_success 'unparseable tree object' '
@@@ -294,7 -294,7 +294,7 @@@ test_expect_success 'tag pointing to no
        test_when_finished "git update-ref -d refs/tags/invalid" &&
        test_must_fail git fsck --tags >out &&
        cat out &&
 -      grep "broken link" out
 +      test_i18ngrep "broken link" out
  '
  
  test_expect_success 'tag pointing to something else than its type' '
@@@ -336,7 -336,7 +336,7 @@@ test_expect_success 'tag with incorrec
        warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
        warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
        EOF
 -      test_cmp expect out
 +      test_i18ncmp expect out
  '
  
  test_expect_success 'tag with bad tagger' '
        echo $tag >.git/refs/tags/wrong &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
 -      grep "error in tag .*: invalid author/committer" out
 +      test_i18ngrep "error in tag .*: invalid author/committer" out
  '
  
  test_expect_success 'tag with NUL in header' '
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
        cat out &&
 -      grep "error in tag $tag.*unterminated header: NUL at offset" out
 +      test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
  '
  
  test_expect_success 'cleaned up' '
@@@ -406,7 -406,7 +406,7 @@@ test_expect_success 'rev-list --verify-
  
        test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
        cat out &&
 -      test_i18ngrep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
 +      test_i18ngrep -q "error: hash mismatch 63ffffffffffffffffffffffffffffffffffffff" out
  '
  
  test_expect_success 'force fsck to ignore double author' '
@@@ -431,7 -431,7 +431,7 @@@ test_expect_success 'fsck notices blob 
               git hash-object -w --stdin -t tree) &&
          git fsck 2>out &&
          cat out &&
 -        grep "warning.*null sha1" out
 +        test_i18ngrep "warning.*null sha1" out
        )
  '
  
@@@ -442,7 -442,7 +442,7 @@@ test_expect_success 'fsck notices submo
               git hash-object -w --stdin -t tree) &&
          git fsck 2>out &&
          cat out &&
 -        grep "warning.*null sha1" out
 +        test_i18ngrep "warning.*null sha1" out
        )
  '
  
@@@ -463,7 -463,7 +463,7 @@@ while read name path pretty; d
                        bad_tree=$(git mktree <bad) &&
                        git fsck 2>out &&
                        cat out &&
 -                      grep "warning.*tree $bad_tree" out
 +                      test_i18ngrep "warning.*tree $bad_tree" out
                )'
        done <<-\EOF
        100644 blob
@@@ -509,9 -509,9 +509,9 @@@ test_expect_success 'NUL in commit' 
                git branch bad $(cat name) &&
  
                test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
 -              grep nulInCommit warn.1 &&
 +              test_i18ngrep nulInCommit warn.1 &&
                git fsck 2>warn.2 &&
 -              grep nulInCommit warn.2
 +              test_i18ngrep nulInCommit warn.2
        )
  '
  
@@@ -629,7 -629,7 +629,7 @@@ test_expect_success 'fsck --name-object
                remove_object $(git rev-parse julius:caesar.t) &&
                test_must_fail git fsck --name-objects >out &&
                tree=$(git rev-parse --verify julius:) &&
 -              egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
 +              test_i18ngrep -E "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
        )
  '
  
@@@ -640,7 -640,7 +640,7 @@@ test_expect_success 'alternate objects 
        mkdir alt.git/objects/12 &&
        >alt.git/objects/12/34567890123456789012345678901234567890 &&
        test_must_fail git fsck >out 2>&1 &&
 -      grep alt.git out
 +      test_i18ngrep alt.git out
  '
  
  test_expect_success 'fsck errors in packed objects' '
        remove_object $one &&
        remove_object $two &&
        test_must_fail git fsck 2>out &&
 -      grep "error in commit $one.* - bad name" out &&
 -      grep "error in commit $two.* - bad name" out &&
 +      test_i18ngrep "error in commit $one.* - bad name" out &&
 +      test_i18ngrep "error in commit $two.* - bad name" out &&
        ! grep corrupt out
  '
  
@@@ -740,7 -740,7 +740,7 @@@ test_expect_success 'fsck detects trunc
  # for each of type, we have one version which is referenced by another object
  # (and so while unreachable, not dangling), and another variant which really is
  # dangling.
- test_expect_success 'fsck notices dangling objects' '
+ test_expect_success 'create dangling-object repository' '
        git init dangling &&
        (
                cd dangling &&
                commit=$(git commit-tree $tree) &&
                dcommit=$(git commit-tree -p $commit $tree) &&
  
-               cat >expect <<-EOF &&
+               cat >expect <<-EOF
                dangling blob $dblob
                dangling commit $dcommit
                dangling tree $dtree
                EOF
+       )
+ '
  
+ test_expect_success 'fsck notices dangling objects' '
+       (
+               cd dangling &&
                git fsck >actual &&
                # the output order is non-deterministic, as it comes from a hash
                sort <actual >actual.sorted &&
 -              test_cmp expect actual.sorted
 +              test_i18ncmp expect actual.sorted
        )
  '
  
+ test_expect_success 'fsck --connectivity-only notices dangling objects' '
+       (
+               cd dangling &&
+               git fsck --connectivity-only >actual &&
+               # the output order is non-deterministic, as it comes from a hash
+               sort <actual >actual.sorted &&
+               test_i18ncmp expect actual.sorted
+       )
+ '
  test_expect_success 'fsck $name notices bogus $name' '
        test_must_fail git fsck bogus &&
        test_must_fail git fsck $ZERO_OID
@@@ -808,7 -823,7 +823,7 @@@ test_expect_success 'detect corrupt ind
        test_when_finished "mv .git/index.backup .git/index" &&
        corrupt_index_checksum &&
        test_must_fail git fsck --cache 2>errors &&
 -      grep "bad index file" errors
 +      test_i18ngrep "bad index file" errors
  '
  
  test_done