From: Linus Torvalds <torvalds@osdl.org>
To: Junio C Hamano <junkio@cox.net>, Git Mailing List <git@vger.kernel.org>
Cc: Davide Libenzi <davidel@xmailserver.org>
Subject: Add "git-update-ref" to update the HEAD (or other) ref
Date: Sun, 25 Sep 2005 11:43:05 -0700 (PDT) [thread overview]
Message-ID: <Pine.LNX.4.58.0509251134480.3308@g5.osdl.org> (raw)
This is a careful version of the script stuff that currently just
blindly writes HEAD with a new value.
You can use
git-update-ref HEAD <newhead>
or
git-update-ref HEAD <newhead> <oldhead>
where the latter version verifies that the old value of HEAD matches
oldhead.
It basically allows a "ref" file to be a symbolic pointer to another ref
file by starting with the four-byte header sequence of "ref:".
More importantly, it allows the update of a ref file to follow these
symbolic pointers, whether they are symlinks or these "regular file
symbolic refs".
NOTE! It follows _real_ symlinks only if they start with "refs/":
otherwise it will just try to read them and update them as a regular file
(ie it will allow the filesystem to follow them, but will overwrite such a
symlink to somewhere else with a regular filename).
In general, using
git-update-ref HEAD "$head"
should be a _lot_ safer than doing
echo "$head" > "$GIT_DIR/HEAD"
both from a symlink following standpoint _and_ an error checking
standpoint. The "refs/" rule for symlinks means that symlinks that point
to "outside" the tree are safe: they'll be followed for reading but not
for writing (so we'll never write through a ref symlink to some other
tree, if you have copied a whole archive by creating a symlink tree).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---
This is independent of my previous patch, but in the same basic series. It
is useful regardless of whether you use the new symbolic refs or not
because of the much improved error checking.
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -114,6 +114,7 @@ PROGRAMS = \
git-ssh-upload git-tar-tree git-unpack-file \
git-unpack-objects git-update-index git-update-server-info \
git-upload-pack git-verify-pack git-write-tree \
+ git-update-ref \
$(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed in 0.99.8
diff --git a/update-ref.c b/update-ref.c
new file mode 100644
--- /dev/null
+++ b/update-ref.c
@@ -0,0 +1,124 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
+
+#define MAXDEPTH 5
+
+const char *resolve_ref(const char *path, unsigned char *sha1)
+{
+ int depth = MAXDEPTH, len;
+ char buffer[256];
+
+ for (;;) {
+ struct stat st;
+ char *buf;
+ int fd;
+
+ if (--depth < 0)
+ return NULL;
+
+ /* Special case: non-existing file */
+ if (lstat(path, &st) < 0) {
+ if (errno != ENOENT)
+ return NULL;
+ memset(sha1, 0, 20);
+ return path;
+ }
+
+ /* Follow "normalized" - ie "refs/.." symlinks by hand */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5)) {
+ path = git_path("%.*s", len, buffer);
+ continue;
+ }
+ }
+
+ /*
+ * Anything else, just open it and try to use it as
+ * a ref
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+ len = read(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (len < 4 || memcmp("ref:", buffer, 4))
+ break;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ while (len && isspace(buf[len-1]))
+ buf[--len] = 0;
+ path = git_path("%.*s", len, buf);
+ }
+ if (len < 40 || get_sha1_hex(buffer, sha1))
+ return NULL;
+ return path;
+}
+
+int main(int argc, char **argv)
+{
+ char *hex;
+ const char *refname, *value, *oldval, *path, *lockpath;
+ unsigned char sha1[20], oldsha1[20], currsha1[20];
+ int fd, written;
+
+ setup_git_directory();
+ if (argc < 3 || argc > 4)
+ usage(git_update_ref_usage);
+
+ refname = argv[1];
+ value = argv[2];
+ oldval = argv[3];
+ if (get_sha1(value, sha1) < 0)
+ die("%s: not a valid SHA1", value);
+ memset(oldsha1, 0, 20);
+ if (oldval && get_sha1(oldval, oldsha1) < 0)
+ die("%s: not a valid old SHA1", oldval);
+
+ path = resolve_ref(git_path("%s", refname), currsha1);
+ if (!path)
+ die("No such ref: %s", refname);
+
+ if (oldval) {
+ if (memcmp(currsha1, oldsha1, 20))
+ die("Ref %s changed to %s", refname, sha1_to_hex(currsha1));
+ /* Nothing to do? */
+ if (!memcmp(oldsha1, sha1, 20))
+ exit(0);
+ }
+ path = strdup(path);
+ lockpath = mkpath("%s.lock", path);
+
+ fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0)
+ die("Unable to create %s", lockpath);
+ hex = sha1_to_hex(sha1);
+ hex[40] = '\n';
+ written = write(fd, hex, 41);
+ close(fd);
+ if (written != 41) {
+ unlink(lockpath);
+ die("Unable to write to %s", lockpath);
+ }
+
+ /*
+ * FIXME!
+ *
+ * We should re-read the old ref here, and re-verify that it
+ * matches "oldsha1". Otherwise there's a small race.
+ */
+
+ if (rename(lockpath, path) < 0) {
+ unlink(lockpath);
+ die("Unable to create %s", path);
+ }
+ return 0;
+}
next reply other threads:[~2005-09-25 18:43 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-09-25 18:43 Linus Torvalds [this message]
2005-09-25 19:05 ` Add "git-update-ref" to update the HEAD (or other) ref Linus Torvalds
2005-09-25 22:37 ` Junio C Hamano
2005-09-28 2:45 ` [PATCH] Use git-update-ref in scripts Junio C Hamano
2005-09-28 15:21 ` Linus Torvalds
2005-09-28 16:56 ` Junio C Hamano
2005-09-28 17:13 ` Linus Torvalds
2005-09-28 17:29 ` Junio C Hamano
2005-09-28 18:14 ` Linus Torvalds
2005-09-28 18:28 ` Junio C Hamano
2005-09-29 16:07 ` Linus Torvalds
2005-09-28 18:17 ` Junio C Hamano
2005-09-28 19:47 ` Junio C Hamano
2005-09-28 21:19 ` Fredrik Kuivinen
2005-09-29 15:16 ` Linus Torvalds
2005-09-25 23:27 ` Add "git-update-ref" to update the HEAD (or other) ref Junio C Hamano
2005-09-26 0:50 ` Linus Torvalds
2005-09-26 4:25 ` Junio C Hamano
2005-09-26 1:07 ` Linus Torvalds
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Pine.LNX.4.58.0509251134480.3308@g5.osdl.org \
--to=torvalds@osdl.org \
--cc=davidel@xmailserver.org \
--cc=git@vger.kernel.org \
--cc=junkio@cox.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox