Merge branch 'jk/prune-mtime'
authorJunio C Hamano <gitster@pobox.com>
Wed, 29 Oct 2014 17:07:56 +0000 (10:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 Oct 2014 17:07:56 +0000 (10:07 -0700)
Tighten the logic to decide that an unreachable cruft is
sufficiently old by covering corner cases such as an ancient object
becoming reachable and then going unreachable again, in which case
its retention period should be prolonged.

* jk/prune-mtime: (28 commits)
drop add_object_array_with_mode
revision: remove definition of unused 'add_object' function
pack-objects: double-check options before discarding objects
repack: pack objects mentioned by the index
pack-objects: use argv_array
reachable: use revision machinery's --indexed-objects code
rev-list: add --indexed-objects option
rev-list: document --reflog option
t5516: test pushing a tag of an otherwise unreferenced blob
traverse_commit_list: support pending blobs/trees with paths
make add_object_array_with_context interface more sane
write_sha1_file: freshen existing objects
pack-objects: match prune logic for discarding objects
pack-objects: refactor unpack-unreachable expiration check
prune: keep objects reachable from recent objects
sha1_file: add for_each iterators for loose and packed objects
count-objects: use for_each_loose_file_in_objdir
count-objects: do not use xsize_t when counting object size
prune-packed: use for_each_loose_file_in_objdir
reachable: mark index blobs as SEEN
...

1  2 
builtin/pack-objects.c
builtin/reflog.c
cache.h
git-compat-util.h
sha1_file.c
diff --combined builtin/pack-objects.c
index 78c659a6b44f73db70b32ae5f3fc032e7a5c639b,64123d4222187afcf3bede5def93157bdff2748d..3f9f5c7760a63bc2deb32b75f500ac0d1bc87538
@@@ -20,6 -20,9 +20,9 @@@
  #include "streaming.h"
  #include "thread-utils.h"
  #include "pack-bitmap.h"
+ #include "reachable.h"
+ #include "sha1-array.h"
+ #include "argv-array.h"
  
  static const char *pack_usage[] = {
        N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@@ -811,7 -814,6 +814,7 @@@ static void write_pack_file(void
                        fixup_pack_header_footer(fd, sha1, pack_tmp_name,
                                                 nr_written, sha1, offset);
                        close(fd);
 +                      write_bitmap_index = 0;
                }
  
                if (!pack_to_stdout) {
@@@ -1973,6 -1975,8 +1976,6 @@@ static void ll_find_deltas(struct objec
  
        init_threaded_search();
  
 -      if (!delta_search_threads)      /* --threads=0 means autodetect */
 -              delta_search_threads = online_cpus();
        if (delta_search_threads <= 1) {
                find_deltas(list, &list_size, window, depth, processed);
                cleanup_threaded_search();
@@@ -2406,6 -2410,27 +2409,27 @@@ static int has_sha1_pack_kept_or_nonloc
        return 0;
  }
  
+ /*
+  * Store a list of sha1s that are should not be discarded
+  * because they are either written too recently, or are
+  * reachable from another object that was.
+  *
+  * This is filled by get_object_list.
+  */
+ static struct sha1_array recent_objects;
+ static int loosened_object_can_be_discarded(const unsigned char *sha1,
+                                           unsigned long mtime)
+ {
+       if (!unpack_unreachable_expiration)
+               return 0;
+       if (mtime > unpack_unreachable_expiration)
+               return 0;
+       if (sha1_array_lookup(&recent_objects, sha1) >= 0)
+               return 0;
+       return 1;
+ }
  static void loosen_unused_packed_objects(struct rev_info *revs)
  {
        struct packed_git *p;
                if (!p->pack_local || p->pack_keep)
                        continue;
  
-               if (unpack_unreachable_expiration &&
-                   p->mtime < unpack_unreachable_expiration)
-                       continue;
                if (open_pack_index(p))
                        die("cannot open pack index");
  
                for (i = 0; i < p->num_objects; i++) {
                        sha1 = nth_packed_object_sha1(p, i);
                        if (!packlist_find(&to_pack, sha1, NULL) &&
-                               !has_sha1_pack_kept_or_nonlocal(sha1))
+                           !has_sha1_pack_kept_or_nonlocal(sha1) &&
+                           !loosened_object_can_be_discarded(sha1, p->mtime))
                                if (force_object_loose(sha1, p->mtime))
                                        die("unable to force loose object");
                }
@@@ -2462,6 -2484,19 +2483,19 @@@ static int get_object_list_from_bitmap(
        return 0;
  }
  
+ static void record_recent_object(struct object *obj,
+                                const struct name_path *path,
+                                const char *last,
+                                void *data)
+ {
+       sha1_array_append(&recent_objects, obj->sha1);
+ }
+ static void record_recent_commit(struct commit *commit, void *data)
+ {
+       sha1_array_append(&recent_objects, commit->object.sha1);
+ }
  static void get_object_list(int ac, const char **av)
  {
        struct rev_info revs;
        mark_edges_uninteresting(&revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object, NULL);
  
+       if (unpack_unreachable_expiration) {
+               revs.ignore_missing_links = 1;
+               if (add_unseen_recent_objects_to_traversal(&revs,
+                               unpack_unreachable_expiration))
+                       die("unable to add recent objects");
+               if (prepare_revision_walk(&revs))
+                       die("revision walk setup failed");
+               traverse_commit_list(&revs, record_recent_commit,
+                                    record_recent_object, NULL);
+       }
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
+       sha1_array_clear(&recent_objects);
  }
  
  static int option_parse_index_version(const struct option *opt,
@@@ -2567,9 -2615,9 +2614,9 @@@ int cmd_pack_objects(int argc, const ch
        int use_internal_rev_list = 0;
        int thin = 0;
        int all_progress_implied = 0;
-       const char *rp_av[6];
-       int rp_ac = 0;
+       struct argv_array rp = ARGV_ARRAY_INIT;
        int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
+       int rev_list_index = 0;
        struct option pack_objects_options[] = {
                OPT_SET_INT('q', "quiet", &progress,
                            N_("do not show progress meter"), 0),
                { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
                  N_("include objects referred by reflog entries"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+               { OPTION_SET_INT, 0, "indexed-objects", &rev_list_index, NULL,
+                 N_("include objects referred to by the index"),
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
                OPT_BOOL(0, "stdout", &pack_to_stdout,
                         N_("output pack to stdout")),
                OPT_BOOL(0, "include-tag", &include_tag,
        if (pack_to_stdout != !base_name || argc)
                usage_with_options(pack_usage, pack_objects_options);
  
-       rp_av[rp_ac++] = "pack-objects";
+       argv_array_push(&rp, "pack-objects");
        if (thin) {
                use_internal_rev_list = 1;
-               rp_av[rp_ac++] = "--objects-edge";
+               argv_array_push(&rp, "--objects-edge");
        } else
-               rp_av[rp_ac++] = "--objects";
+               argv_array_push(&rp, "--objects");
  
        if (rev_list_all) {
                use_internal_rev_list = 1;
-               rp_av[rp_ac++] = "--all";
+               argv_array_push(&rp, "--all");
        }
        if (rev_list_reflog) {
                use_internal_rev_list = 1;
-               rp_av[rp_ac++] = "--reflog";
+               argv_array_push(&rp, "--reflog");
+       }
+       if (rev_list_index) {
+               use_internal_rev_list = 1;
+               argv_array_push(&rp, "--indexed-objects");
        }
        if (rev_list_unpacked) {
                use_internal_rev_list = 1;
-               rp_av[rp_ac++] = "--unpacked";
+               argv_array_push(&rp, "--unpacked");
        }
  
        if (!reuse_object)
                pack_compression_level = Z_DEFAULT_COMPRESSION;
        else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
                die("bad pack compression level %d", pack_compression_level);
 +
 +      if (!delta_search_threads)      /* --threads=0 means autodetect */
 +              delta_search_threads = online_cpus();
 +
  #ifdef NO_PTHREADS
        if (delta_search_threads != 1)
                warning("no threads support, ignoring --threads");
  
        if (keep_unreachable && unpack_unreachable)
                die("--keep-unreachable and --unpack-unreachable are incompatible.");
+       if (!rev_list_all || !rev_list_reflog || !rev_list_index)
+               unpack_unreachable_expiration = 0;
  
        if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
                use_bitmap_index = 0;
        if (!use_internal_rev_list)
                read_object_list_from_stdin();
        else {
-               rp_av[rp_ac] = NULL;
-               get_object_list(rp_ac, rp_av);
+               get_object_list(rp.argc, rp.argv);
+               argv_array_clear(&rp);
        }
        cleanup_preferred_base();
        if (include_tag && nr_result)
diff --combined builtin/reflog.c
index b6388f75b0cffcce03b9eee03be966ce67806310,80bddc259b7237c1c2e337a59a48573be4ee6d08..2d85d260ca66deb060b837f86d81a7aad2761b69
@@@ -1,5 -1,5 +1,5 @@@
 -#include "cache.h"
  #include "builtin.h"
 +#include "lockfile.h"
  #include "commit.h"
  #include "refs.h"
  #include "dir.h"
@@@ -431,7 -431,7 +431,7 @@@ static int expire_reflog(const char *re
                         write_str_in_full(lock->lock_fd, "\n") != 1 ||
                         close_ref(lock) < 0)) {
                        status |= error("Couldn't write %s",
 -                              lock->lk->filename);
 +                                      lock->lk->filename.buf);
                        unlink(newlog_path);
                } else if (rename(newlog_path, log_file)) {
                        status |= error("cannot rename %s to %s",
@@@ -649,7 -649,7 +649,7 @@@ static int cmd_reflog_expire(int argc, 
                init_revisions(&cb.revs, prefix);
                if (cb.verbose)
                        printf("Marking reachable objects...");
-               mark_reachable_objects(&cb.revs, 0, NULL);
+               mark_reachable_objects(&cb.revs, 0, 0, NULL);
                if (cb.verbose)
                        putchar('\n');
        }
diff --combined cache.h
index 0501f7dca855b85f408bac32d4f457fdf799cae4,51ee856acccb90b0652072fcf721b7b99b069f1c..99ed096aed03b865dd6e263474e6e6a265681b91
+++ b/cache.h
@@@ -570,11 -570,29 +570,11 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  
 -struct lock_file {
 -      struct lock_file *next;
 -      int fd;
 -      pid_t owner;
 -      char on_list;
 -      char filename[PATH_MAX];
 -};
 -#define LOCK_DIE_ON_ERROR 1
 -#define LOCK_NODEREF 2
 -extern int unable_to_lock_error(const char *path, int err);
 -extern void unable_to_lock_message(const char *path, int err,
 -                                 struct strbuf *buf);
 -extern NORETURN void unable_to_lock_index_die(const char *path, int err);
 -extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 -extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 -extern int commit_lock_file(struct lock_file *);
 -extern int reopen_lock_file(struct lock_file *);
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
 -extern int close_lock_file(struct lock_file *);
 -extern void rollback_lock_file(struct lock_file *);
 +
  extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
  
  /* Environment bits from configuration mechanism */
@@@ -950,8 -968,8 +950,8 @@@ extern int for_each_abbrev(const char *
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern int read_ref_full(const char *refname, unsigned char *sha1,
 -                       int reading, int *flags);
 +extern int read_ref_full(const char *refname, int resolve_flags,
 +                       unsigned char *sha1, int *flags);
  extern int read_ref(const char *refname, unsigned char *sha1);
  
  /*
   * or the input ref.
   *
   * If the reference cannot be resolved to an object, the behavior
 - * depends on the "reading" argument:
 + * depends on the RESOLVE_REF_READING flag:
   *
 - * - If reading is set, return NULL.
 + * - If RESOLVE_REF_READING is set, return NULL.
   *
 - * - If reading is not set, clear sha1 and return the name of the last
 - *   reference name in the chain, which will either be a non-symbolic
 + * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
 + *   the last reference name in the chain, which will either be a non-symbolic
   *   reference or an undefined reference.  If this is a prelude to
   *   "writing" to the ref, the return value is the name of the ref
   *   that will actually be created or changed.
   *
 - * If flag is non-NULL, set the value that it points to the
 + * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
 + * level of symbolic reference.  The value stored in sha1 for a symbolic
 + * reference will always be null_sha1 in this case, and the return
 + * value is the reference that the symref refers to directly.
 + *
 + * If flags is non-NULL, set the value that it points to the
   * combination of REF_ISPACKED (if the reference was found among the
 - * packed references) and REF_ISSYMREF (if the initial reference was a
 - * symbolic reference).
 + * packed references), REF_ISSYMREF (if the initial reference was a
 + * symbolic reference), REF_BAD_NAME (if the reference name is ill
 + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
 + * (if the ref is malformed or has a bad name). See refs.h for more detail
 + * on each flag.
   *
   * If ref is not a properly-formatted, normalized reference, return
   * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
   * give up and return NULL.
   *
 - * errno is set to something meaningful on error.
 + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
 + * name is invalid according to git-check-ref-format(1).  If the name
 + * is bad then the value stored in sha1 will be null_sha1 and the two
 + * flags REF_ISBROKEN and REF_BAD_NAME will be set.
 + *
 + * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
 + * directory and do not consist of all caps and underscores cannot be
 + * resolved. The function returns NULL for such ref names.
 + * Caps and underscores refers to the special refs, such as HEAD,
 + * FETCH_HEAD and friends, that all live outside of the refs/ directory.
   */
 -extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
 -extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 +#define RESOLVE_REF_READING 0x01
 +#define RESOLVE_REF_NO_RECURSE 0x02
 +#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 +extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
 +extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
  
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
@@@ -1145,7 -1143,7 +1145,7 @@@ 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*);
+ extern int foreach_alt_odb(alt_odb_fn, void*);
  
  struct pack_window {
        struct pack_window *next;
@@@ -1241,6 -1239,50 +1241,50 @@@ extern unsigned long unpack_object_head
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
  extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
  
+ /*
+  * Iterate over the files in the loose-object parts of the object
+  * directory "path", triggering the following callbacks:
+  *
+  *  - loose_object is called for each loose object we find.
+  *
+  *  - loose_cruft is called for any files that do not appear to be
+  *    loose objects. Note that we only look in the loose object
+  *    directories "objects/[0-9a-f]{2}/", so we will not report
+  *    "objects/foobar" as cruft.
+  *
+  *  - loose_subdir is called for each top-level hashed subdirectory
+  *    of the object directory (e.g., "$OBJDIR/f0"). It is called
+  *    after the objects in the directory are processed.
+  *
+  * Any callback that is NULL will be ignored. Callbacks returning non-zero
+  * will end the iteration.
+  */
+ typedef int each_loose_object_fn(const unsigned char *sha1,
+                                const char *path,
+                                void *data);
+ typedef int each_loose_cruft_fn(const char *basename,
+                               const char *path,
+                               void *data);
+ typedef int each_loose_subdir_fn(int nr,
+                                const char *path,
+                                void *data);
+ int for_each_loose_file_in_objdir(const char *path,
+                                 each_loose_object_fn obj_cb,
+                                 each_loose_cruft_fn cruft_cb,
+                                 each_loose_subdir_fn subdir_cb,
+                                 void *data);
+ /*
+  * Iterate over loose and packed objects in both the local
+  * repository and any alternates repositories.
+  */
+ typedef int each_packed_object_fn(const unsigned char *sha1,
+                                 struct packed_git *pack,
+                                 uint32_t pos,
+                                 void *data);
+ extern int for_each_loose_object(each_loose_object_fn, void *);
+ extern int for_each_packed_object(each_packed_object_fn, void *);
  struct object_info {
        /* Request */
        enum object_type *typep;
diff --combined git-compat-util.h
index 210712728daa45de2f0c274e49ae69f43accdab4,44890d5b18f1f37308bdf77c80ee66bb65afc913..fc83339bd765a978b372c2baddb51e54693d05bc
@@@ -326,8 -326,6 +326,8 @@@ static inline char *git_find_last_dir_s
  
  #include "wildmatch.h"
  
 +struct strbuf;
 +
  /* General helper functions */
  extern void vreportf(const char *prefix, const char *err, va_list params);
  extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
@@@ -596,11 -594,6 +596,11 @@@ int inet_pton(int af, const char *src, 
  const char *inet_ntop(int af, const void *src, char *dst, size_t size);
  #endif
  
 +#ifdef NO_PTHREADS
 +#define atexit git_atexit
 +extern int git_atexit(void (*handler)(void));
 +#endif
 +
  extern void release_pack_memory(size_t);
  
  typedef void (*try_to_free_t)(size_t);
@@@ -684,7 -677,7 +684,7 @@@ extern const unsigned char sane_ctype[2
  #define iscntrl(x) (sane_istest(x,GIT_CNTRL))
  #define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
                GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
- #define isxdigit(x) (hexval_table[x] != -1)
+ #define isxdigit(x) (hexval_table[(unsigned char)(x)] != -1)
  #define tolower(x) sane_case((unsigned char)(x), 0x20)
  #define toupper(x) sane_case((unsigned char)(x), 0)
  #define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
@@@ -784,21 -777,11 +784,21 @@@ void git_qsort(void *base, size_t nmemb
  
  /*
   * Preserves errno, prints a message, but gives no warning for ENOENT.
 - * Always returns the return value of unlink(2).
 + * Returns 0 on success, which includes trying to unlink an object that does
 + * not exist.
   */
  int unlink_or_warn(const char *path);
 + /*
 +  * Tries to unlink file.  Returns 0 if unlink succeeded
 +  * or the file already didn't exist.  Returns -1 and
 +  * appends a message to err suitable for
 +  * 'error("%s", err->buf)' on error.
 +  */
 +int unlink_or_msg(const char *file, struct strbuf *err);
  /*
 - * Likewise for rmdir(2).
 + * Preserves errno, prints a message, but gives no warning for ENOENT.
 + * Returns 0 on success, which includes trying to remove a directory that does
 + * not exist.
   */
  int rmdir_or_warn(const char *path);
  /*
diff --combined sha1_file.c
index 83f77f01b6370589fb90c72f111b4e6e381f9189,c63264198ea3b52c78387e2f949b3cac7a7a256f..d7f1838c13f5a88448d0f2034347bfb6482b3edf
@@@ -8,7 -8,6 +8,7 @@@
   */
  #include "cache.h"
  #include "string-list.h"
 +#include "lockfile.h"
  #include "delta.h"
  #include "pack.h"
  #include "blob.h"
@@@ -413,14 -412,18 +413,18 @@@ void add_to_alternates_file(const char 
                link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
  }
  
void foreach_alt_odb(alt_odb_fn fn, void *cb)
int foreach_alt_odb(alt_odb_fn fn, void *cb)
  {
        struct alternate_object_database *ent;
+       int r = 0;
  
        prepare_alt_odb();
-       for (ent = alt_odb_list; ent; ent = ent->next)
-               if (fn(ent, cb))
-                       return;
+       for (ent = alt_odb_list; ent; ent = ent->next) {
+               r = fn(ent, cb);
+               if (r)
+                       break;
+       }
+       return r;
  }
  
  void prepare_alt_odb(void)
        read_info_alternates(get_object_directory(), 0);
  }
  
- static int has_loose_object_local(const unsigned char *sha1)
+ static int freshen_file(const char *fn)
  {
-       return !access(sha1_file_name(sha1), F_OK);
+       struct utimbuf t;
+       t.actime = t.modtime = time(NULL);
+       return !utime(fn, &t);
  }
  
- int has_loose_object_nonlocal(const unsigned char *sha1)
+ static int check_and_freshen_file(const char *fn, int freshen)
+ {
+       if (access(fn, F_OK))
+               return 0;
+       if (freshen && freshen_file(fn))
+               return 0;
+       return 1;
+ }
+ static int check_and_freshen_local(const unsigned char *sha1, int freshen)
+ {
+       return check_and_freshen_file(sha1_file_name(sha1), freshen);
+ }
+ static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
  {
        struct alternate_object_database *alt;
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
                fill_sha1_path(alt->name, sha1);
-               if (!access(alt->base, F_OK))
+               if (check_and_freshen_file(alt->base, freshen))
                        return 1;
        }
        return 0;
  }
  
+ static int check_and_freshen(const unsigned char *sha1, int freshen)
+ {
+       return check_and_freshen_local(sha1, freshen) ||
+              check_and_freshen_nonlocal(sha1, freshen);
+ }
+ int has_loose_object_nonlocal(const unsigned char *sha1)
+ {
+       return check_and_freshen_nonlocal(sha1, 0);
+ }
  static int has_loose_object(const unsigned char *sha1)
  {
-       return has_loose_object_local(sha1) ||
-              has_loose_object_nonlocal(sha1);
+       return check_and_freshen(sha1, 0);
  }
  
  static unsigned int pack_used_ctr;
@@@ -2962,6 -2991,17 +2992,17 @@@ static int write_loose_object(const uns
        return move_temp_to_file(tmp_file, filename);
  }
  
+ static int freshen_loose_object(const unsigned char *sha1)
+ {
+       return check_and_freshen(sha1, 1);
+ }
+ static int freshen_packed_object(const unsigned char *sha1)
+ {
+       struct pack_entry e;
+       return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
+ }
  int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
  {
        unsigned char sha1[20];
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        if (returnsha1)
                hashcpy(returnsha1, sha1);
-       if (has_sha1_file(sha1))
+       if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
                return 0;
        return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
  }
@@@ -3261,3 -3301,149 +3302,149 @@@ void assert_sha1_type(const unsigned ch
                die("%s is not a valid '%s' object", sha1_to_hex(sha1),
                    typename(expect));
  }
+ static int for_each_file_in_obj_subdir(int subdir_nr,
+                                      struct strbuf *path,
+                                      each_loose_object_fn obj_cb,
+                                      each_loose_cruft_fn cruft_cb,
+                                      each_loose_subdir_fn subdir_cb,
+                                      void *data)
+ {
+       size_t baselen = path->len;
+       DIR *dir = opendir(path->buf);
+       struct dirent *de;
+       int r = 0;
+       if (!dir) {
+               if (errno == ENOENT)
+                       return 0;
+               return error("unable to open %s: %s", path->buf, strerror(errno));
+       }
+       while ((de = readdir(dir))) {
+               if (is_dot_or_dotdot(de->d_name))
+                       continue;
+               strbuf_setlen(path, baselen);
+               strbuf_addf(path, "/%s", de->d_name);
+               if (strlen(de->d_name) == 38)  {
+                       char hex[41];
+                       unsigned char sha1[20];
+                       snprintf(hex, sizeof(hex), "%02x%s",
+                                subdir_nr, de->d_name);
+                       if (!get_sha1_hex(hex, sha1)) {
+                               if (obj_cb) {
+                                       r = obj_cb(sha1, path->buf, data);
+                                       if (r)
+                                               break;
+                               }
+                               continue;
+                       }
+               }
+               if (cruft_cb) {
+                       r = cruft_cb(de->d_name, path->buf, data);
+                       if (r)
+                               break;
+               }
+       }
+       strbuf_setlen(path, baselen);
+       if (!r && subdir_cb)
+               r = subdir_cb(subdir_nr, path->buf, data);
+       closedir(dir);
+       return r;
+ }
+ int for_each_loose_file_in_objdir(const char *path,
+                           each_loose_object_fn obj_cb,
+                           each_loose_cruft_fn cruft_cb,
+                           each_loose_subdir_fn subdir_cb,
+                           void *data)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       size_t baselen;
+       int r = 0;
+       int i;
+       strbuf_addstr(&buf, path);
+       strbuf_addch(&buf, '/');
+       baselen = buf.len;
+       for (i = 0; i < 256; i++) {
+               strbuf_addf(&buf, "%02x", i);
+               r = for_each_file_in_obj_subdir(i, &buf, obj_cb, cruft_cb,
+                                               subdir_cb, data);
+               strbuf_setlen(&buf, baselen);
+               if (r)
+                       break;
+       }
+       strbuf_release(&buf);
+       return r;
+ }
+ struct loose_alt_odb_data {
+       each_loose_object_fn *cb;
+       void *data;
+ };
+ static int loose_from_alt_odb(struct alternate_object_database *alt,
+                             void *vdata)
+ {
+       struct loose_alt_odb_data *data = vdata;
+       return for_each_loose_file_in_objdir(alt->base,
+                                            data->cb, NULL, NULL,
+                                            data->data);
+ }
+ int for_each_loose_object(each_loose_object_fn cb, void *data)
+ {
+       struct loose_alt_odb_data alt;
+       int r;
+       r = for_each_loose_file_in_objdir(get_object_directory(),
+                                         cb, NULL, NULL, data);
+       if (r)
+               return r;
+       alt.cb = cb;
+       alt.data = data;
+       return foreach_alt_odb(loose_from_alt_odb, &alt);
+ }
+ static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
+ {
+       uint32_t i;
+       int r = 0;
+       for (i = 0; i < p->num_objects; i++) {
+               const unsigned char *sha1 = nth_packed_object_sha1(p, i);
+               if (!sha1)
+                       return error("unable to get sha1 of object %u in %s",
+                                    i, p->pack_name);
+               r = cb(sha1, p, i, data);
+               if (r)
+                       break;
+       }
+       return r;
+ }
+ int for_each_packed_object(each_packed_object_fn cb, void *data)
+ {
+       struct packed_git *p;
+       int r = 0;
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next) {
+               r = for_each_object_in_pack(p, cb, data);
+               if (r)
+                       break;
+       }
+       return r;
+ }