X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=refs.c;h=eeb1196ec40a64e9ab1d865fbfc42b207fed98ed;hb=b3ca4e4ebba6915ea534c4b82cfc5d6f10b4c283;hp=91c8c44a159b3e4c1b7bb35c5662502642758ab2;hpb=4bd18c43d9a8a8fc1aa598926100f6999953ba48;p=git.git diff --git a/refs.c b/refs.c index 91c8c44a..eeb1196e 100644 --- a/refs.c +++ b/refs.c @@ -142,6 +142,8 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u namelen = strlen(de->d_name); if (namelen > 255) continue; + if (namelen>5 && !strcmp(de->d_name+namelen-5,".lock")) + continue; memcpy(path + baselen, de->d_name, namelen+1); if (stat(git_path("%s", path), &st) < 0) continue; @@ -288,6 +290,7 @@ static struct ref_lock* lock_ref_sha1_basic(const char *path, const unsigned char *old_sha1, int mustexist) { struct ref_lock *lock; + struct stat st; lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; @@ -295,13 +298,14 @@ static struct ref_lock* lock_ref_sha1_basic(const char *path, plen = strlen(path) - plen; path = resolve_ref(path, lock->old_sha1, mustexist); if (!path) { - error("Can't read ref %s", path); unlock_ref(lock); return NULL; } lock->ref_file = strdup(path); lock->lock_file = strdup(mkpath("%s.lock", lock->ref_file)); + lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen)); + lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(lock->lock_file)) die("unable to create directory for %s", lock->lock_file); @@ -323,7 +327,7 @@ struct ref_lock* lock_ref_sha1(const char *ref, if (check_ref_format(ref)) return NULL; return lock_ref_sha1_basic(git_path("refs/%s", ref), - strlen(ref), old_sha1, mustexist); + 5 + strlen(ref), old_sha1, mustexist); } struct ref_lock* lock_any_ref_for_update(const char *ref, @@ -343,9 +347,60 @@ void unlock_ref (struct ref_lock *lock) free(lock->ref_file); if (lock->lock_file) free(lock->lock_file); + if (lock->log_file) + free(lock->log_file); free(lock); } +static int log_ref_write(struct ref_lock *lock, + const unsigned char *sha1, const char *msg) +{ + int logfd, written, oflags = O_APPEND | O_WRONLY; + unsigned maxlen, len; + char *logrec; + const char *comitter; + + if (log_all_ref_updates) { + if (safe_create_leading_directories(lock->log_file) < 0) + return error("unable to create directory for %s", + lock->log_file); + oflags |= O_CREAT; + } + + logfd = open(lock->log_file, oflags, 0666); + if (logfd < 0) { + if (!log_all_ref_updates && errno == ENOENT) + return 0; + return error("Unable to append to %s: %s", + lock->log_file, strerror(errno)); + } + + setup_ident(); + comitter = git_committer_info(1); + if (msg) { + maxlen = strlen(comitter) + strlen(msg) + 2*40 + 5; + logrec = xmalloc(maxlen); + len = snprintf(logrec, maxlen, "%s %s %s\t%s\n", + sha1_to_hex(lock->old_sha1), + sha1_to_hex(sha1), + comitter, + msg); + } else { + maxlen = strlen(comitter) + 2*40 + 4; + logrec = xmalloc(maxlen); + len = snprintf(logrec, maxlen, "%s %s %s\n", + sha1_to_hex(lock->old_sha1), + sha1_to_hex(sha1), + comitter); + } + written = len <= maxlen ? write(logfd, logrec, len) : -1; + free(logrec); + close(logfd); + if (written != len) + return error("Unable to append to %s", lock->log_file); + return 0; +} + int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *logmsg) { @@ -353,7 +408,7 @@ int write_ref_sha1(struct ref_lock *lock, if (!lock) return -1; - if (!memcmp(lock->old_sha1, sha1, 20)) { + if (!lock->force_write && !memcmp(lock->old_sha1, sha1, 20)) { unlock_ref(lock); return 0; } @@ -364,6 +419,10 @@ int write_ref_sha1(struct ref_lock *lock, unlock_ref(lock); return -1; } + if (log_ref_write(lock, sha1, logmsg) < 0) { + unlock_ref(lock); + return -1; + } if (rename(lock->lock_file, lock->ref_file) < 0) { error("Couldn't set %s", lock->ref_file); unlock_ref(lock); @@ -373,3 +432,82 @@ int write_ref_sha1(struct ref_lock *lock, unlock_ref(lock); return 0; } + +int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) +{ + const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; + char *tz_c; + int logfd, tz; + struct stat st; + unsigned long date; + unsigned char logged_sha1[20]; + + logfile = git_path("logs/%s", ref); + logfd = open(logfile, O_RDONLY, 0); + if (logfd < 0) + die("Unable to read log %s: %s", logfile, strerror(errno)); + fstat(logfd, &st); + if (!st.st_size) + die("Log %s is empty.", logfile); + logdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); + close(logfd); + + lastrec = NULL; + rec = logend = logdata + st.st_size; + while (logdata < rec) { + if (logdata < rec && *(rec-1) == '\n') + rec--; + lastgt = NULL; + while (logdata < rec && *(rec-1) != '\n') { + rec--; + if (*rec == '>') + lastgt = rec; + } + if (!lastgt) + die("Log %s is corrupt.", logfile); + date = strtoul(lastgt + 1, &tz_c, 10); + if (date <= at_time) { + if (lastrec) { + if (get_sha1_hex(lastrec, logged_sha1)) + die("Log %s is corrupt.", logfile); + if (get_sha1_hex(rec + 41, sha1)) + die("Log %s is corrupt.", logfile); + if (memcmp(logged_sha1, sha1, 20)) { + tz = strtoul(tz_c, NULL, 10); + fprintf(stderr, + "warning: Log %s has gap after %s.\n", + logfile, show_rfc2822_date(date, tz)); + } + } else if (date == at_time) { + if (get_sha1_hex(rec + 41, sha1)) + die("Log %s is corrupt.", logfile); + } else { + if (get_sha1_hex(rec + 41, logged_sha1)) + die("Log %s is corrupt.", logfile); + if (memcmp(logged_sha1, sha1, 20)) { + tz = strtoul(tz_c, NULL, 10); + fprintf(stderr, + "warning: Log %s unexpectedly ended on %s.\n", + logfile, show_rfc2822_date(date, tz)); + } + } + munmap((void*)logdata, st.st_size); + return 0; + } + lastrec = rec; + } + + rec = logdata; + while (rec < logend && *rec != '>' && *rec != '\n') + rec++; + if (rec == logend || *rec == '\n') + die("Log %s is corrupt.", logfile); + date = strtoul(rec + 1, &tz_c, 10); + tz = strtoul(tz_c, NULL, 10); + 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)); + return 0; +}