Commit 9e672c7443bebadbd8806c9a4366543f852eb372

Stefan Sperling 2019-03-11T14:56:04

implement got_ref_write()

diff --git a/got/Makefile b/got/Makefile
index ee6fe64..a0d4587 100644
--- a/got/Makefile
+++ b/got/Makefile
@@ -5,7 +5,7 @@ SRCS=		got.c blame.c commit_graph.c delta.c diff.c diffoffset.c \
 		diffreg.c error.c fileindex.c object.c object_cache.c \
 		object_idset.c object_parse.c opentemp.c path.c pack.c \
 		privsep.c reference.c repository.c sha1.c worktree.c \
-		inflate.c buf.c worklist.c rcsutil.c diff3.c
+		inflate.c buf.c worklist.c rcsutil.c diff3.c lockfile.c
 
 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib \
 	-DGOT_LIBEXECDIR=${GOT_LIBEXECDIR}
diff --git a/include/got_reference.h b/include/got_reference.h
index dfb265d..6f15ea2 100644
--- a/include/got_reference.h
+++ b/include/got_reference.h
@@ -73,3 +73,7 @@ SIMPLEQ_HEAD(got_reflist_head, got_reflist_entry);
 /* Append all known references to a caller-provided ref list head. */
 const struct got_error *got_ref_list(struct got_reflist_head *,
     struct got_repository *);
+
+/* Write a reference to its on-disk path in the repository. */
+const struct got_error *got_ref_write(struct got_reference *,
+    struct got_repository *);
diff --git a/lib/reference.c b/lib/reference.c
index 9e96551..18fca1a 100644
--- a/lib/reference.c
+++ b/lib/reference.c
@@ -16,6 +16,7 @@
 
 #include <sys/types.h>
 #include <sys/queue.h>
+#include <sys/stat.h>
 
 #include <ctype.h>
 #include <dirent.h>
@@ -31,6 +32,7 @@
 #include "got_object.h"
 #include "got_repository.h"
 #include "got_reference.h"
+#include "got_opentemp.h"
 
 #include "got_lib_sha1.h"
 #include "got_lib_path.h"
@@ -696,3 +698,90 @@ done:
 		err = got_error_from_errno();
 	return err;
 }
+
+const struct got_error *
+got_ref_write(struct got_reference *ref, struct got_repository *repo)
+{
+	const struct got_error *err = NULL, *unlock_err = NULL;
+	const char *name = got_ref_get_name(ref);
+	char *path_refs = NULL, *path = NULL, *tmppath = NULL;
+	struct got_lockfile *lf = NULL;
+	FILE *f = NULL;
+	size_t n;
+	struct stat sb;
+
+	path_refs = get_refs_dir_path(repo, name);
+	if (path_refs == NULL) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	if (asprintf(&path, "%s/%s", path_refs, name) == -1) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	err = got_opentemp_named(&tmppath, &f, path);
+	if (f == NULL) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	if (ref->flags & GOT_REF_IS_SYMBOLIC) {
+		n = fprintf(f, "ref: %s\n", ref->ref.symref.ref);
+		if (n != strlen(ref->ref.symref.ref) + 6) {
+			err = got_ferror(f, GOT_ERR_IO);
+			goto done;
+		}
+	} else {
+		char hex[SHA1_DIGEST_STRING_LENGTH];
+		if (got_sha1_digest_to_str(ref->ref.ref.sha1, hex,
+		    sizeof(hex)) == NULL) {
+			err = got_error(GOT_ERR_BAD_REF_DATA);
+			goto done;
+		}
+		n = fprintf(f, "%s\n", hex);
+		if (n != sizeof(hex) + 1) {
+			err = got_ferror(f, GOT_ERR_IO);
+			goto done;
+		}
+	}
+
+	err = got_lockfile_lock(&lf, path);
+	if (err)
+		goto done;
+
+	/* XXX: check if old content matches our expectations? */
+
+	if (stat(path, &sb) != 0) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	if (rename(tmppath, path) != 0) {
+		err = got_error_from_errno();
+		goto done;
+	}
+	free(tmppath);
+	tmppath = NULL;
+
+	if (chmod(path, sb.st_mode) != 0) {
+		err = got_error_from_errno();
+		goto done;
+	}
+done:
+	if (lf)
+		unlock_err = got_lockfile_unlock(lf);
+	if (f) {
+		if (fclose(f) != 0 && err == NULL)
+			err = got_error_from_errno();
+	}
+	free(path_refs);
+	free(path);
+	if (tmppath) {
+		if (unlink(tmppath) != 0 && err == NULL)
+			err = got_error_from_errno();
+		free(tmppath);
+	}
+	return err ? err : unlock_err;
+}
diff --git a/regress/idset/Makefile b/regress/idset/Makefile
index 8fb53df..f9ca312 100644
--- a/regress/idset/Makefile
+++ b/regress/idset/Makefile
@@ -4,7 +4,7 @@ PROG = idset_test
 SRCS = error.c object.c privsep.c sha1.c pack.c inflate.c path.c opentemp.c \
 	delta.c repository.c reference.c worktree.c fileindex.c object_cache.c \
 	object_idset.c object_parse.c idset_test.c \
-	buf.c worklist.c rcsutil.c diff.c diffreg.c diff3.c
+	buf.c worklist.c rcsutil.c diff.c diffreg.c diff3.c lockfile.c
 
 CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib
 LDADD = -lutil -lz
diff --git a/regress/repository/Makefile b/regress/repository/Makefile
index d75f821..77226c0 100644
--- a/regress/repository/Makefile
+++ b/regress/repository/Makefile
@@ -4,7 +4,7 @@ PROG = repository_test
 SRCS = path.c repository.c error.c reference.c object.c object_cache.c \
 	object_idset.c object_parse.c opentemp.c sha1.c diff.c diffreg.c \
 	pack.c privsep.c delta.c fileindex.c worktree.c inflate.c \
-	buf.c worklist.c rcsutil.c diff3.c \
+	buf.c worklist.c rcsutil.c diff3.c lockfile.c \
 	repository_test.c
 
 CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib \
diff --git a/regress/worktree/Makefile b/regress/worktree/Makefile
index 21476af..1f297d9 100644
--- a/regress/worktree/Makefile
+++ b/regress/worktree/Makefile
@@ -4,7 +4,7 @@ PROG = worktree_test
 SRCS = worktree.c repository.c object.c object_cache.c object_idset.c \
 	object_parse.c opentemp.c path.c error.c reference.c sha1.c pack.c \
 	privsep.c delta.c inflate.c fileindex.c \
-	buf.c worklist.c rcsutil.c diff.c diffreg.c diff3.c \
+	buf.c worklist.c rcsutil.c diff.c diffreg.c diff3.c lockfile.c \
 	worktree_test.c
 
 CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib \
diff --git a/tog/Makefile b/tog/Makefile
index 87516af..144d8da 100644
--- a/tog/Makefile
+++ b/tog/Makefile
@@ -5,7 +5,8 @@ SRCS=		tog.c blame.c commit_graph.c delta.c diff.c diffoffset.c \
 		diffreg.c error.c fileindex.c object.c object_cache.c \
 		object_idset.c object_parse.c opentemp.c path.c pack.c \
 		privsep.c reference.c repository.c sha1.c worktree.c \
-		utf8.c inflate.c buf.c worklist.c rcsutil.c diff3.c
+		utf8.c inflate.c buf.c worklist.c rcsutil.c diff3.c \
+		lockfile.c
 
 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib \
 	-DGOT_LIBEXECDIR=${GOT_LIBEXECDIR}