Skip to content

Commit 8098a17

Browse files
Junio C HamanoJunio C Hamano
authored andcommitted
Add git-symbolic-ref
This adds the counterpart of git-update-ref that lets you read and create "symbolic refs". By default it uses a symbolic link to represent ".git/HEAD -> refs/heads/master", but it can be compiled to use the textfile symbolic ref. The places that did 'readlink .git/HEAD' and 'ln -s refs/heads/blah .git/HEAD' have been converted to use new git-symbolic-ref command, so that they can deal with either implementation. Signed-off-by: Junio C Hamano <[email protected]>
1 parent a876ed8 commit 8098a17

16 files changed

+176
-57
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ git-ssh-push
8282
git-ssh-upload
8383
git-status
8484
git-stripspace
85+
git-symbolic-ref
8586
git-tag
8687
git-tar-tree
8788
git-unpack-file

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ PROGRAMS = \
116116
git-ssh-upload git-tar-tree git-unpack-file \
117117
git-unpack-objects git-update-index git-update-server-info \
118118
git-upload-pack git-verify-pack git-write-tree \
119-
git-update-ref \
119+
git-update-ref git-symbolic-ref \
120120
$(SIMPLE_PROGRAMS)
121121

122122
# Backward compatibility -- to be removed after 1.0

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ extern int get_sha1_hex(const char *hex, unsigned char *sha1);
231231
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
232232
extern int read_ref(const char *filename, unsigned char *sha1);
233233
extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
234+
extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
235+
extern int validate_symref(const char *git_HEAD);
234236

235237
/* General helper functions */
236238
extern void usage(const char *err) NORETURN;

fsck-objects.c

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -402,25 +402,17 @@ static void fsck_object_dir(const char *path)
402402

403403
static int fsck_head_link(void)
404404
{
405-
int fd, count;
406-
char hex[40];
407405
unsigned char sha1[20];
408-
static char path[PATH_MAX], link[PATH_MAX];
409-
const char *git_dir = get_git_dir();
410-
411-
snprintf(path, sizeof(path), "%s/HEAD", git_dir);
412-
if (readlink(path, link, sizeof(link)) < 0)
413-
return error("HEAD is not a symlink");
414-
if (strncmp("refs/heads/", link, 11))
415-
return error("HEAD points to something strange (%s)", link);
416-
fd = open(path, O_RDONLY);
417-
if (fd < 0)
418-
return error("HEAD: %s", strerror(errno));
419-
count = read(fd, hex, sizeof(hex));
420-
close(fd);
421-
if (count < 0)
422-
return error("HEAD: %s", strerror(errno));
423-
if (count < 40 || get_sha1_hex(hex, sha1))
406+
const char *git_HEAD = strdup(git_path("HEAD"));
407+
const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
408+
int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
409+
410+
if (!git_refs_heads_master)
411+
return error("HEAD is not a symbolic ref");
412+
if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
413+
return error("HEAD points to something strange (%s)",
414+
git_refs_heads_master + pfxlen);
415+
if (!memcmp(null_sha1, sha1, 20))
424416
return error("HEAD: not a valid git pointer");
425417
return 0;
426418
}

git-bisect.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,16 @@ bisect_start() {
3838
# Verify HEAD. If we were bisecting before this, reset to the
3939
# top-of-line master first!
4040
#
41-
head=$(readlink $GIT_DIR/HEAD) || die "Bad HEAD - I need a symlink"
41+
head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
42+
die "Bad HEAD - I need a symbolic ref"
4243
case "$head" in
4344
refs/heads/bisect*)
4445
git checkout master || exit
4546
;;
4647
refs/heads/*)
4748
;;
4849
*)
49-
die "Bad HEAD - strange symlink"
50+
die "Bad HEAD - strange symbolic ref"
5051
;;
5152
esac
5253

@@ -135,7 +136,7 @@ bisect_next() {
135136
echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
136137
git checkout new-bisect || exit
137138
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
138-
ln -sf refs/heads/bisect "$GIT_DIR/HEAD"
139+
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
139140
git-show-branch "$rev"
140141
}
141142

git-branch.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ If two arguments, create a new branch <branchname> based off of <start-point>.
1414

1515
delete_branch () {
1616
option="$1" branch_name="$2"
17-
headref=$(readlink "$GIT_DIR/HEAD" | sed -e 's|^refs/heads/||')
17+
headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
18+
sed -e 's|^refs/heads/||')
1819
case ",$headref," in
1920
",$branch_name,")
2021
die "Cannot delete the branch you are on." ;;
@@ -67,7 +68,8 @@ done
6768

6869
case "$#" in
6970
0)
70-
headref=$(readlink "$GIT_DIR/HEAD" | sed -e 's|^refs/heads/||')
71+
headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
72+
sed -e 's|^refs/heads/||')
7173
git-rev-parse --symbolic --all |
7274
sed -ne 's|^refs/heads/||p' |
7375
sort |

git-checkout.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ if [ "$?" -eq 0 ]; then
7171
echo $new > "$GIT_DIR/refs/heads/$newbranch"
7272
branch="$newbranch"
7373
fi
74-
[ "$branch" ] && ln -sf "refs/heads/$branch" "$GIT_DIR/HEAD"
74+
[ "$branch" ] &&
75+
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
7576
rm -f "$GIT_DIR/MERGE_HEAD"
7677
else
7778
exit 1

git-commit.sh

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,8 @@ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
153153
fi >>.editmsg
154154

155155
PARENTS="-p HEAD"
156-
if [ ! -r "$GIT_DIR/HEAD" ]; then
157-
if [ -z "$(git-ls-files)" ]; then
158-
echo Nothing to commit 1>&2
159-
exit 1
160-
fi
161-
PARENTS=""
162-
current=
163-
else
164-
current=$(git-rev-parse --verify HEAD)
156+
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
157+
then
165158
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
166159
PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
167160
fi
@@ -194,6 +187,12 @@ else
194187
export GIT_AUTHOR_EMAIL
195188
export GIT_AUTHOR_DATE
196189
fi
190+
else
191+
if [ -z "$(git-ls-files)" ]; then
192+
echo Nothing to commit 1>&2
193+
exit 1
194+
fi
195+
PARENTS=""
197196
fi
198197
git-status >>.editmsg
199198
if [ "$?" != "0" -a ! -f $GIT_DIR/MERGE_HEAD ]

git-sh-setup.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
unset CDPATH
1414

1515
die() {
16-
echo "$@" >&2
16+
echo >&2 "$@"
1717
exit 1
1818
}
1919

20-
[ -h "$GIT_DIR/HEAD" ] &&
20+
case "$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD 2>/dev/null)" in
21+
refs/*) : ;;
22+
*) false ;;
23+
esac &&
2124
[ -d "$GIT_DIR/refs" ] &&
2225
[ -d "$GIT_OBJECT_DIRECTORY/00" ]

git-status.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ report () {
3131
[ "$header" ]
3232
}
3333

34-
branch=`readlink "$GIT_DIR/HEAD"`
34+
branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
3535
case "$branch" in
3636
refs/heads/master) ;;
3737
*) echo "# On branch $branch" ;;
3838
esac
3939

4040
git-update-index --refresh >/dev/null 2>&1
4141

42-
if test -f "$GIT_DIR/HEAD"
42+
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
4343
then
4444
git-diff-index -M --cached HEAD |
4545
sed 's/^://' |

init-db.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ static void create_default_files(const char *git_dir,
166166
{
167167
unsigned len = strlen(git_dir);
168168
static char path[PATH_MAX];
169+
unsigned char sha1[20];
169170

170171
if (len > sizeof(path)-50)
171172
die("insane git directory %s", git_dir);
@@ -186,15 +187,14 @@ static void create_default_files(const char *git_dir,
186187

187188
/*
188189
* Create the default symlink from ".git/HEAD" to the "master"
189-
* branch
190+
* branch, if it does not exist yet.
190191
*/
191192
strcpy(path + len, "HEAD");
192-
if (symlink("refs/heads/master", path) < 0) {
193-
if (errno != EEXIST) {
194-
perror(path);
193+
if (read_ref(path, sha1) < 0) {
194+
if (create_symref(path, "refs/heads/master") < 0)
195195
exit(1);
196-
}
197196
}
197+
path[len] = 0;
198198
copy_templates(path, len, template_path);
199199
}
200200

refs.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@
77
/* We allow "recursive" symbolic refs. Only within reason, though */
88
#define MAXDEPTH 5
99

10+
#ifndef USE_SYMLINK_HEAD
11+
#define USE_SYMLINK_HEAD 1
12+
#endif
13+
14+
int validate_symref(const char *path)
15+
{
16+
struct stat st;
17+
char *buf, buffer[256];
18+
int len, fd;
19+
20+
if (lstat(path, &st) < 0)
21+
return -1;
22+
23+
/* Make sure it is a "refs/.." symlink */
24+
if (S_ISLNK(st.st_mode)) {
25+
len = readlink(path, buffer, sizeof(buffer)-1);
26+
if (len >= 5 && !memcmp("refs/", buffer, 5))
27+
return 0;
28+
return -1;
29+
}
30+
31+
/*
32+
* Anything else, just open it and try to see if it is a symbolic ref.
33+
*/
34+
fd = open(path, O_RDONLY);
35+
if (fd < 0)
36+
return -1;
37+
len = read(fd, buffer, sizeof(buffer)-1);
38+
close(fd);
39+
40+
/*
41+
* Is it a symbolic ref?
42+
*/
43+
if (len < 4 || memcmp("ref:", buffer, 4))
44+
return -1;
45+
buf = buffer + 4;
46+
len -= 4;
47+
while (len && isspace(*buf))
48+
buf++, len--;
49+
if (len >= 5 && !memcmp("refs/", buffer, 5))
50+
return 0;
51+
return -1;
52+
}
53+
1054
const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
1155
{
1256
int depth = MAXDEPTH, len;
@@ -71,6 +115,39 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
71115
return path;
72116
}
73117

118+
int create_symref(const char *git_HEAD, const char *refs_heads_master)
119+
{
120+
#if USE_SYMLINK_HEAD
121+
unlink(git_HEAD);
122+
return symlink(refs_heads_master, git_HEAD);
123+
#else
124+
const char *lockpath;
125+
char ref[1000];
126+
int fd, len, written;
127+
128+
len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
129+
if (sizeof(ref) <= len) {
130+
error("refname too long: %s", refs_heads_master);
131+
return -1;
132+
}
133+
lockpath = mkpath("%s.lock", git_HEAD);
134+
fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
135+
written = write(fd, ref, len);
136+
close(fd);
137+
if (written != len) {
138+
unlink(lockpath);
139+
error("Unable to write to %s", lockpath);
140+
return -2;
141+
}
142+
if (rename(lockpath, git_HEAD) < 0) {
143+
unlink(lockpath);
144+
error("Unable to create %s", git_HEAD);
145+
return -3;
146+
}
147+
return 0;
148+
#endif
149+
}
150+
74151
int read_ref(const char *filename, unsigned char *sha1)
75152
{
76153
if (resolve_ref(filename, sha1, 1))

setup.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,20 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
7676
* Test it it looks like we're at the top
7777
* level git directory. We want to see a
7878
*
79-
* - a HEAD symlink and a refs/ directory under ".git"
8079
* - either a .git/objects/ directory _or_ the proper
8180
* GIT_OBJECT_DIRECTORY environment variable
81+
* - a refs/ directory under ".git"
82+
* - either a HEAD symlink or a HEAD file that is formatted as
83+
* a proper "ref:".
8284
*/
8385
static int is_toplevel_directory(void)
8486
{
85-
struct stat st;
86-
87-
return !lstat(".git/HEAD", &st) &&
88-
S_ISLNK(st.st_mode) &&
89-
!access(".git/refs/", X_OK) &&
90-
(getenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK));
87+
if (access(".git/refs/", X_OK) ||
88+
access(getenv(DB_ENVIRONMENT) ?
89+
getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
90+
validate_symref(".git/HEAD"))
91+
return 0;
92+
return 1;
9193
}
9294

9395
const char *setup_git_directory(void)

show-branch.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ int main(int ac, char **av)
349349
int all_heads = 0, all_tags = 0;
350350
int all_mask, all_revs, shown_merge_point;
351351
char head_path[128];
352+
const char *head_path_p;
352353
int head_path_len;
353354
unsigned char head_sha1[20];
354355
int merge_base = 0;
@@ -430,11 +431,15 @@ int main(int ac, char **av)
430431
if (0 <= extra)
431432
join_revs(&list, &seen, num_rev, extra);
432433

433-
head_path_len = readlink(".git/HEAD", head_path, sizeof(head_path)-1);
434-
if ((head_path_len < 0) || get_sha1("HEAD", head_sha1))
434+
head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
435+
if (head_path_p) {
436+
head_path_len = strlen(head_path_p);
437+
memcpy(head_path, head_path_p, head_path_len + 1);
438+
}
439+
else {
440+
head_path_len = 0;
435441
head_path[0] = 0;
436-
else
437-
head_path[head_path_len] = 0;
442+
}
438443

439444
if (merge_base)
440445
return show_merge_base(seen, num_rev);

symbolic-ref.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "cache.h"
2+
3+
static const char git_symbolic_ref_usage[] =
4+
"git-symbolic-ref name [ref]";
5+
6+
static int check_symref(const char *HEAD)
7+
{
8+
unsigned char sha1[20];
9+
const char *git_HEAD = strdup(git_path("%s", HEAD));
10+
const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
11+
if (git_refs_heads_master) {
12+
/* we want to strip the .git/ part */
13+
int pfxlen = strlen(git_HEAD) - strlen(HEAD);
14+
puts(git_refs_heads_master + pfxlen);
15+
}
16+
else
17+
die("No such ref: %s", HEAD);
18+
}
19+
20+
int main(int argc, const char **argv)
21+
{
22+
setup_git_directory();
23+
switch (argc) {
24+
case 2:
25+
check_symref(argv[1]);
26+
break;
27+
case 3:
28+
create_symref(strdup(git_path("%s", argv[1])), argv[2]);
29+
break;
30+
default:
31+
usage(git_symbolic_ref_usage);
32+
}
33+
return 0;
34+
}

0 commit comments

Comments
 (0)