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);
- 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) {
- 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))
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);
}
return commit_lock_file(&packlock);
}
-int delete_ref(const char *refname, unsigned char *sha1)
+int delete_ref(const char *refname, const unsigned char *sha1)
{
struct ref_lock *lock;
int err, i, ret = 0, flag = 0;
goto rollback;
}
- if (!strncmp(oldref, "refs/heads/", 11) &&
- !strncmp(newref, "refs/heads/", 11)) {
- char oldsection[1024], newsection[1024];
-
- snprintf(oldsection, 1024, "branch.%s", oldref + 11);
- snprintf(newsection, 1024, "branch.%s", newref + 11);
- if (git_config_rename_section(oldsection, newsection) < 0)
- return 1;
- }
-
return 0;
rollback:
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",
log_file);
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 */
return -1;
}
invalidate_cached_refs();
- if (log_ref_write(lock->ref_name, lock->old_sha1, 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;
}
+ 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);
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;
+ }
+
+#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;
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);
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);
logfile, show_rfc2822_date(date, tz));
}
}
- munmap(log_mapped, st.st_size);
+ munmap(log_mapped, mapsz);
return 0;
}
lastrec = rec;
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;
!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;
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);
+}