Merge branch 'km/empty-repo-is-still-a-repo'
authorJunio C Hamano <gitster@pobox.com>
Wed, 8 May 2019 15:37:23 +0000 (00:37 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 8 May 2019 15:37:23 +0000 (00:37 +0900)
Running "git add" on a repository created inside the current
repository is an explicit indication that the user wants to add it
as a submodule, but when the HEAD of the inner repository is on an
unborn branch, it cannot be added as a submodule. Worse, the files
in its working tree can be added as if they are a part of the outer
repository, which is not what the user wants. These problems are
being addressed.

* km/empty-repo-is-still-a-repo:
add: error appropriately on repository with no commits
dir: do not traverse repositories with no commits
submodule: refuse to add repository with no commits

1  2 
dir.c
git-submodule.sh
read-cache.c
diff --combined dir.c
index ad30a5a46dd8d902874d3c3185f905c13a18ba7f,a4e59eb351ca4a095d30e7ef4a8265080f1774c7..0a3ddcf2bc39daf88b6d3630360927610191f545
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -502,7 -502,8 +502,7 @@@ int submodule_path_match(const struct i
  }
  
  int report_path_error(const char *ps_matched,
 -                    const struct pathspec *pathspec,
 -                    const char *prefix)
 +                    const struct pathspec *pathspec)
  {
        /*
         * Make sure all pathspec matched; otherwise it is an error.
@@@ -1466,9 -1467,11 +1466,11 @@@ static enum path_treatment treat_direct
                        return path_none;
                }
                if (!(dir->flags & DIR_NO_GITLINKS)) {
-                       struct object_id oid;
-                       if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addstr(&sb, dirname);
+                       if (is_nonbare_repository_dir(&sb))
                                return exclude ? path_excluded : path_untracked;
+                       strbuf_release(&sb);
                }
                return path_recurse;
        }
@@@ -2544,9 -2547,13 +2546,9 @@@ struct ondisk_untracked_cache 
        struct stat_data info_exclude_stat;
        struct stat_data excludes_file_stat;
        uint32_t dir_flags;
 -      unsigned char info_exclude_sha1[20];
 -      unsigned char excludes_file_sha1[20];
 -      char exclude_per_dir[FLEX_ARRAY];
  };
  
  #define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x)
 -#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)
  
  struct write_data {
        int index;         /* number of written untracked_cache_dir */
@@@ -2629,21 -2636,20 +2631,21 @@@ void write_untracked_extension(struct s
        struct write_data wd;
        unsigned char varbuf[16];
        int varint_len;
 -      size_t len = strlen(untracked->exclude_per_dir);
 +      const unsigned hashsz = the_hash_algo->rawsz;
  
 -      FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
 +      ouc = xcalloc(1, sizeof(*ouc));
        stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
        stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
 -      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash);
 -      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
        ouc->dir_flags = htonl(untracked->dir_flags);
  
        varint_len = encode_varint(untracked->ident.len, varbuf);
        strbuf_add(out, varbuf, varint_len);
        strbuf_addbuf(out, &untracked->ident);
  
 -      strbuf_add(out, ouc, ouc_size(len));
 +      strbuf_add(out, ouc, sizeof(*ouc));
 +      strbuf_add(out, untracked->ss_info_exclude.oid.hash, hashsz);
 +      strbuf_add(out, untracked->ss_excludes_file.oid.hash, hashsz);
 +      strbuf_add(out, untracked->exclude_per_dir, strlen(untracked->exclude_per_dir) + 1);
        FREE_AND_NULL(ouc);
  
        if (!untracked->root) {
@@@ -2756,7 -2762,7 +2758,7 @@@ static int read_one_dir(struct untracke
        next = data + len + 1;
        if (next > rd->end)
                return -1;
 -      *untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len));
 +      *untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), len, 1));
        memcpy(untracked, &ud, sizeof(ud));
        memcpy(untracked->name, data, len + 1);
        data = next;
@@@ -2830,9 -2836,6 +2832,9 @@@ struct untracked_cache *read_untracked_
        int ident_len;
        ssize_t len;
        const char *exclude_per_dir;
 +      const unsigned hashsz = the_hash_algo->rawsz;
 +      const unsigned offset = sizeof(struct ondisk_untracked_cache);
 +      const unsigned exclude_per_dir_offset = offset + 2 * hashsz;
  
        if (sz <= 1 || end[-1] != '\0')
                return NULL;
        ident = (const char *)next;
        next += ident_len;
  
 -      if (next + ouc_size(0) > end)
 +      if (next + exclude_per_dir_offset + 1 > end)
                return NULL;
  
        uc = xcalloc(1, sizeof(*uc));
        strbuf_add(&uc->ident, ident, ident_len);
        load_oid_stat(&uc->ss_info_exclude,
                      next + ouc_offset(info_exclude_stat),
 -                    next + ouc_offset(info_exclude_sha1));
 +                    next + offset);
        load_oid_stat(&uc->ss_excludes_file,
                      next + ouc_offset(excludes_file_stat),
 -                    next + ouc_offset(excludes_file_sha1));
 +                    next + offset + hashsz);
        uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
 -      exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
 +      exclude_per_dir = (const char *)next + exclude_per_dir_offset;
        uc->exclude_per_dir = xstrdup(exclude_per_dir);
        /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
 -      next += ouc_size(strlen(exclude_per_dir));
 +      next += exclude_per_dir_offset + strlen(exclude_per_dir) + 1;
        if (next >= end)
                goto done2;
  
diff --combined git-submodule.sh
index e3c054bde5f1d6fb858071ba7bf2939253811e1f,16b49f1419f8431725edd841893753c4fddeb78d..c7f58c5756f7b6f78689d574c5a2177d1bd409b5
@@@ -5,13 -5,11 +5,13 @@@
  # Copyright (c) 2007 Lars Hjemli
  
  dashless=$(basename "$0" | sed -e 's/-/ /')
 -USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
 +USAGE="[--quiet] [--cached]
 +   or: $dashless [--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
     or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
     or: $dashless [--quiet] init [--] [<path>...]
     or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
     or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...]
 +   or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path>
     or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
     or: $dashless [--quiet] foreach [--recursive] <command>
     or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
@@@ -232,6 -230,13 +232,13 @@@ cmd_add(
                die "$(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
        fi
  
+       if test -d "$sm_path" &&
+               test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
+       then
+           git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
+           die "$(eval_gettext "'\$sm_path' does not have a commit checked out")"
+       fi
        if test -z "$force" &&
                ! git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" > /dev/null 2>&1
        then
@@@ -347,7 -352,7 +354,7 @@@ cmd_foreach(
                shift
        done
  
 -      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
 +      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} -- "$@"
  }
  
  #
@@@ -378,7 -383,7 +385,7 @@@ cmd_init(
                shift
        done
  
 -      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet}  "$@"
 +      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet} -- "$@"
  }
  
  #
@@@ -414,7 -419,7 +421,7 @@@ cmd_deinit(
                shift
        done
  
 -      git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
 +      git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} -- "$@"
  }
  
  is_tip_reachable () (
@@@ -543,7 -548,6 +550,7 @@@ cmd_update(
                ${depth:+--depth "$depth"} \
                $recommend_shallow \
                $jobs \
 +              -- \
                "$@" || echo "#unmatched" $?
        } | {
        err=
                                # is not reachable from a ref.
                                is_tip_reachable "$sm_path" "$sha1" ||
                                fetch_in_submodule "$sm_path" $depth ||
 -                              say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
 +                              say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'; trying to directly fetch \$sha1:")"
  
                                # Now we tried the usual fetch, but $sha1 may
                                # not be reachable from any of the refs
        }
  }
  
 +#
 +# Configures a submodule's default branch
 +#
 +# $@ = requested path
 +#
 +cmd_set_branch() {
 +      unset_branch=false
 +      branch=
 +
 +      while test $# -ne 0
 +      do
 +              case "$1" in
 +              -q|--quiet)
 +                      # we don't do anything with this but we need to accept it
 +                      ;;
 +              -d|--default)
 +                      unset_branch=true
 +                      ;;
 +              -b|--branch)
 +                      case "$2" in '') usage ;; esac
 +                      branch=$2
 +                      shift
 +                      ;;
 +              --)
 +                      shift
 +                      break
 +                      ;;
 +              -*)
 +                      usage
 +                      ;;
 +              *)
 +                      break
 +                      ;;
 +              esac
 +              shift
 +      done
 +
 +      if test $# -ne 1
 +      then
 +              usage
 +      fi
 +
 +      # we can't use `git submodule--helper name` here because internally, it
 +      # hashes the path so a trailing slash could lead to an unintentional no match
 +      name="$(git submodule--helper list "$1" | cut -f2)"
 +      if test -z "$name"
 +      then
 +              exit 1
 +      fi
 +
 +      test -n "$branch"; has_branch=$?
 +      test "$unset_branch" = true; has_unset_branch=$?
 +
 +      if test $((!$has_branch != !$has_unset_branch)) -eq 0
 +      then
 +              usage
 +      fi
 +
 +      if test $has_branch -eq 0
 +      then
 +              git submodule--helper config submodule."$name".branch "$branch"
 +      else
 +              git submodule--helper config --unset submodule."$name".branch
 +      fi
 +}
 +
  #
  # Show commit summary for submodules in index or working tree
  #
@@@ -1002,7 -940,7 +1009,7 @@@ cmd_status(
                shift
        done
  
 -      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} "$@"
 +      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} -- "$@"
  }
  #
  # Sync remote urls for submodules
@@@ -1035,7 -973,7 +1042,7 @@@ cmd_sync(
                esac
        done
  
 -      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
 +      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} -- "$@"
  }
  
  cmd_absorbgitdirs()
  while test $# != 0 && test -z "$command"
  do
        case "$1" in
 -      add | foreach | init | deinit | update | status | summary | sync | absorbgitdirs)
 +      add | foreach | init | deinit | update | set-branch | status | summary | sync | absorbgitdirs)
                command=$1
                ;;
        -q|--quiet)
@@@ -1093,8 -1031,8 +1100,8 @@@ the
      fi
  fi
  
 -# "-b branch" is accepted only by "add"
 -if test -n "$branch" && test "$command" != add
 +# "-b branch" is accepted only by "add" and "set-branch"
 +if test -n "$branch" && (test "$command" != add || test "$command" != set-branch)
  then
        usage
  fi
@@@ -1105,4 -1043,4 +1112,4 @@@ the
        usage
  fi
  
 -"cmd_$command" "$@"
 +"cmd_$(echo $command | sed -e s/-/_/g)" "$@"
diff --combined read-cache.c
index d5a74b1861eb66861f2394f477775320efb07be7,5511a2b14c8ecf1a4bb7464ccc3ff9ddc0a03f94..61b043bac3f2e26088177d13adc5ee5fe5e5c2e7
@@@ -17,7 -17,6 +17,7 @@@
  #include "commit.h"
  #include "blob.h"
  #include "resolve-undo.h"
 +#include "run-command.h"
  #include "strbuf.h"
  #include "varint.h"
  #include "split-index.h"
@@@ -589,19 -588,13 +589,19 @@@ int remove_index_entry_at(struct index_
   * CE_REMOVE is set in ce_flags.  This is much more effective than
   * calling remove_index_entry_at() for each entry to be removed.
   */
 -void remove_marked_cache_entries(struct index_state *istate)
 +void remove_marked_cache_entries(struct index_state *istate, int invalidate)
  {
        struct cache_entry **ce_array = istate->cache;
        unsigned int i, j;
  
        for (i = j = 0; i < istate->cache_nr; i++) {
                if (ce_array[i]->ce_flags & CE_REMOVE) {
 +                      if (invalidate) {
 +                              cache_tree_invalidate_path(istate,
 +                                                         ce_array[i]->name);
 +                              untracked_cache_remove_from_index(istate,
 +                                                                ce_array[i]->name);
 +                      }
                        remove_name_hash(istate, ce_array[i]);
                        save_or_free_index_entry(istate, ce_array[i]);
                }
@@@ -709,6 -702,7 +709,7 @@@ int add_to_index(struct index_state *is
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
        int hash_flags = HASH_WRITE_OBJECT;
+       struct object_id oid;
  
        if (flags & ADD_CACHE_RENORMALIZE)
                hash_flags |= HASH_RENORMALIZE;
  
        namelen = strlen(path);
        if (S_ISDIR(st_mode)) {
+               if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+                       return error(_("'%s' does not have a commit checked out"), path);
                while (namelen && path[namelen-1] == '/')
                        namelen--;
        }
@@@ -1641,24 -1637,39 +1644,24 @@@ struct ondisk_cache_entry 
        uint32_t uid;
        uint32_t gid;
        uint32_t size;
 -      unsigned char sha1[20];
 -      uint16_t flags;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -/*
 - * This struct is used when CE_EXTENDED bit is 1
 - * The struct must match ondisk_cache_entry exactly from
 - * ctime till flags
 - */
 -struct ondisk_cache_entry_extended {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      uint32_t dev;
 -      uint32_t ino;
 -      uint32_t mode;
 -      uint32_t uid;
 -      uint32_t gid;
 -      uint32_t size;
 -      unsigned char sha1[20];
 -      uint16_t flags;
 -      uint16_t flags2;
 -      char name[FLEX_ARRAY]; /* more */
 +      /*
 +       * unsigned char hash[hashsz];
 +       * uint16_t flags;
 +       * if (flags & CE_EXTENDED)
 +       *      uint16_t flags2;
 +       */
 +      unsigned char data[GIT_MAX_RAWSZ + 2 * sizeof(uint16_t)];
 +      char name[FLEX_ARRAY];
  };
  
  /* These are only used for v3 or lower */
  #define align_padding_size(size, len) ((size + (len) + 8) & ~7) - (size + len)
 -#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
 +#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,data) + (len) + 8) & ~7)
  #define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
 -#define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
 -#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
 -                          ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
 -                          ondisk_cache_entry_size(ce_namelen(ce)))
 +#define ondisk_data_size(flags, len) (the_hash_algo->rawsz + \
 +                                   ((flags & CE_EXTENDED) ? 2 : 1) * sizeof(uint16_t) + len)
 +#define ondisk_data_size_max(len) (ondisk_data_size(CE_EXTENDED, len))
 +#define ondisk_ce_size(ce) (ondisk_cache_entry_size(ondisk_data_size((ce)->ce_flags, ce_namelen(ce))))
  
  /* Allow fsck to force verification of the index checksum. */
  int verify_index_checksum;
@@@ -1732,8 -1743,6 +1735,8 @@@ static struct cache_entry *create_from_
        struct cache_entry *ce;
        size_t len;
        const char *name;
 +      const unsigned hashsz = the_hash_algo->rawsz;
 +      const uint16_t *flagsp = (const uint16_t *)(ondisk->data + hashsz);
        unsigned int flags;
        size_t copy_len = 0;
        /*
        int expand_name_field = version == 4;
  
        /* On-disk flags are just 16 bits */
 -      flags = get_be16(&ondisk->flags);
 +      flags = get_be16(flagsp);
        len = flags & CE_NAMEMASK;
  
        if (flags & CE_EXTENDED) {
 -              struct ondisk_cache_entry_extended *ondisk2;
                int extended_flags;
 -              ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
 -              extended_flags = get_be16(&ondisk2->flags2) << 16;
 +              extended_flags = get_be16(flagsp + 1) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
                        die(_("unknown index entry format 0x%08x"), extended_flags);
                flags |= extended_flags;
 -              name = ondisk2->name;
 +              name = (const char *)(flagsp + 2);
        }
        else
 -              name = ondisk->name;
 +              name = (const char *)(flagsp + 1);
  
        if (expand_name_field) {
                const unsigned char *cp = (const unsigned char *)name;
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        ce->index = 0;
 -      hashcpy(ce->oid.hash, ondisk->sha1);
 +      hashcpy(ce->oid.hash, ondisk->data);
 +      memcpy(ce->name, name, len);
 +      ce->name[len] = '\0';
  
        if (expand_name_field) {
                if (copy_len)
@@@ -2214,16 -2223,6 +2217,16 @@@ int do_read_index(struct index_state *i
                load_index_extensions(&p);
        }
        munmap((void *)mmap, mmap_size);
 +
 +      /*
 +       * TODO trace2: replace "the_repository" with the actual repo instance
 +       * that is associated with the given "istate".
 +       */
 +      trace2_data_intmax("index", the_repository, "read/version",
 +                         istate->version);
 +      trace2_data_intmax("index", the_repository, "read/cache_nr",
 +                         istate->cache_nr);
 +
        return istate->cache_nr;
  
  unmap:
@@@ -2255,17 -2254,9 +2258,17 @@@ int read_index_from(struct index_state 
        if (istate->initialized)
                return istate->cache_nr;
  
 +      /*
 +       * TODO trace2: replace "the_repository" with the actual repo instance
 +       * that is associated with the given "istate".
 +       */
 +      trace2_region_enter_printf("index", "do_read_index", the_repository,
 +                                 "%s", path);
        trace_performance_enter();
        ret = do_read_index(istate, path, 0);
        trace_performance_leave("read cache %s", path);
 +      trace2_region_leave_printf("index", "do_read_index", the_repository,
 +                                 "%s", path);
  
        split_index = istate->split_index;
        if (!split_index || is_null_oid(&split_index->base_oid)) {
  
        base_oid_hex = oid_to_hex(&split_index->base_oid);
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
 +      trace2_region_enter_printf("index", "shared/do_read_index",
 +                                 the_repository, "%s", base_path);
        ret = do_read_index(split_index->base, base_path, 1);
 +      trace2_region_leave_printf("index", "shared/do_read_index",
 +                                 the_repository, "%s", base_path);
        if (!oideq(&split_index->base_oid, &split_index->base->oid))
                die(_("broken index, expect %s in %s, got %s"),
                    base_oid_hex, base_path,
@@@ -2544,8 -2531,6 +2547,8 @@@ static void copy_cache_entry_to_ondisk(
                                       struct cache_entry *ce)
  {
        short flags;
 +      const unsigned hashsz = the_hash_algo->rawsz;
 +      uint16_t *flagsp = (uint16_t *)(ondisk->data + hashsz);
  
        ondisk->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
        ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
        ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
        ondisk->size = htonl(ce->ce_stat_data.sd_size);
 -      hashcpy(ondisk->sha1, ce->oid.hash);
 +      hashcpy(ondisk->data, ce->oid.hash);
  
        flags = ce->ce_flags & ~CE_NAMEMASK;
        flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
 -      ondisk->flags = htons(flags);
 +      flagsp[0] = htons(flags);
        if (ce->ce_flags & CE_EXTENDED) {
 -              struct ondisk_cache_entry_extended *ondisk2;
 -              ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
 -              ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
 +              flagsp[1] = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
        }
  }
  
@@@ -2582,7 -2569,10 +2585,7 @@@ static int ce_write_entry(git_hash_ctx 
                stripped_name = 1;
        }
  
 -      if (ce->ce_flags & CE_EXTENDED)
 -              size = offsetof(struct ondisk_cache_entry_extended, name);
 -      else
 -              size = offsetof(struct ondisk_cache_entry, name);
 +      size = offsetof(struct ondisk_cache_entry,data) + ondisk_data_size(ce->ce_flags, 0);
  
        if (!previous_name) {
                int len = ce_namelen(ce);
@@@ -2740,7 -2730,7 +2743,7 @@@ static int do_write_index(struct index_
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
        struct stat st;
 -      struct ondisk_cache_entry_extended ondisk;
 +      struct ondisk_cache_entry ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = istate->drop_cache_tree;
        off_t offset;
                        return -1;
        }
  
 -      if (!strip_extensions && istate->split_index) {
 +      if (!strip_extensions && istate->split_index &&
 +          !is_null_oid(&istate->split_index->base_oid)) {
                struct strbuf sb = STRBUF_INIT;
  
                err = write_link_extension(&sb, istate) < 0 ||
        istate->timestamp.sec = (unsigned int)st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
        trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
 +
 +      /*
 +       * TODO trace2: replace "the_repository" with the actual repo instance
 +       * that is associated with the given "istate".
 +       */
 +      trace2_data_intmax("index", the_repository, "write/version",
 +                         istate->version);
 +      trace2_data_intmax("index", the_repository, "write/cache_nr",
 +                         istate->cache_nr);
 +
        return 0;
  }
  
@@@ -3019,32 -2998,12 +3022,32 @@@ static int commit_locked_index(struct l
  static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
  {
 -      int ret = do_write_index(istate, lock->tempfile, 0);
 +      int ret;
 +
 +      /*
 +       * TODO trace2: replace "the_repository" with the actual repo instance
 +       * that is associated with the given "istate".
 +       */
 +      trace2_region_enter_printf("index", "do_write_index", the_repository,
 +                                 "%s", lock->tempfile->filename.buf);
 +      ret = do_write_index(istate, lock->tempfile, 0);
 +      trace2_region_leave_printf("index", "do_write_index", the_repository,
 +                                 "%s", lock->tempfile->filename.buf);
 +
        if (ret)
                return ret;
        if (flags & COMMIT_LOCK)
 -              return commit_locked_index(lock);
 -      return close_lock_file_gently(lock);
 +              ret = commit_locked_index(lock);
 +      else
 +              ret = close_lock_file_gently(lock);
 +
 +      run_hook_le(NULL, "post-index-change",
 +                      istate->updated_workdir ? "1" : "0",
 +                      istate->updated_skipworktree ? "1" : "0", NULL);
 +      istate->updated_workdir = 0;
 +      istate->updated_skipworktree = 0;
 +
 +      return ret;
  }
  
  static int write_split_index(struct index_state *istate,
@@@ -3124,13 -3083,7 +3127,13 @@@ static int write_shared_index(struct in
        int ret;
  
        move_cache_to_base_index(istate);
 +
 +      trace2_region_enter_printf("index", "shared/do_write_index",
 +                                 the_repository, "%s", (*temp)->filename.buf);
        ret = do_write_index(si->base, *temp, 1);
 +      trace2_region_enter_printf("index", "shared/do_write_index",
 +                                 the_repository, "%s", (*temp)->filename.buf);
 +
        if (ret)
                return ret;
        ret = adjust_shared_perm(get_tempfile_path(*temp));
@@@ -3239,7 -3192,7 +3242,7 @@@ int write_locked_index(struct index_sta
        ret = write_split_index(istate, lock, flags);
  
        /* Freshen the shared index only if the split-index was written */
 -      if (!ret && !new_shared_index) {
 +      if (!ret && !new_shared_index && !is_null_oid(&si->base_oid)) {
                const char *shared_index = git_path("sharedindex.%s",
                                                    oid_to_hex(&si->base_oid));
                freshen_shared_index(shared_index, 1);