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; 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 && (!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); oflags |= O_CREAT; } 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(log_file)) { return error("There are still logs under '%s'", log_file); } logfd = open(log_file, oflags, 0666); } if (logfd < 0) return error("Unable to append to %s: %s", log_file, strerror(errno)); } adjust_shared_perm(log_file); msglen = msg ? strlen(msg) : 0; committer = git_committer_info(0); 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 += copy_msg(logrec + len - 1, msg) - 1; written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1; free(logrec); if (close(logfd) != 0 || written != len) return error("Unable to append to %s", log_file); return 0; }
bool remove_empty_directories_recur(const fs::path& p, std::vector<fs::path>& dirs) { if (!fs::exists(p)) return false; bool ret = true; for (fs::directory_iterator i = fs::directory_iterator(p), e = fs::directory_iterator(); i != e; ++i) { if (!fs::is_directory(*i)) ret = false; else { if (!remove_empty_directories(*i)) ret = false; } } if (ret) { HAL_DEV_MSG(wform(L"Removing directory: %1%") % p.wstring()); dirs.push_back(p); } return ret; }
int log_ref_setup(const char *ref_name, char *logfile, int bufsize) { int logfd, oflags = O_APPEND | O_WRONLY; git_snpath(logfile, bufsize, "logs/%s", ref_name); if (log_all_ref_updates && (!prefixcmp(ref_name, "refs/heads/") || !prefixcmp(ref_name, "refs/remotes/") || !prefixcmp(ref_name, "refs/notes/") || !strcmp(ref_name, "HEAD"))) { if (safe_create_leading_directories(logfile) < 0) return error("unable to create directory for %s", logfile); oflags |= O_CREAT; } logfd = open(logfile, oflags, 0666); if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; if ((oflags & O_CREAT) && errno == EISDIR) { if (remove_empty_directories(logfile)) { return error("There are still logs under '%s'", logfile); } logfd = open(logfile, oflags, 0666); } if (logfd < 0) return error("Unable to append to %s: %s", logfile, strerror(errno)); } adjust_shared_perm(logfile); close(logfd); return 0; }
int rename_ref(const char *oldref, const char *newref, const char *logmsg) { static const char renamed_ref[] = "RENAMED-REF"; unsigned char sha1[20], orig_sha1[20]; int flag = 0, logmoved = 0; struct ref_lock *lock; struct stat loginfo; int log = !lstat(git_path("logs/%s", oldref), &loginfo); const char *symref = NULL; if (log && S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldref); symref = resolve_ref(oldref, orig_sha1, 1, &flag); if (flag & REF_ISSYMREF) return error("refname %s is a symbolic ref, renaming it is not supported", oldref); if (!symref) return error("refname %s not found", oldref); if (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0)) return 1; if (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0)) return 1; lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL); if (!lock) return error("unable to lock %s", renamed_ref); lock->force_write = 1; if (write_ref_sha1(lock, orig_sha1, logmsg)) return error("unable to save current sha1 in %s", renamed_ref); if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG))) return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s", oldref, strerror(errno)); if (delete_ref(oldref, orig_sha1, REF_NODEREF)) { error("unable to delete old %s", oldref); goto rollback; } if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) { if (errno==EISDIR) { if (remove_empty_directories(git_path("%s", newref))) { error("Directory not empty: %s", newref); goto rollback; } } else { error("unable to delete existing %s", newref); goto rollback; } } if (log && safe_create_leading_directories(git_path("logs/%s", newref))) { error("unable to create directory for %s", newref); goto rollback; } retry: if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) { 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; } goto retry; } else { error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s", newref, strerror(errno)); goto rollback; } } logmoved = log; lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); if (!lock) { error("unable to lock %s for update", newref); goto rollback; } lock->force_write = 1; hashcpy(lock->old_sha1, orig_sha1); if (write_ref_sha1(lock, orig_sha1, logmsg)) { error("unable to write current sha1 into %s", newref); goto rollback; } return 0; rollback: lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL); if (!lock) { error("unable to lock %s for rollback", oldref); goto rollbacklog; } lock->force_write = 1; flag = log_all_ref_updates; log_all_ref_updates = 0; if (write_ref_sha1(lock, orig_sha1, NULL)) error("unable to write current sha1 into %s", oldref); log_all_ref_updates = flag; rollbacklog: if (logmoved && rename(git_path("logs/%s", newref), git_path("logs/%s", oldref))) error("unable to restore logfile %s from %s: %s", oldref, newref, strerror(errno)); if (!logmoved && log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref))) error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s", oldref, strerror(errno)); return 1; }
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p) { char *ref_file; const char *orig_ref = ref; struct ref_lock *lock; int last_errno = 0; int type, lflags; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); int missing = 0; lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; ref = resolve_ref(ref, lock->old_sha1, mustexist, &type); if (!ref && errno == EISDIR) { /* we are trying to lock foo but we used to * have foo/bar which now does not exist; * it is normal for the empty directory 'foo' * to remain. */ ref_file = git_path("%s", orig_ref); if (remove_empty_directories(ref_file)) { last_errno = errno; error("there are still refs under '%s'", orig_ref); goto error_return; } ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type); } if (type_p) *type_p = type; if (!ref) { last_errno = errno; error("unable to resolve reference %s: %s", orig_ref, strerror(errno)); goto error_return; } missing = is_null_sha1(lock->old_sha1); /* When the ref did not exist and we are creating it, * make sure there is no existing ref that is packed * whose name begins with our refname, nor a ref whose * name is a proper prefix of our refname. */ if (missing && !is_refname_available(ref, NULL, get_packed_refs(NULL), 0)) { last_errno = ENOTDIR; goto error_return; } lock->lk = xcalloc(1, sizeof(struct lock_file)); lflags = LOCK_DIE_ON_ERROR; if (flags & REF_NODEREF) { ref = orig_ref; lflags |= LOCK_NODEREF; } lock->ref_name = xstrdup(ref); lock->orig_ref_name = xstrdup(orig_ref); ref_file = git_path("%s", ref); if (missing) lock->force_write = 1; if ((flags & REF_NODEREF) && (type & REF_ISSYMREF)) lock->force_write = 1; if (safe_create_leading_directories(ref_file)) { last_errno = errno; error("unable to create directory for %s", ref_file); goto error_return; } lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; error_return: unlock_ref(lock); errno = last_errno; return NULL; }