Code

git-blame --incremental
[git.git] / refs.c
diff --git a/refs.c b/refs.c
index 689ac50bae64a1068f2d9b1d720817748fc2289d..12e46b8bbefb35e5d402b3c8ef20dd9e4263e81a 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -331,7 +331,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master)
                return -1;
        }
        lockpath = mkpath("%s.lock", git_HEAD);
-       fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); 
+       fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
+       if (fd < 0) {
+               error("Unable to open %s for writing", lockpath);
+               return -5;
+       }
        written = write_in_full(fd, ref, len);
        close(fd);
        if (written != len) {
@@ -837,7 +841,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;
@@ -920,6 +929,7 @@ static int log_ref_write(struct ref_lock *lock,
 {
        int logfd, written, oflags = O_APPEND | O_WRONLY;
        unsigned maxlen, len;
+       int msglen;
        char *logrec;
        const char *committer;
 
@@ -953,24 +963,30 @@ static int log_ref_write(struct ref_lock *lock,
                                     lock->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(lock->old_sha1),
+                     sha1_to_hex(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);
@@ -1012,7 +1028,21 @@ 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)
+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;
@@ -1020,6 +1050,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);
@@ -1028,7 +1059,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;
@@ -1047,13 +1079,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));
@@ -1067,13 +1107,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;
@@ -1090,14 +1129,17 @@ 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;
 }
 
 int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
@@ -1105,6 +1147,7 @@ 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");
@@ -1114,7 +1157,7 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
                unsigned char osha1[20], nsha1[20];
                char *email_end, *message;
                unsigned long timestamp;
-               int len, ret, tz;
+               int len, tz;
 
                /* old SP new SP name <email> SP time TAB msg LF */
                len = strlen(buf);
@@ -1135,9 +1178,9 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
                message += 7;
                ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
                if (ret)
-                       return ret;
+                       break;
        }
        fclose(logfp);
-       return 0;
+       return ret;
 }