Merge branch 'maint'
authorJunio C Hamano <junkio@cox.net>
Fri, 5 Jan 2007 06:28:21 +0000 (22:28 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 5 Jan 2007 06:28:21 +0000 (22:28 -0800)
* maint:
pack-check.c::verify_packfile(): don't run SHA-1 update on huge data
Fix infinite loop when deleting multiple packed refs.

1  2 
cache.h
lockfile.c
refs.c
diff --combined cache.h
index 384f829b96a5056d843548c39c4e55976a9a7772,a0e9727a0bb750657325b323b00ad47f5f6fabdb..31b0819e83d7f76ed52db7771cf274e74b2156dc
+++ b/cache.h
@@@ -122,12 -122,7 +122,12 @@@ extern int cache_errno
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 +#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
 +#define CONFIG_ENVIRONMENT "GIT_CONFIG"
 +#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 +#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
  
 +extern int is_bare_git_dir(const char *dir);
  extern const char *get_git_dir(void);
  extern char *get_object_directory(void);
  extern char *get_refs_directory(void);
@@@ -179,6 -174,7 +179,7 @@@ extern int refresh_cache(unsigned int f
  
  struct lock_file {
        struct lock_file *next;
+       char on_list;
        char filename[PATH_MAX];
  };
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
@@@ -313,7 -309,6 +314,7 @@@ void datestamp(char *buf, int bufsize)
  unsigned long approxidate(const char *);
  
  extern int setup_ident(void);
 +extern void ignore_missing_committer_name();
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
  
@@@ -409,18 -404,16 +410,18 @@@ extern int git_config_int(const char *
  extern int git_config_bool(const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
 +extern int git_config_rename_section(const char *, const char *);
  extern int check_repository_format_version(const char *var, const char *value);
  
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
  extern char git_default_name[MAX_GITNAME];
  
 -#define MAX_ENCODING_LENGTH 64
 -extern char git_commit_encoding[MAX_ENCODING_LENGTH];
 +extern char *git_commit_encoding;
 +extern char *git_log_output_encoding;
  
  extern int copy_fd(int ifd, int ofd);
 +extern int write_in_full(int fd, const void *buf, size_t count, const char *);
  extern void write_or_die(int fd, const void *buf, size_t count);
  extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
  
diff --combined lockfile.c
index 261baff049cd8b2e4d1b1a269992851eb00b2aa8,143d7d85b653019e8082b332e17c0a72831bd319..731bbf3c9c312687714d19391e6e02ffde8eaebd
@@@ -1,6 -1,7 +1,6 @@@
  /*
   * Copyright (c) 2005, Junio C Hamano
   */
 -#include <signal.h>
  #include "cache.h"
  
  static struct lock_file *lock_file_list;
@@@ -27,9 -28,12 +27,12 @@@ static int lock_file(struct lock_file *
        sprintf(lk->filename, "%s.lock", path);
        fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
        if (0 <= fd) {
-               if (!lk->next) {
+               if (!lk->on_list) {
                        lk->next = lock_file_list;
                        lock_file_list = lk;
+                       lk->on_list = 1;
+               }
+               if (lock_file_list) {
                        signal(SIGINT, remove_lock_file_on_signal);
                        atexit(remove_lock_file);
                }
@@@ -37,6 -41,8 +40,8 @@@
                        return error("cannot fix permission bits on %s",
                                     lk->filename);
        }
+       else
+               lk->filename[0] = 0;
        return fd;
  }
  
diff --combined refs.c
index e88ed8b2d3f42f48e6859de16f3d6a7f3641a84c,1549f2ae06c28570ddc9ff9aa6cb361567067e7b..f76b4fe20dea81f99b330103ce9d05cc6364c96c
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -1,16 -1,12 +1,16 @@@
 -#include "refs.h"
  #include "cache.h"
 +#include "refs.h"
 +#include "object.h"
 +#include "tag.h"
  
 -#include <errno.h>
 +/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
 +#define REF_KNOWS_PEELED 04
  
  struct ref_list {
        struct ref_list *next;
        unsigned char flag; /* ISSYMREF? ISPACKED? */
        unsigned char sha1[20];
 +      unsigned char peeled[20];
        char name[FLEX_ARRAY];
  };
  
@@@ -38,13 -34,11 +38,13 @@@ static const char *parse_ref_line(char 
        if (line[len] != '\n')
                return NULL;
        line[len] = 0;
 +
        return line;
  }
  
  static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
 -                              int flag, struct ref_list *list)
 +                              int flag, struct ref_list *list,
 +                              struct ref_list **new_entry)
  {
        int len;
        struct ref_list **p = &list, *entry;
                        break;
  
                /* Same as existing entry? */
 -              if (!cmp)
 +              if (!cmp) {
 +                      if (new_entry)
 +                              *new_entry = entry;
                        return list;
 +              }
                p = &entry->next;
        }
  
        len = strlen(name) + 1;
        entry = xmalloc(sizeof(struct ref_list) + len);
        hashcpy(entry->sha1, sha1);
 +      hashclr(entry->peeled);
        memcpy(entry->name, name, len);
        entry->flag = flag;
        entry->next = *p;
        *p = entry;
 +      if (new_entry)
 +              *new_entry = entry;
        return list;
  }
  
@@@ -110,50 -98,25 +110,50 @@@ static void invalidate_cached_refs(void
        ca->did_loose = ca->did_packed = 0;
  }
  
 +static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
 +{
 +      struct ref_list *list = NULL;
 +      struct ref_list *last = NULL;
 +      char refline[PATH_MAX];
 +      int flag = REF_ISPACKED;
 +
 +      while (fgets(refline, sizeof(refline), f)) {
 +              unsigned char sha1[20];
 +              const char *name;
 +              static const char header[] = "# pack-refs with:";
 +
 +              if (!strncmp(refline, header, sizeof(header)-1)) {
 +                      const char *traits = refline + sizeof(header) - 1;
 +                      if (strstr(traits, " peeled "))
 +                              flag |= REF_KNOWS_PEELED;
 +                      /* perhaps other traits later as well */
 +                      continue;
 +              }
 +
 +              name = parse_ref_line(refline, sha1);
 +              if (name) {
 +                      list = add_ref(name, sha1, flag, list, &last);
 +                      continue;
 +              }
 +              if (last &&
 +                  refline[0] == '^' &&
 +                  strlen(refline) == 42 &&
 +                  refline[41] == '\n' &&
 +                  !get_sha1_hex(refline + 1, sha1))
 +                      hashcpy(last->peeled, sha1);
 +      }
 +      cached_refs->packed = list;
 +}
 +
  static struct ref_list *get_packed_refs(void)
  {
        if (!cached_refs.did_packed) {
 -              struct ref_list *refs = NULL;
                FILE *f = fopen(git_path("packed-refs"), "r");
 +              cached_refs.packed = NULL;
                if (f) {
 -                      struct ref_list *list = NULL;
 -                      char refline[PATH_MAX];
 -                      while (fgets(refline, sizeof(refline), f)) {
 -                              unsigned char sha1[20];
 -                              const char *name = parse_ref_line(refline, sha1);
 -                              if (!name)
 -                                      continue;
 -                              list = add_ref(name, sha1, REF_ISPACKED, list);
 -                      }
 +                      read_packed_refs(f, &cached_refs);
                        fclose(f);
 -                      refs = list;
                }
 -              cached_refs.packed = refs;
                cached_refs.did_packed = 1;
        }
        return cached_refs.packed;
@@@ -196,7 -159,7 +196,7 @@@ static struct ref_list *get_ref_dir(con
                                error("%s points nowhere!", ref);
                                continue;
                        }
 -                      list = add_ref(ref, sha1, flag, list);
 +                      list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
                closedir(dir);
@@@ -373,43 -336,6 +373,43 @@@ static int do_one_ref(const char *base
        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
  }
  
 +int peel_ref(const char *ref, unsigned char *sha1)
 +{
 +      int flag;
 +      unsigned char base[20];
 +      struct object *o;
 +
 +      if (!resolve_ref(ref, base, 1, &flag))
 +              return -1;
 +
 +      if ((flag & REF_ISPACKED)) {
 +              struct ref_list *list = get_packed_refs();
 +
 +              while (list) {
 +                      if (!strcmp(list->name, ref)) {
 +                              if (list->flag & REF_KNOWS_PEELED) {
 +                                      hashcpy(sha1, list->peeled);
 +                                      return 0;
 +                              }
 +                              /* older pack-refs did not leave peeled ones */
 +                              break;
 +                      }
 +                      list = list->next;
 +              }
 +      }
 +
 +      /* fallback - callers should not call this for unpacked refs */
 +      o = parse_object(base);
 +      if (o->type == OBJ_TAG) {
 +              o = deref_tag(o, ref, 0);
 +              if (o) {
 +                      hashcpy(sha1, o->sha1);
 +                      return 0;
 +              }
 +      }
 +      return -1;
 +}
 +
  static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
                           void *cb_data)
  {
@@@ -532,7 -458,7 +532,7 @@@ int check_ref_format(const char *ref
                level++;
                if (!ch) {
                        if (level < 2)
 -                              return -1; /* at least of form "heads/blah" */
 +                              return -2; /* at least of form "heads/blah" */
                        return 0;
                }
        }
@@@ -608,29 -534,6 +608,29 @@@ static int remove_empty_directories(cha
        return remove_empty_dir_recursive(path, len);
  }
  
 +static int is_refname_available(const char *ref, const char *oldref,
 +                              struct ref_list *list, int quiet)
 +{
 +      int namlen = strlen(ref); /* e.g. 'foo/bar' */
 +      while (list) {
 +              /* list->name could be 'foo' or 'foo/bar/baz' */
 +              if (!oldref || strcmp(oldref, list->name)) {
 +                      int len = strlen(list->name);
 +                      int cmplen = (namlen < len) ? namlen : len;
 +                      const char *lead = (namlen < len) ? list->name : ref;
 +                      if (!strncmp(ref, list->name, cmplen) &&
 +                          lead[cmplen] == '/') {
 +                              if (!quiet)
 +                                      error("'%s' exists; cannot create '%s'",
 +                                            list->name, ref);
 +                              return 0;
 +                      }
 +              }
 +              list = list->next;
 +      }
 +      return 1;
 +}
 +
  static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
  {
        char *ref_file;
                        orig_ref, strerror(errno));
                goto error_return;
        }
 -      if (is_null_sha1(lock->old_sha1)) {
 -              /* The ref did not exist and we are creating it.
 -               * Make sure there is no existing ref that is packed
 -               * whose name begins with our refname, nor a ref whose
 -               * name is a proper prefix of our refname.
 -               */
 -              int namlen = strlen(ref); /* e.g. 'foo/bar' */
 -              struct ref_list *list = get_packed_refs();
 -              while (list) {
 -                      /* list->name could be 'foo' or 'foo/bar/baz' */
 -                      int len = strlen(list->name);
 -                      int cmplen = (namlen < len) ? namlen : len;
 -                      const char *lead = (namlen < len) ? list->name : ref;
 -
 -                      if (!strncmp(ref, list->name, cmplen) &&
 -                          lead[cmplen] == '/') {
 -                              error("'%s' exists; cannot create '%s'",
 -                                    list->name, ref);
 -                              goto error_return;
 -                      }
 -                      list = list->next;
 -              }
 -      }
 +      /* When the ref did not exist and we are creating it,
 +       * make sure there is no existing ref that is packed
 +       * whose name begins with our refname, nor a ref whose
 +       * name is a proper prefix of our refname.
 +       */
 +      if (is_null_sha1(lock->old_sha1) &&
 +            !is_refname_available(ref, NULL, get_packed_refs(), 0))
 +              goto error_return;
  
        lock->lk = xcalloc(1, sizeof(struct lock_file));
  
@@@ -726,7 -644,6 +726,6 @@@ static int repack_without_ref(const cha
        }
        if (!found)
                return 0;
-       memset(&packlock, 0, sizeof(packlock));
        fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
        if (fd < 0)
                return error("cannot delete '%s' from packed refs", refname);
@@@ -782,127 -699,6 +781,127 @@@ int delete_ref(const char *refname, uns
        return ret;
  }
  
 +int rename_ref(const char *oldref, const char *newref, const char *logmsg)
 +{
 +      static const char renamed_ref[] = "RENAMED-REF";
 +      unsigned char sha1[20], orig_sha1[20];
 +      int flag = 0, logmoved = 0;
 +      struct ref_lock *lock;
 +      struct stat loginfo;
 +      int log = !lstat(git_path("logs/%s", oldref), &loginfo);
 +
 +      if (S_ISLNK(loginfo.st_mode))
 +              return error("reflog for %s is a symlink", oldref);
 +
 +      if (!resolve_ref(oldref, orig_sha1, 1, &flag))
 +              return error("refname %s not found", oldref);
 +
 +      if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
 +              return 1;
 +
 +      if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
 +              return 1;
 +
 +      lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL);
 +      if (!lock)
 +              return error("unable to lock %s", renamed_ref);
 +      lock->force_write = 1;
 +      if (write_ref_sha1(lock, orig_sha1, logmsg))
 +              return error("unable to save current sha1 in %s", renamed_ref);
 +
 +      if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
 +              return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
 +                      oldref, strerror(errno));
 +
 +      if (delete_ref(oldref, orig_sha1)) {
 +              error("unable to delete old %s", oldref);
 +              goto rollback;
 +      }
 +
 +      if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) {
 +              if (errno==EISDIR) {
 +                      if (remove_empty_directories(git_path("%s", newref))) {
 +                              error("Directory not empty: %s", newref);
 +                              goto rollback;
 +                      }
 +              } else {
 +                      error("unable to delete existing %s", newref);
 +                      goto rollback;
 +              }
 +      }
 +
 +      if (log && safe_create_leading_directories(git_path("logs/%s", newref))) {
 +              error("unable to create directory for %s", newref);
 +              goto rollback;
 +      }
 +
 + retry:
 +      if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
 +              if (errno==EISDIR) {
 +                      if (remove_empty_directories(git_path("logs/%s", newref))) {
 +                              error("Directory not empty: logs/%s", newref);
 +                              goto rollback;
 +                      }
 +                      goto retry;
 +              } else {
 +                      error("unable to move logfile tmp-renamed-log to logs/%s: %s",
 +                              newref, strerror(errno));
 +                      goto rollback;
 +              }
 +      }
 +      logmoved = log;
 +
 +      lock = lock_ref_sha1_basic(newref, NULL, NULL);
 +      if (!lock) {
 +              error("unable to lock %s for update", newref);
 +              goto rollback;
 +      }
 +
 +      lock->force_write = 1;
 +      hashcpy(lock->old_sha1, orig_sha1);
 +      if (write_ref_sha1(lock, orig_sha1, logmsg)) {
 +              error("unable to write current sha1 into %s", newref);
 +              goto rollback;
 +      }
 +
 +      if (!strncmp(oldref, "refs/heads/", 11) &&
 +                      !strncmp(newref, "refs/heads/", 11)) {
 +              char oldsection[1024], newsection[1024];
 +
 +              snprintf(oldsection, 1024, "branch.%s", oldref + 11);
 +              snprintf(newsection, 1024, "branch.%s", newref + 11);
 +              if (git_config_rename_section(oldsection, newsection) < 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +
 + rollback:
 +      lock = lock_ref_sha1_basic(oldref, NULL, NULL);
 +      if (!lock) {
 +              error("unable to lock %s for rollback", oldref);
 +              goto rollbacklog;
 +      }
 +
 +      lock->force_write = 1;
 +      flag = log_all_ref_updates;
 +      log_all_ref_updates = 0;
 +      if (write_ref_sha1(lock, orig_sha1, NULL))
 +              error("unable to write current sha1 into %s", oldref);
 +      log_all_ref_updates = flag;
 +
 + rollbacklog:
 +      if (logmoved && rename(git_path("logs/%s", newref), git_path("logs/%s", oldref)))
 +              error("unable to restore logfile %s from %s: %s",
 +                      oldref, newref, strerror(errno));
 +      if (!logmoved && log &&
 +          rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
 +              error("unable to restore logfile %s from tmp-renamed-log: %s",
 +                      oldref, strerror(errno));
 +
 +      return 1;
 +}
 +
  void unlock_ref(struct ref_lock *lock)
  {
        if (lock->lock_fd >= 0) {
@@@ -925,8 -721,7 +924,8 @@@ static int log_ref_write(struct ref_loc
        const char *committer;
  
        if (log_all_ref_updates &&
 -          !strncmp(lock->ref_name, "refs/heads/", 11)) {
 +          (!strncmp(lock->ref_name, "refs/heads/", 11) ||
 +           !strncmp(lock->ref_name, "refs/remotes/", 13))) {
                if (safe_create_leading_directories(lock->log_file) < 0)
                        return error("unable to create directory for %s",
                                lock->log_file);
@@@ -1014,7 -809,7 +1013,7 @@@ int read_ref_at(const char *ref, unsign
  {
        const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
        char *tz_c;
 -      int logfd, tz;
 +      int logfd, tz, reccnt = 0;
        struct stat st;
        unsigned long date;
        unsigned char logged_sha1[20];
        lastrec = NULL;
        rec = logend = logdata + st.st_size;
        while (logdata < rec) {
 +              reccnt++;
                if (logdata < rec && *(rec-1) == '\n')
                        rec--;
                lastgt = NULL;
        if (get_sha1_hex(logdata, sha1))
                die("Log %s is corrupt.", logfile);
        munmap((void*)logdata, st.st_size);
 -      fprintf(stderr, "warning: Log %s only goes back to %s.\n",
 -              logfile, show_rfc2822_date(date, tz));
 +      if (at_time)
 +              fprintf(stderr, "warning: Log %s only goes back to %s.\n",
 +                      logfile, show_rfc2822_date(date, tz));
 +      else
 +              fprintf(stderr, "warning: Log %s only has %d entries.\n",
 +                      logfile, reccnt);
        return 0;
  }
 +
 +void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
 +{
 +      const char *logfile;
 +      FILE *logfp;
 +      char buf[1024];
 +
 +      logfile = git_path("logs/%s", ref);
 +      logfp = fopen(logfile, "r");
 +      if (!logfp)
 +              return;
 +      while (fgets(buf, sizeof(buf), logfp)) {
 +              unsigned char osha1[20], nsha1[20];
 +              int len;
 +
 +              /* old SP new SP name <email> SP time TAB msg LF */
 +              len = strlen(buf);
 +              if (len < 83 || buf[len-1] != '\n' ||
 +                  get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
 +                  get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ')
 +                      continue; /* corrupt? */
 +              fn(osha1, nsha1, buf+82, cb_data);
 +      }
 +      fclose(logfp);
 +}
 +