[PATCH] Operations on refs
authorDaniel Barkalow <barkalow@iabervon.org>
Mon, 6 Jun 2005 20:31:29 +0000 (16:31 -0400)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 7 Jun 2005 00:09:45 +0000 (17:09 -0700)
This patch adds code to read a hash out of a specified file under
{GIT_DIR}/refs/, and to write such files atomically and optionally with an
compare and lock.

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Makefile
cache.h
refs.c [new file with mode: 0644]
refs.h [new file with mode: 0644]
sha1_file.c

index 5d5c856..50eb01c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,8 @@ install: $(PROG) $(SCRIPTS)
        $(INSTALL) $(PROG) $(SCRIPTS) $(dest)$(bin)
 
 LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \
-        tag.o delta.o date.o index.o diff-delta.o patch-delta.o entry.o epoch.o
+        tag.o delta.o date.o index.o diff-delta.o patch-delta.o entry.o \
+        epoch.o refs.o
 LIB_FILE=libgit.a
 LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h
 
diff --git a/cache.h b/cache.h
index 95353a1..74307f1 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -117,6 +117,7 @@ extern unsigned int active_nr, active_alloc, active_cache_changed;
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 
 extern char *get_object_directory(void);
+extern char *get_refs_directory(void);
 extern char *get_index_file(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
diff --git a/refs.c b/refs.c
new file mode 100644 (file)
index 0000000..9973d1f
--- /dev/null
+++ b/refs.c
@@ -0,0 +1,173 @@
+#include "refs.h"
+#include "cache.h"
+
+#include <errno.h>
+
+static char *ref_file_name(const char *ref)
+{
+       char *base = get_refs_directory();
+       int baselen = strlen(base);
+       int reflen = strlen(ref);
+       char *ret = xmalloc(baselen + 2 + reflen);
+       sprintf(ret, "%s/%s", base, ref);
+       return ret;
+}
+
+static char *ref_lock_file_name(const char *ref)
+{
+       char *base = get_refs_directory();
+       int baselen = strlen(base);
+       int reflen = strlen(ref);
+       char *ret = xmalloc(baselen + 7 + reflen);
+       sprintf(ret, "%s/%s.lock", base, ref);
+       return ret;
+}
+
+static int read_ref_file(const char *filename, unsigned char *sha1) {
+       int fd = open(filename, O_RDONLY);
+       char hex[41];
+       if (fd < 0) {
+               return error("Couldn't open %s\n", filename);
+       }
+       if ((read(fd, hex, 41) < 41) ||
+           (hex[40] != '\n') ||
+           get_sha1_hex(hex, sha1)) {
+               error("Couldn't read a hash from %s\n", filename);
+               close(fd);
+               return -1;
+       }
+       close(fd);
+       return 0;
+}
+
+int get_ref_sha1(const char *ref, unsigned char *sha1)
+{
+       char *filename;
+       int retval;
+       if (check_ref_format(ref))
+               return -1;
+       filename = ref_file_name(ref);
+       retval = read_ref_file(filename, sha1);
+       free(filename);
+       return retval;
+}
+
+static int lock_ref_file(const char *filename, const char *lock_filename,
+                        const unsigned char *old_sha1)
+{
+       int fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
+       unsigned char current_sha1[20];
+       int retval;
+       if (fd < 0) {
+               return error("Couldn't open lock file for %s: %s",
+                            filename, strerror(errno));
+       }
+       retval = read_ref_file(filename, current_sha1);
+       if (old_sha1) {
+               if (retval) {
+                       close(fd);
+                       unlink(lock_filename);
+                       return error("Could not read the current value of %s",
+                                    filename);
+               }
+               if (memcmp(current_sha1, old_sha1, 20)) {
+                       close(fd);
+                       unlink(lock_filename);
+                       error("The current value of %s is %s",
+                             filename, sha1_to_hex(current_sha1));
+                       return error("Expected %s",
+                                    sha1_to_hex(old_sha1));
+               }
+       } else {
+               if (!retval) {
+                       close(fd);
+                       unlink(lock_filename);
+                       return error("Unexpectedly found a value of %s for %s",
+                                    sha1_to_hex(current_sha1), filename);
+               }
+       }
+       return fd;
+}
+
+int lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
+{
+       char *filename;
+       char *lock_filename;
+       int retval;
+       if (check_ref_format(ref))
+               return -1;
+       filename = ref_file_name(ref);
+       lock_filename = ref_lock_file_name(ref);
+       retval = lock_ref_file(filename, lock_filename, old_sha1);
+       free(filename);
+       free(lock_filename);
+       return retval;
+}
+
+static int write_ref_file(const char *filename,
+                         const char *lock_filename, int fd,
+                         const unsigned char *sha1)
+{
+       char *hex = sha1_to_hex(sha1);
+       char term = '\n';
+       if (write(fd, hex, 40) < 40 ||
+           write(fd, &term, 1) < 1) {
+               error("Couldn't write %s\n", filename);
+               close(fd);
+               return -1;
+       }
+       close(fd);
+       rename(lock_filename, filename);
+       return 0;
+}
+
+int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
+{
+       char *filename;
+       char *lock_filename;
+       int retval;
+       if (fd < 0)
+               return -1;
+       if (check_ref_format(ref))
+               return -1;
+       filename = ref_file_name(ref);
+       lock_filename = ref_lock_file_name(ref);
+       retval = write_ref_file(filename, lock_filename, fd, sha1);
+       free(filename);
+       free(lock_filename);
+       return retval;
+}
+
+int check_ref_format(const char *ref)
+{
+       char *middle;
+       if (ref[0] == '.' || ref[0] == '/')
+               return -1;
+       middle = strchr(ref, '/');
+       if (!middle || !middle[1])
+               return -1;
+       if (strchr(middle + 1, '/'))
+               return -1;
+       return 0;
+}
+
+int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
+{
+       char *filename;
+       char *lock_filename;
+       int fd;
+       int retval;
+       if (check_ref_format(ref))
+               return -1;
+       filename = ref_file_name(ref);
+       lock_filename = ref_lock_file_name(ref);
+       fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
+       if (fd < 0) {
+               error("Writing %s", lock_filename);
+               perror("Open");
+       }
+       retval = write_ref_file(filename, lock_filename, fd, sha1);
+       free(filename);
+       free(lock_filename);
+       return retval;
+}
diff --git a/refs.h b/refs.h
new file mode 100644 (file)
index 0000000..60cf480
--- /dev/null
+++ b/refs.h
@@ -0,0 +1,21 @@
+#ifndef REFS_H
+#define REFS_H
+
+/** Reads the refs file specified into sha1 **/
+extern int get_ref_sha1(const char *ref, unsigned char *sha1);
+
+/** Locks ref and returns the fd to give to write_ref_sha1() if the ref
+ * has the given value currently; otherwise, returns -1.
+ **/
+extern int lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
+
+/** Writes sha1 into the refs file specified, locked with the given fd. **/
+extern int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1);
+
+/** Writes sha1 into the refs file specified. **/
+extern int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1);
+
+/** Returns 0 if target has the right format for a ref. **/
+extern int check_ref_format(const char *target);
+
+#endif /* REFS_H */
index a2ba4c8..7cfd43c 100644 (file)
@@ -58,7 +58,7 @@ static int get_sha1_file(const char *path, unsigned char *result)
        return get_sha1_hex(buffer, result);
 }
 
-static char *git_dir, *git_object_dir, *git_index_file;
+static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir;
 static void setup_git_env(void)
 {
        git_dir = gitenv(GIT_DIR_ENVIRONMENT);
@@ -69,6 +69,8 @@ static void setup_git_env(void)
                git_object_dir = xmalloc(strlen(git_dir) + 9);
                sprintf(git_object_dir, "%s/objects", git_dir);
        }
+       git_refs_dir = xmalloc(strlen(git_dir) + 6);
+       sprintf(git_refs_dir, "%s/refs", git_dir);
        git_index_file = gitenv(INDEX_ENVIRONMENT);
        if (!git_index_file) {
                git_index_file = xmalloc(strlen(git_dir) + 7);
@@ -83,6 +85,13 @@ char *get_object_directory(void)
        return git_object_dir;
 }
 
+char *get_refs_directory(void)
+{
+       if (!git_refs_dir)
+               setup_git_env();
+       return git_refs_dir;
+}
+
 char *get_index_file(void)
 {
        if (!git_index_file)