X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=refs.c;h=a9b8c72c6b6ea017f36639f01c997275299b16ad;hb=fd547a972ad1ad714e1dac0a9ffc7637a64dd9b2;hp=4d6fad8879561ec86a5f37c06f129a19ae5c2f7d;hpb=240897e908e48e8f8c8d5555522afe255b3b734b;p=git.git diff --git a/refs.c b/refs.c index 4d6fad887..a9b8c72c6 100644 --- a/refs.c +++ b/refs.c @@ -309,49 +309,6 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * return ref; } -int create_symref(const char *ref_target, const char *refs_heads_master) -{ - const char *lockpath; - char ref[1000]; - int fd, len, written; - const char *git_HEAD = git_path("%s", ref_target); - -#ifndef NO_SYMLINK_HEAD - if (prefer_symlink_refs) { - unlink(git_HEAD); - if (!symlink(refs_heads_master, git_HEAD)) - return 0; - fprintf(stderr, "no symlink - falling back to symbolic ref\n"); - } -#endif - - len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); - if (sizeof(ref) <= len) { - error("refname too long: %s", refs_heads_master); - return -1; - } - lockpath = mkpath("%s.lock", git_HEAD); - fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); - written = write_in_full(fd, ref, len); - close(fd); - if (written != len) { - unlink(lockpath); - error("Unable to write to %s", lockpath); - return -2; - } - if (rename(lockpath, git_HEAD) < 0) { - unlink(lockpath); - error("Unable to create %s", git_HEAD); - return -3; - } - if (adjust_shared_perm(git_HEAD)) { - unlink(lockpath); - error("Unable to fix permissions on %s", lockpath); - return -4; - } - return 0; -} - int read_ref(const char *ref, unsigned char *sha1) { if (resolve_ref(ref, sha1, 1, NULL)) @@ -676,7 +633,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock->lk = xcalloc(1, sizeof(struct lock_file)); lock->ref_name = xstrdup(ref); - lock->log_file = xstrdup(git_path("logs/%s", ref)); + lock->orig_ref_name = xstrdup(orig_ref); ref_file = git_path("%s", ref); lock->force_write = lstat(ref_file, &st) && errno == ENOENT; @@ -706,6 +663,8 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1) { + if (check_ref_format(ref) == -1) + return NULL; return lock_ref_sha1_basic(ref, old_sha1, NULL); } @@ -772,10 +731,10 @@ int delete_ref(const char *refname, unsigned char *sha1) */ ret |= repack_without_ref(refname); - err = unlink(lock->log_file); + err = unlink(git_path("logs/%s", lock->ref_name)); if (err && errno != ENOENT) fprintf(stderr, "warning: unlink(%s) failed: %s", - lock->log_file, strerror(errno)); + git_path("logs/%s", lock->ref_name), strerror(errno)); invalidate_cached_refs(); unlock_ref(lock); return ret; @@ -837,7 +796,12 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) retry: if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) { - if (errno==EISDIR) { + if (errno==EISDIR || errno==ENOTDIR) { + /* + * rename(a, b) when b is an existing + * directory ought to result in ISDIR, but + * Solaris 5.8 gives ENOTDIR. Sheesh. + */ if (remove_empty_directories(git_path("logs/%s", newref))) { error("Directory not empty: logs/%s", newref); goto rollback; @@ -911,68 +875,81 @@ void unlock_ref(struct ref_lock *lock) rollback_lock_file(lock->lk); } free(lock->ref_name); - free(lock->log_file); + free(lock->orig_ref_name); free(lock); } -static int log_ref_write(struct ref_lock *lock, - const unsigned char *sha1, const char *msg) +static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, + const unsigned char *new_sha1, const char *msg) { int logfd, written, oflags = O_APPEND | O_WRONLY; unsigned maxlen, len; - char *logrec; + int msglen; + char *log_file, *logrec; const char *committer; + if (log_all_ref_updates < 0) + log_all_ref_updates = !is_bare_repository(); + + log_file = git_path("logs/%s", ref_name); + if (log_all_ref_updates && - (!strncmp(lock->ref_name, "refs/heads/", 11) || - !strncmp(lock->ref_name, "refs/remotes/", 13))) { - if (safe_create_leading_directories(lock->log_file) < 0) + (!strncmp(ref_name, "refs/heads/", 11) || + !strncmp(ref_name, "refs/remotes/", 13) || + !strcmp(ref_name, "HEAD"))) { + if (safe_create_leading_directories(log_file) < 0) return error("unable to create directory for %s", - lock->log_file); + log_file); oflags |= O_CREAT; } - logfd = open(lock->log_file, oflags, 0666); + logfd = open(log_file, oflags, 0666); if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; if ((oflags & O_CREAT) && errno == EISDIR) { - if (remove_empty_directories(lock->log_file)) { + if (remove_empty_directories(log_file)) { return error("There are still logs under '%s'", - lock->log_file); + log_file); } - logfd = open(lock->log_file, oflags, 0666); + logfd = open(log_file, oflags, 0666); } if (logfd < 0) return error("Unable to append to %s: %s", - lock->log_file, strerror(errno)); + log_file, strerror(errno)); } - committer = git_committer_info(1); + msglen = 0; if (msg) { - maxlen = strlen(committer) + 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), - committer, - msg); - } - else { - maxlen = strlen(committer) + 2*40 + 4; - logrec = xmalloc(maxlen); - len = snprintf(logrec, maxlen, "%s %s %s\n", - sha1_to_hex(lock->old_sha1), - sha1_to_hex(sha1), - committer); + /* clean up the message and make sure it is a single line */ + for ( ; *msg; msg++) + if (!isspace(*msg)) + break; + if (*msg) { + const char *ep = strchr(msg, '\n'); + if (ep) + msglen = ep - msg; + else + msglen = strlen(msg); + } } + + committer = git_committer_info(-1); + maxlen = strlen(committer) + msglen + 100; + logrec = xmalloc(maxlen); + len = sprintf(logrec, "%s %s %s\n", + sha1_to_hex(old_sha1), + sha1_to_hex(new_sha1), + committer); + if (msglen) + len += sprintf(logrec + len - 1, "\t%.*s\n", msglen, msg) - 1; written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1; free(logrec); close(logfd); if (written != len) - return error("Unable to append to %s", lock->log_file); + return error("Unable to append to %s", log_file); return 0; } @@ -995,7 +972,9 @@ int write_ref_sha1(struct ref_lock *lock, return -1; } invalidate_cached_refs(); - if (log_ref_write(lock, sha1, logmsg) < 0) { + if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 || + (strcmp(lock->ref_name, lock->orig_ref_name) && + log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) { unlock_ref(lock); return -1; } @@ -1009,7 +988,85 @@ int write_ref_sha1(struct ref_lock *lock, return 0; } -int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1) +int create_symref(const char *ref_target, const char *refs_heads_master, + const char *logmsg) +{ + const char *lockpath; + char ref[1000]; + int fd, len, written; + char *git_HEAD = xstrdup(git_path("%s", ref_target)); + unsigned char old_sha1[20], new_sha1[20]; + + if (logmsg && read_ref(ref_target, old_sha1)) + hashclr(old_sha1); + + if (safe_create_leading_directories(git_HEAD) < 0) + return error("unable to create directory for %s", git_HEAD); + +#ifndef NO_SYMLINK_HEAD + if (prefer_symlink_refs) { + unlink(git_HEAD); + if (!symlink(refs_heads_master, git_HEAD)) + goto done; + fprintf(stderr, "no symlink - falling back to symbolic ref\n"); + } +#endif + + len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); + if (sizeof(ref) <= len) { + error("refname too long: %s", refs_heads_master); + goto error_free_return; + } + lockpath = mkpath("%s.lock", git_HEAD); + fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); + if (fd < 0) { + error("Unable to open %s for writing", lockpath); + goto error_free_return; + } + written = write_in_full(fd, ref, len); + close(fd); + if (written != len) { + error("Unable to write to %s", lockpath); + goto error_unlink_return; + } + if (rename(lockpath, git_HEAD) < 0) { + error("Unable to create %s", git_HEAD); + goto error_unlink_return; + } + if (adjust_shared_perm(git_HEAD)) { + error("Unable to fix permissions on %s", lockpath); + error_unlink_return: + unlink(lockpath); + error_free_return: + free(git_HEAD); + return -1; + } + +#ifndef NO_SYMLINK_HEAD + done: +#endif + if (logmsg && !read_ref(refs_heads_master, new_sha1)) + log_ref_write(ref_target, old_sha1, new_sha1, logmsg); + + free(git_HEAD); + return 0; +} + +static char *ref_msg(const char *line, const char *endp) +{ + const char *ep; + char *msg; + + line += 82; + for (ep = line; ep < endp && *ep != '\n'; ep++) + ; + msg = xmalloc(ep - line + 1); + memcpy(msg, line, ep - line); + msg[ep - line] = 0; + return msg; +} + +int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; char *tz_c; @@ -1017,6 +1074,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * struct stat st; unsigned long date; unsigned char logged_sha1[20]; + void *log_mapped; logfile = git_path("logs/%s", ref); logfd = open(logfile, O_RDONLY, 0); @@ -1025,7 +1083,8 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * fstat(logfd, &st); if (!st.st_size) die("Log %s is empty.", logfile); - logdata = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); + log_mapped = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); + logdata = log_mapped; close(logfd); lastrec = NULL; @@ -1044,13 +1103,21 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); if (date <= at_time || cnt == 0) { + tz = strtoul(tz_c, NULL, 10); + if (msg) + *msg = ref_msg(rec, logend); + if (cutoff_time) + *cutoff_time = date; + if (cutoff_tz) + *cutoff_tz = tz; + if (cutoff_cnt) + *cutoff_cnt = reccnt - 1; 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 (hashcmp(logged_sha1, sha1)) { - tz = strtoul(tz_c, NULL, 10); fprintf(stderr, "warning: Log %s has gap after %s.\n", logfile, show_rfc2822_date(date, tz)); @@ -1064,13 +1131,12 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * if (get_sha1_hex(rec + 41, logged_sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { - 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); + munmap(log_mapped, st.st_size); return 0; } lastrec = rec; @@ -1087,38 +1153,112 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * tz = strtoul(tz_c, NULL, 10); if (get_sha1_hex(logdata, sha1)) die("Log %s is corrupt.", logfile); - munmap((void*)logdata, st.st_size); - 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; + if (msg) + *msg = ref_msg(logdata, logend); + munmap(log_mapped, st.st_size); + + if (cutoff_time) + *cutoff_time = date; + if (cutoff_tz) + *cutoff_tz = tz; + if (cutoff_cnt) + *cutoff_cnt = reccnt; + return 1; } -void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) +int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) { const char *logfile; FILE *logfp; char buf[1024]; + int ret = 0; logfile = git_path("logs/%s", ref); logfp = fopen(logfile, "r"); if (!logfp) - return; + return -1; while (fgets(buf, sizeof(buf), logfp)) { unsigned char osha1[20], nsha1[20]; - int len; + char *email_end, *message; + unsigned long timestamp; + int len, tz; /* old SP new SP name 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] != ' ') + get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' || + !(email_end = strchr(buf + 82, '>')) || + email_end[1] != ' ' || + !(timestamp = strtoul(email_end + 2, &message, 10)) || + !message || message[0] != ' ' || + (message[1] != '+' && message[1] != '-') || + !isdigit(message[2]) || !isdigit(message[3]) || + !isdigit(message[4]) || !isdigit(message[5])) continue; /* corrupt? */ - fn(osha1, nsha1, buf+82, cb_data); + email_end[1] = '\0'; + tz = strtol(message + 1, NULL, 10); + if (message[6] != '\t') + message += 6; + else + message += 7; + ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data); + if (ret) + break; } fclose(logfp); + return ret; } +static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data) +{ + DIR *dir = opendir(git_path("logs/%s", base)); + int retval = 0; + + if (dir) { + struct dirent *de; + int baselen = strlen(base); + char *log = xmalloc(baselen + 257); + + memcpy(log, base, baselen); + if (baselen && base[baselen-1] != '/') + log[baselen++] = '/'; + + while ((de = readdir(dir)) != NULL) { + struct stat st; + int namelen; + + if (de->d_name[0] == '.') + continue; + namelen = strlen(de->d_name); + if (namelen > 255) + continue; + if (has_extension(de->d_name, ".lock")) + continue; + memcpy(log + baselen, de->d_name, namelen+1); + if (stat(git_path("logs/%s", log), &st) < 0) + continue; + if (S_ISDIR(st.st_mode)) { + retval = do_for_each_reflog(log, fn, cb_data); + } else { + unsigned char sha1[20]; + if (!resolve_ref(log, sha1, 0, NULL)) + retval = error("bad ref for %s", log); + else + retval = fn(log, sha1, 0, cb_data); + } + if (retval) + break; + } + free(log); + closedir(dir); + } + else if (*base) + return errno; + return retval; +} + +int for_each_reflog(each_ref_fn fn, void *cb_data) +{ + return do_for_each_reflog("", fn, cb_data); +}