Merge branch 'hv/submodule-alt-odb'
authorJunio C Hamano <gitster@pobox.com>
Wed, 23 May 2012 20:35:05 +0000 (13:35 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 23 May 2012 20:35:06 +0000 (13:35 -0700)
When peeking into object stores of submodules, the code forgot that they
might borrow objects from alternate object stores on their own.

By Heiko Voigt
* hv/submodule-alt-odb:
teach add_submodule_odb() to look for alternates

1  2 
cache.h
sha1_file.c
submodule.c
diff --combined cache.h
index e14ffcd914be8759a5f5a71fa7fc71e4f9774f0e,3a28cb13b3790e1752615ec1aa4be948edfd4390..cc5048c202b6799591e7121de91ee2c6cdce9997
+++ b/cache.h
@@@ -105,9 -105,6 +105,9 @@@ struct cache_header 
        unsigned int hdr_entries;
  };
  
 +#define INDEX_FORMAT_LB 2
 +#define INDEX_FORMAT_UB 4
 +
  /*
   * The "cache_time" is just the low 32 bits of the
   * time. It doesn't matter if it overflows - we only
@@@ -118,6 -115,48 +118,6 @@@ struct cache_time 
        unsigned int nsec;
  };
  
 -/*
 - * dev/ino/uid/gid/size are also just tracked to the low 32 bits
 - * Again - this is just a (very strong in practice) heuristic that
 - * the inode hasn't changed.
 - *
 - * We save the fields in big-endian order to allow using the
 - * index file over NFS transparently.
 - */
 -struct ondisk_cache_entry {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short 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;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      unsigned short flags2;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
  struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
@@@ -214,6 -253,9 +214,6 @@@ static inline size_t ce_namelen(const s
  }
  
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#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 ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@@ -264,11 -306,13 +264,11 @@@ static inline unsigned int canon_mode(u
        return S_IFGITLINK;
  }
  
 -#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 -#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
 -#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
  struct index_state {
        struct cache_entry **cache;
 +      unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
@@@ -580,10 -624,8 +580,10 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 +      PUSH_DEFAULT_SIMPLE,
        PUSH_DEFAULT_UPSTREAM,
 -      PUSH_DEFAULT_CURRENT
 +      PUSH_DEFAULT_CURRENT,
 +      PUSH_DEFAULT_UNSPECIFIED
  };
  
  extern enum branch_track git_branch_track;
@@@ -666,19 -708,6 +666,19 @@@ static inline void hashclr(unsigned cha
  #define EMPTY_TREE_SHA1_BIN \
         ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
 +#define EMPTY_BLOB_SHA1_HEX \
 +      "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
 +#define EMPTY_BLOB_SHA1_BIN_LITERAL \
 +      "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
 +      "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 +#define EMPTY_BLOB_SHA1_BIN \
 +      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +
 +static inline int is_empty_blob_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +}
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
@@@ -877,8 -906,10 +877,8 @@@ enum date_mode 
  };
  
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 -const char *show_date_relative(unsigned long time, int tz,
 -                             const struct timeval *now,
 -                             char *timebuf,
 -                             size_t timebuf_size);
 +void show_date_relative(unsigned long time, int tz, const struct timeval *now,
 +                      struct strbuf *timebuf);
  int parse_date(const char *date, char *buf, int bufsize);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
@@@ -897,22 -928,6 +897,22 @@@ extern const char *fmt_name(const char 
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
  
 +struct ident_split {
 +      const char *name_begin;
 +      const char *name_end;
 +      const char *mail_begin;
 +      const char *mail_end;
 +      const char *date_begin;
 +      const char *date_end;
 +      const char *tz_begin;
 +      const char *tz_end;
 +};
 +/*
 + * Signals an success with 0, but time part of the result may be NULL
 + * if the input lacks timestamp and zone
 + */
 +extern int split_ident_line(struct ident_split *, const char *, int);
 +
  struct checkout {
        const char *base_dir;
        int base_dir_len;
@@@ -935,9 -950,7 +935,9 @@@ struct cache_def 
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
  extern int check_leading_path(const char *name, int len);
 +extern int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 +extern int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -947,6 -960,7 +947,7 @@@ extern struct alternate_object_databas
        char base[FLEX_ARRAY]; /* more */
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
+ extern void read_info_alternates(const char * relative_base, int depth);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern void foreach_alt_odb(alt_odb_fn, void*);
@@@ -1102,8 -1116,6 +1103,8 @@@ extern int git_config_from_file(config_
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_with_options(config_fn_t fn, void *,
 +                                 const char *filename, int respect_includes);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
@@@ -1119,7 -1131,6 +1120,7 @@@ extern int git_config_parse_key(const c
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
 +extern int git_config_rename_section_in_file(const char *, const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
@@@ -1130,13 -1141,7 +1131,13 @@@ extern const char *get_commit_output_en
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
  
 -extern const char *config_exclusive_filename;
 +struct config_include_data {
 +      int depth;
 +      config_fn_t fn;
 +      void *data;
 +};
 +#define CONFIG_INCLUDE_INIT { 0 }
 +extern int git_config_include(const char *name, const char *value, void *data);
  
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
@@@ -1174,7 -1179,6 +1175,7 @@@ extern const char *pager_program
  extern int pager_in_use(void);
  extern int pager_use_color;
  extern int term_columns(void);
 +extern int decimal_width(int);
  
  extern const char *editor_program;
  extern const char *askpass_program;
@@@ -1261,6 -1265,4 +1262,6 @@@ extern struct startup_info *startup_inf
  /* builtin/merge.c */
  int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
  
 +int sane_execvp(const char *file, char *const argv[]);
 +
  #endif /* CACHE_H */
diff --combined sha1_file.c
index 3c4f1652f13479238dee56674cbb86d299363be1,f615fc72064ef17f989ebdb5bb6a3ac2256d872c..4ccaf7ac197c28400eddd496abcfc725528ca32b
@@@ -19,7 -19,6 +19,7 @@@
  #include "pack-revindex.h"
  #include "sha1-lookup.h"
  #include "bulk-checkin.h"
 +#include "streaming.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -229,7 -228,6 +229,6 @@@ char *sha1_pack_index_name(const unsign
  struct alternate_object_database *alt_odb_list;
  static struct alternate_object_database **alt_odb_tail;
  
- static void read_info_alternates(const char * alternates, int depth);
  static int git_open_noatime(const char *name);
  
  /*
@@@ -354,7 -352,7 +353,7 @@@ static void link_alt_odb_entries(const 
        }
  }
  
static void read_info_alternates(const char * relative_base, int depth)
+ void read_info_alternates(const char * relative_base, int depth)
  {
        char *map;
        size_t mapsz;
@@@ -1147,47 -1145,10 +1146,47 @@@ static const struct packed_git *has_pac
        return NULL;
  }
  
 -int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
 +/*
 + * With an in-core object data in "map", rehash it to make sure the
 + * object name actually matches "sha1" to detect object corruption.
 + * With "map" == NULL, try reading the object named with "sha1" using
 + * the streaming interface and rehash it to do the same.
 + */
 +int check_sha1_signature(const unsigned char *sha1, void *map,
 +                       unsigned long size, const char *type)
  {
        unsigned char real_sha1[20];
 -      hash_sha1_file(map, size, type, real_sha1);
 +      enum object_type obj_type;
 +      struct git_istream *st;
 +      git_SHA_CTX c;
 +      char hdr[32];
 +      int hdrlen;
 +
 +      if (map) {
 +              hash_sha1_file(map, size, type, real_sha1);
 +              return hashcmp(sha1, real_sha1) ? -1 : 0;
 +      }
 +
 +      st = open_istream(sha1, &obj_type, &size, NULL);
 +      if (!st)
 +              return -1;
 +
 +      /* Generate the header */
 +      hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1;
 +
 +      /* Sha1.. */
 +      git_SHA1_Init(&c);
 +      git_SHA1_Update(&c, hdr, hdrlen);
 +      for (;;) {
 +              char buf[1024 * 16];
 +              ssize_t readlen = read_istream(st, buf, sizeof(buf));
 +
 +              if (!readlen)
 +                      break;
 +              git_SHA1_Update(&c, buf, readlen);
 +      }
 +      git_SHA1_Final(real_sha1, &c);
 +      close_istream(st);
        return hashcmp(sha1, real_sha1) ? -1 : 0;
  }
  
@@@ -2417,7 -2378,7 +2416,7 @@@ int move_temp_to_file(const char *tmpfi
        unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
 -                      return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret));
 +                      return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
                }
                /* FIXME!!! Collision check here ? */
        }
@@@ -2509,9 -2470,9 +2508,9 @@@ static int write_loose_object(const uns
        fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
        if (fd < 0) {
                if (errno == EACCES)
 -                      return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
 +                      return error("insufficient permission for adding an object to repository database %s", get_object_directory());
                else
 -                      return error("unable to create temporary sha1 filename %s: %s\n", tmp_file, strerror(errno));
 +                      return error("unable to create temporary file: %s", strerror(errno));
        }
  
        /* Set it up */
diff --combined submodule.c
index 784b58039dd078fc1e0c4554820cd0e99c8e41d2,b63978a161c84101b41728d95f48326de897b12d..959d349ea7426344289020ee4216785fa65ec1e6
@@@ -63,6 -63,9 +63,9 @@@ static int add_submodule_odb(const cha
        alt_odb->name[40] = '\0';
        alt_odb->name[41] = '\0';
        alt_odb_list = alt_odb;
+       /* add possible alternates from the submodule */
+       read_info_alternates(objects_directory.buf, 0);
        prepare_alt_odb();
  done:
        strbuf_release(&objects_directory);
@@@ -357,19 -360,21 +360,19 @@@ static void collect_submodules_from_dif
                                         void *data)
  {
        int i;
 -      int *needs_pushing = data;
 +      struct string_list *needs_pushing = data;
  
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                if (!S_ISGITLINK(p->two->mode))
                        continue;
 -              if (submodule_needs_pushing(p->two->path, p->two->sha1)) {
 -                      *needs_pushing = 1;
 -                      break;
 -              }
 +              if (submodule_needs_pushing(p->two->path, p->two->sha1))
 +                      string_list_insert(needs_pushing, p->two->path);
        }
  }
  
 -
 -static void commit_need_pushing(struct commit *commit, int *needs_pushing)
 +static void find_unpushed_submodule_commits(struct commit *commit,
 +              struct string_list *needs_pushing)
  {
        struct rev_info rev;
  
        diff_tree_combined_merge(commit, 1, &rev);
  }
  
 -int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
 +int find_unpushed_submodules(unsigned char new_sha1[20],
 +              const char *remotes_name, struct string_list *needs_pushing)
  {
        struct rev_info rev;
        struct commit *commit;
        const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
        int argc = ARRAY_SIZE(argv) - 1;
        char *sha1_copy;
 -      int needs_pushing = 0;
 +
        struct strbuf remotes_arg = STRBUF_INIT;
  
        strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
  
 -      while ((commit = get_revision(&rev)) && !needs_pushing)
 -              commit_need_pushing(commit, &needs_pushing);
 +      while ((commit = get_revision(&rev)) != NULL)
 +              find_unpushed_submodule_commits(commit, needs_pushing);
  
 +      reset_revision_walk();
        free(sha1_copy);
        strbuf_release(&remotes_arg);
  
 -      return needs_pushing;
 +      return needs_pushing->nr;
 +}
 +
 +static int push_submodule(const char *path)
 +{
 +      if (add_submodule_odb(path))
 +              return 1;
 +
 +      if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 +              struct child_process cp;
 +              const char *argv[] = {"push", NULL};
 +
 +              memset(&cp, 0, sizeof(cp));
 +              cp.argv = argv;
 +              cp.env = local_repo_env;
 +              cp.git_cmd = 1;
 +              cp.no_stdin = 1;
 +              cp.dir = path;
 +              if (run_command(&cp))
 +                      return 0;
 +              close(cp.out);
 +      }
 +
 +      return 1;
 +}
 +
 +int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
 +{
 +      int i, ret = 1;
 +      struct string_list needs_pushing;
 +
 +      memset(&needs_pushing, 0, sizeof(struct string_list));
 +      needs_pushing.strdup_strings = 1;
 +
 +      if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
 +              return 1;
 +
 +      for (i = 0; i < needs_pushing.nr; i++) {
 +              const char *path = needs_pushing.items[i].string;
 +              fprintf(stderr, "Pushing submodule '%s'\n", path);
 +              if (!push_submodule(path)) {
 +                      fprintf(stderr, "Unable to push submodule '%s'\n", path);
 +                      ret = 0;
 +              }
 +      }
 +
 +      string_list_clear(&needs_pushing, 0);
 +
 +      return ret;
  }
  
  static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
@@@ -789,7 -744,6 +792,7 @@@ static int find_first_merges(struct obj
                if (in_merge_bases(b, &commit, 1))
                        add_object_array(o, NULL, &merges);
        }
 +      reset_revision_walk();
  
        /* Now we've got all merges that contain a and b. Prune all
         * merges that contain another found merge and save them in