static void check_notes_merge_worktree(struct notes_merge_options *o) { if (!o->has_worktree) { /* * Must establish NOTES_MERGE_WORKTREE. * Abort if NOTES_MERGE_WORKTREE already exists */ if (file_exists(git_path(NOTES_MERGE_WORKTREE))) { if (advice_resolve_conflict) die("You have not concluded your previous " "notes merge (%s exists).\nPlease, use " "'git notes merge --commit' or 'git notes " "merge --abort' to commit/abort the " "previous merge before you start a new " "notes merge.", git_path("NOTES_MERGE_*")); else die("You have not concluded your notes merge " "(%s exists).", git_path("NOTES_MERGE_*")); } if (safe_create_leading_directories(git_path( NOTES_MERGE_WORKTREE "/.test"))) die_errno("unable to create directory %s", git_path(NOTES_MERGE_WORKTREE)); o->has_worktree = 1; } else if (!file_exists(git_path(NOTES_MERGE_WORKTREE))) /* NOTES_MERGE_WORKTREE should already be established */ die("missing '%s'. This should not happen", git_path(NOTES_MERGE_WORKTREE)); }
static void write_buf_to_worktree(const unsigned char *obj, const char *buf, unsigned long size) { int fd; char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj)); if (safe_create_leading_directories(path)) die_errno("unable to create directory for '%s'", path); if (file_exists(path)) die("found existing file at '%s'", path); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666); if (fd < 0) die_errno("failed to open '%s'", path); while (size > 0) { long ret = write_in_full(fd, buf, size); if (ret < 0) { /* Ignore epipe */ if (errno == EPIPE) break; die_errno("notes-merge"); } else if (!ret) { die("notes-merge: disk full?"); } size -= ret; buf += ret; } close(fd); }
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; }
static int copy_file(const char *source, char *dest, const char *hex, int warn_if_not_exists) { safe_create_leading_directories(dest); if (use_link) { if (!link(source, dest)) { pull_say("link %s\n", hex); return 0; } /* If we got ENOENT there is no point continuing. */ if (errno == ENOENT) { if (warn_if_not_exists) fprintf(stderr, "does not exist %s\n", source); return -1; } } if (use_symlink) { struct stat st; if (stat(source, &st)) { if (!warn_if_not_exists && errno == ENOENT) return -1; fprintf(stderr, "cannot stat %s: %s\n", source, strerror(errno)); return -1; } if (!symlink(source, dest)) { pull_say("symlink %s\n", hex); return 0; } } if (use_filecopy) { int ifd, ofd, status = 0; ifd = open(source, O_RDONLY); if (ifd < 0) { if (!warn_if_not_exists && errno == ENOENT) return -1; fprintf(stderr, "cannot open %s\n", source); return -1; } ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666); if (ofd < 0) { fprintf(stderr, "cannot open %s\n", dest); close(ifd); return -1; } status = copy_fd(ifd, ofd); close(ofd); if (status) fprintf(stderr, "cannot write %s\n", dest); else pull_say("copy %s\n", hex); return status; } fprintf(stderr, "failed to copy %s with given copy methods.\n", hex); return -1; }
static int ensure_leading_directories(char *path) { switch (safe_create_leading_directories(path)) { case SCLD_OK: case SCLD_EXISTS: return 0; default: return error(_("could not create leading directories " "of '%s'"), path); } }
static int write_one_ref(const char *name, const unsigned char *sha1, int flags, void *data) { struct strbuf *buf = data; int len = buf->len; /* when called via for_each_ref(), flags is non-zero */ if (flags && !starts_with(name, "refs/heads/") && !starts_with(name, "refs/tags/")) return 0; strbuf_addstr(buf, name); if (safe_create_leading_directories(buf->buf) || write_file(buf->buf, 0, "%s\n", sha1_to_hex(sha1))) return error("problems writing temporary file %s: %s", buf->buf, strerror(errno)); strbuf_setlen(buf, len); return 0; }
static int write_one_ref(const char *name, const unsigned char *sha1, int flags, void *data) { struct strbuf *buf = data; int len = buf->len; FILE *f; /* when called via for_each_ref(), flags is non-zero */ if (flags && prefixcmp(name, "refs/heads/") && prefixcmp(name, "refs/tags/")) return 0; strbuf_addstr(buf, name); if (safe_create_leading_directories(buf->buf) || !(f = fopen(buf->buf, "w")) || fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 || fclose(f)) return error("problems writing temporary file %s", buf->buf); strbuf_setlen(buf, len); return 0; }
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; }
/* * Create the file "path" by writing to a temporary file and renaming * it into place. The contents of the file come from "generate", which * should return non-zero if it encounters an error. */ static int update_info_file(char *path, int (*generate)(FILE *)) { char *tmp = mkpathdup("%s_XXXXXX", path); int ret = -1; int fd = -1; FILE *fp = NULL; safe_create_leading_directories(path); fd = git_mkstemp_mode(tmp, 0666); if (fd < 0) goto out; fp = fdopen(fd, "w"); if (!fp) goto out; ret = generate(fp); if (ret) goto out; if (fclose(fp)) goto out; if (adjust_shared_perm(tmp) < 0) goto out; if (rename(tmp, path) < 0) goto out; ret = 0; out: if (ret) { error_errno("unable to update %s", path); if (fp) fclose(fp); else if (fd >= 0) close(fd); unlink(tmp); } free(tmp); return ret; }
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 = git_pathdup("%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); if (close(fd) != 0 || 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_or_warn(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; }
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; }