Add the WITH_P4IMPORT knob to the Makefile.
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 4a523086e37fa0bcdd647c826b9221165d630586..f471152bfc6c500a2597068496ffff3f4d8f5961 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -663,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);
 }
 
@@ -826,8 +828,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                goto rollback;
        }
 
-       if (!strncmp(oldref, "refs/heads/", 11) &&
-                       !strncmp(newref, "refs/heads/", 11)) {
+       if (!prefixcmp(oldref, "refs/heads/") &&
+                       !prefixcmp(newref, "refs/heads/")) {
                char oldsection[1024], newsection[1024];
 
                snprintf(oldsection, 1024, "branch.%s", oldref + 11);
@@ -892,8 +894,8 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
        log_file = git_path("logs/%s", ref_name);
 
        if (log_all_ref_updates &&
-           (!strncmp(ref_name, "refs/heads/", 11) ||
-            !strncmp(ref_name, "refs/remotes/", 13) ||
+           (!prefixcmp(ref_name, "refs/heads/") ||
+            !prefixcmp(ref_name, "refs/remotes/") ||
             !strcmp(ref_name, "HEAD"))) {
                if (safe_create_leading_directories(log_file) < 0)
                        return error("unable to create directory for %s",
@@ -919,6 +921,8 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
                                     log_file, strerror(errno));
        }
 
+       adjust_shared_perm(log_file);
+
        msglen = 0;
        if (msg) {
                /* clean up the message and make sure it is a single line */
@@ -976,6 +980,27 @@ int write_ref_sha1(struct ref_lock *lock,
                unlock_ref(lock);
                return -1;
        }
+       if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
+               /*
+                * Special hack: If a branch is updated directly and HEAD
+                * points to it (may happen on the remote side of a push
+                * for example) then logically the HEAD reflog should be
+                * updated too.
+                * A generic solution implies reverse symref information,
+                * but finding all symrefs pointing to the given branch
+                * would be rather costly for this rare event (the direct
+                * update of a branch) to be worth it.  So let's cheat and
+                * check with HEAD only which should cover 99% of all usage
+                * scenarios (even 100% of the default ones).
+                */
+               unsigned char head_sha1[20];
+               int head_flag;
+               const char *head_ref;
+               head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+               if (head_ref && (head_flag & REF_ISSYMREF) &&
+                   !strcmp(head_ref, lock->ref_name))
+                       log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+       }
        if (commit_lock_file(lock->lk)) {
                error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
@@ -998,6 +1023,9 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        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);
@@ -1037,7 +1065,9 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
                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);
 
@@ -1068,6 +1098,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        unsigned long date;
        unsigned char logged_sha1[20];
        void *log_mapped;
+       size_t mapsz;
 
        logfile = git_path("logs/%s", ref);
        logfd = open(logfile, O_RDONLY, 0);
@@ -1076,7 +1107,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);
-       log_mapped = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);
+       mapsz = xsize_t(st.st_size);
+       log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0);
        logdata = log_mapped;
        close(logfd);
 
@@ -1129,7 +1161,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
                                                logfile, show_rfc2822_date(date, tz));
                                }
                        }
-                       munmap(log_mapped, st.st_size);
+                       munmap(log_mapped, mapsz);
                        return 0;
                }
                lastrec = rec;
@@ -1148,7 +1180,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
                die("Log %s is corrupt.", logfile);
        if (msg)
                *msg = ref_msg(logdata, logend);
-       munmap(log_mapped, st.st_size);
+       munmap(log_mapped, mapsz);
 
        if (cutoff_time)
                *cutoff_time = date;
@@ -1187,12 +1219,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;
@@ -1201,3 +1235,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 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);
+}