git-ssh-upload
git-status
git-stripspace
+git-symbolic-ref
git-tag
git-tar-tree
git-unpack-file
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 \
+ git-update-ref git-symbolic-ref \
$(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed after 1.0
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
+extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
+extern int validate_symref(const char *git_HEAD);
/* General helper functions */
extern void usage(const char *err) NORETURN;
static int fsck_head_link(void)
{
- int fd, count;
- char hex[40];
unsigned char sha1[20];
- static char path[PATH_MAX], link[PATH_MAX];
- const char *git_dir = get_git_dir();
-
- snprintf(path, sizeof(path), "%s/HEAD", git_dir);
- if (readlink(path, link, sizeof(link)) < 0)
- return error("HEAD is not a symlink");
- if (strncmp("refs/heads/", link, 11))
- return error("HEAD points to something strange (%s)", link);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return error("HEAD: %s", strerror(errno));
- count = read(fd, hex, sizeof(hex));
- close(fd);
- if (count < 0)
- return error("HEAD: %s", strerror(errno));
- if (count < 40 || get_sha1_hex(hex, sha1))
+ const char *git_HEAD = strdup(git_path("HEAD"));
+ const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
+ int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
+
+ if (!git_refs_heads_master)
+ return error("HEAD is not a symbolic ref");
+ if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
+ return error("HEAD points to something strange (%s)",
+ git_refs_heads_master + pfxlen);
+ if (!memcmp(null_sha1, sha1, 20))
return error("HEAD: not a valid git pointer");
return 0;
}
# Verify HEAD. If we were bisecting before this, reset to the
# top-of-line master first!
#
- head=$(readlink $GIT_DIR/HEAD) || die "Bad HEAD - I need a symlink"
+ head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
+ die "Bad HEAD - I need a symbolic ref"
case "$head" in
refs/heads/bisect*)
git checkout master || exit
refs/heads/*)
;;
*)
- die "Bad HEAD - strange symlink"
+ die "Bad HEAD - strange symbolic ref"
;;
esac
echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
git checkout new-bisect || exit
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
- ln -sf refs/heads/bisect "$GIT_DIR/HEAD"
+ GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
git-show-branch "$rev"
}
delete_branch () {
option="$1" branch_name="$2"
- headref=$(readlink "$GIT_DIR/HEAD" | sed -e 's|^refs/heads/||')
+ headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
+ sed -e 's|^refs/heads/||')
case ",$headref," in
",$branch_name,")
die "Cannot delete the branch you are on." ;;
case "$#" in
0)
- headref=$(readlink "$GIT_DIR/HEAD" | sed -e 's|^refs/heads/||')
+ headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
+ sed -e 's|^refs/heads/||')
git-rev-parse --symbolic --all |
sed -ne 's|^refs/heads/||p' |
sort |
echo $new > "$GIT_DIR/refs/heads/$newbranch"
branch="$newbranch"
fi
- [ "$branch" ] && ln -sf "refs/heads/$branch" "$GIT_DIR/HEAD"
+ [ "$branch" ] &&
+ GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
rm -f "$GIT_DIR/MERGE_HEAD"
else
exit 1
fi >>.editmsg
PARENTS="-p HEAD"
-if [ ! -r "$GIT_DIR/HEAD" ]; then
- if [ -z "$(git-ls-files)" ]; then
- echo Nothing to commit 1>&2
- exit 1
- fi
- PARENTS=""
- current=
-else
- current=$(git-rev-parse --verify HEAD)
+if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
+then
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
fi
export GIT_AUTHOR_EMAIL
export GIT_AUTHOR_DATE
fi
+else
+ if [ -z "$(git-ls-files)" ]; then
+ echo Nothing to commit 1>&2
+ exit 1
+ fi
+ PARENTS=""
fi
git-status >>.editmsg
if [ "$?" != "0" -a ! -f $GIT_DIR/MERGE_HEAD ]
unset CDPATH
die() {
- echo "$@" >&2
+ echo >&2 "$@"
exit 1
}
-[ -h "$GIT_DIR/HEAD" ] &&
+case "$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD 2>/dev/null)" in
+refs/*) : ;;
+*) false ;;
+esac &&
[ -d "$GIT_DIR/refs" ] &&
[ -d "$GIT_OBJECT_DIRECTORY/00" ]
[ "$header" ]
}
-branch=`readlink "$GIT_DIR/HEAD"`
+branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
case "$branch" in
refs/heads/master) ;;
*) echo "# On branch $branch" ;;
git-update-index --refresh >/dev/null 2>&1
-if test -f "$GIT_DIR/HEAD"
+if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
then
git-diff-index -M --cached HEAD |
sed 's/^://' |
{
unsigned len = strlen(git_dir);
static char path[PATH_MAX];
+ unsigned char sha1[20];
if (len > sizeof(path)-50)
die("insane git directory %s", git_dir);
/*
* Create the default symlink from ".git/HEAD" to the "master"
- * branch
+ * branch, if it does not exist yet.
*/
strcpy(path + len, "HEAD");
- if (symlink("refs/heads/master", path) < 0) {
- if (errno != EEXIST) {
- perror(path);
+ if (read_ref(path, sha1) < 0) {
+ if (create_symref(path, "refs/heads/master") < 0)
exit(1);
- }
}
+ path[len] = 0;
copy_templates(path, len, template_path);
}
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
+#ifndef USE_SYMLINK_HEAD
+#define USE_SYMLINK_HEAD 1
+#endif
+
+int validate_symref(const char *path)
+{
+ struct stat st;
+ char *buf, buffer[256];
+ int len, fd;
+
+ if (lstat(path, &st) < 0)
+ return -1;
+
+ /* Make sure it is a "refs/.." symlink */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+ }
+
+ /*
+ * Anything else, just open it and try to see if it is a symbolic ref.
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ len = read(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (len < 4 || memcmp("ref:", buffer, 4))
+ return -1;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+}
+
const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
{
int depth = MAXDEPTH, len;
return path;
}
+int create_symref(const char *git_HEAD, const char *refs_heads_master)
+{
+#if USE_SYMLINK_HEAD
+ unlink(git_HEAD);
+ return symlink(refs_heads_master, git_HEAD);
+#else
+ const char *lockpath;
+ char ref[1000];
+ int fd, len, written;
+
+ len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
+ if (sizeof(ref) <= len) {
+ error("refname too long: %s", refs_heads_master);
+ return -1;
+ }
+ lockpath = mkpath("%s.lock", git_HEAD);
+ fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ written = write(fd, ref, len);
+ close(fd);
+ if (written != len) {
+ unlink(lockpath);
+ error("Unable to write to %s", lockpath);
+ return -2;
+ }
+ if (rename(lockpath, git_HEAD) < 0) {
+ unlink(lockpath);
+ error("Unable to create %s", git_HEAD);
+ return -3;
+ }
+ return 0;
+#endif
+}
+
int read_ref(const char *filename, unsigned char *sha1)
{
if (resolve_ref(filename, sha1, 1))
* Test it it looks like we're at the top
* level git directory. We want to see a
*
- * - a HEAD symlink and a refs/ directory under ".git"
* - either a .git/objects/ directory _or_ the proper
* GIT_OBJECT_DIRECTORY environment variable
+ * - a refs/ directory under ".git"
+ * - either a HEAD symlink or a HEAD file that is formatted as
+ * a proper "ref:".
*/
static int is_toplevel_directory(void)
{
- struct stat st;
-
- return !lstat(".git/HEAD", &st) &&
- S_ISLNK(st.st_mode) &&
- !access(".git/refs/", X_OK) &&
- (getenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK));
+ if (access(".git/refs/", X_OK) ||
+ access(getenv(DB_ENVIRONMENT) ?
+ getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
+ validate_symref(".git/HEAD"))
+ return 0;
+ return 1;
}
const char *setup_git_directory(void)
int all_heads = 0, all_tags = 0;
int all_mask, all_revs, shown_merge_point;
char head_path[128];
+ const char *head_path_p;
int head_path_len;
unsigned char head_sha1[20];
int merge_base = 0;
if (0 <= extra)
join_revs(&list, &seen, num_rev, extra);
- head_path_len = readlink(".git/HEAD", head_path, sizeof(head_path)-1);
- if ((head_path_len < 0) || get_sha1("HEAD", head_sha1))
+ head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
+ if (head_path_p) {
+ head_path_len = strlen(head_path_p);
+ memcpy(head_path, head_path_p, head_path_len + 1);
+ }
+ else {
+ head_path_len = 0;
head_path[0] = 0;
- else
- head_path[head_path_len] = 0;
+ }
if (merge_base)
return show_merge_base(seen, num_rev);
--- /dev/null
+#include "cache.h"
+
+static const char git_symbolic_ref_usage[] =
+"git-symbolic-ref name [ref]";
+
+static int check_symref(const char *HEAD)
+{
+ unsigned char sha1[20];
+ const char *git_HEAD = strdup(git_path("%s", HEAD));
+ const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
+ if (git_refs_heads_master) {
+ /* we want to strip the .git/ part */
+ int pfxlen = strlen(git_HEAD) - strlen(HEAD);
+ puts(git_refs_heads_master + pfxlen);
+ }
+ else
+ die("No such ref: %s", HEAD);
+}
+
+int main(int argc, const char **argv)
+{
+ setup_git_directory();
+ switch (argc) {
+ case 2:
+ check_symref(argv[1]);
+ break;
+ case 3:
+ create_symref(strdup(git_path("%s", argv[1])), argv[2]);
+ break;
+ default:
+ usage(git_symbolic_ref_usage);
+ }
+ return 0;
+}
commit=$(echo "Commit #$i" | git-commit-tree $tree -p $parent) &&
parent=$commit || return 1
done &&
- echo "$commit" >.git/HEAD &&
+ git-update-ref HEAD "$commit" &&
git-clone -l ./. victim &&
cd victim &&
git-log &&
cd .. &&
- echo $zero >.git/HEAD &&
+ git-update-ref HEAD "$zero" &&
parent=$zero &&
for i in $cnt
do
commit=$(echo "Rebase #$i" | git-commit-tree $tree -p $parent) &&
parent=$commit || return 1
done &&
- echo "$commit" >.git/HEAD &&
+ git-update-ref HEAD "$commit" &&
echo Rebase &&
git-log'