X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=refs.c;h=9e3dfb3c974c893760a6bcb75ecc39026f74a23b;hb=c6ec3b13b81d59272e41d5316689c65dd4cc2e63;hp=60375a786f859a1cb9a4899bbad917740d41e29e;hpb=16d7cc90dd6e68a170362ae6f0fbfbc504fd246b;p=git.git diff --git a/refs.c b/refs.c index 60375a786..9e3dfb3c9 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; @@ -916,71 +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; } @@ -1003,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; } @@ -1017,6 +988,68 @@ int write_ref_sha1(struct ref_lock *lock, return 0; } +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; + } + + done: + 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; @@ -1068,6 +1101,7 @@ 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) @@ -1075,14 +1109,13 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * if (cutoff_tz) *cutoff_tz = tz; if (cutoff_cnt) - *cutoff_cnt = reccnt; + *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)); @@ -1096,7 +1129,6 @@ 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)); @@ -1160,12 +1192,14 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) !message || message[0] != ' ' || (message[1] != '+' && message[1] != '-') || !isdigit(message[2]) || !isdigit(message[3]) || - !isdigit(message[4]) || !isdigit(message[5]) || - message[6] != '\t') + !isdigit(message[4]) || !isdigit(message[5])) continue; /* corrupt? */ email_end[1] = '\0'; tz = strtol(message + 1, NULL, 10); - message += 7; + if (message[6] != '\t') + message += 6; + else + message += 7; ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data); if (ret) break; @@ -1174,3 +1208,55 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) 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 + return errno; + return retval; +} + +int for_each_reflog(each_ref_fn fn, void *cb_data) +{ + return do_for_each_reflog("", fn, cb_data); +}