Skip to content

Commit e2e2def

Browse files
committed
Merge branch 'lh/git-file'
* lh/git-file: Teach GIT-VERSION-GEN about the .git file Teach git-submodule.sh about the .git file Teach resolve_gitlink_ref() about the .git file Add platform-independent .git "symlink"
2 parents 9c36e17 + c48799e commit e2e2def

File tree

8 files changed

+174
-7
lines changed

8 files changed

+174
-7
lines changed

Documentation/repository-layout.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ git repository layout
33

44
You may find these things in your git repository (`.git`
55
directory for a repository associated with your working tree, or
6-
`'project'.git` directory for a public 'bare' repository).
6+
`'project'.git` directory for a public 'bare' repository. It is
7+
also possible to have a working tree where `.git` is a plain
8+
ascii file containing `gitdir: <path>`, i.e. the path to the
9+
real git repository).
710

811
objects::
912
Object store associated with this repository. Usually

GIT-VERSION-GEN

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LF='
1111
if test -f version
1212
then
1313
VN=$(cat version) || VN="$DEF_VER"
14-
elif test -d .git &&
14+
elif test -d .git -o -f .git &&
1515
VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
1616
case "$VN" in
1717
*$LF*) (exit 1) ;;

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ extern char *get_index_file(void);
311311
extern char *get_graft_file(void);
312312
extern int set_git_dir(const char *path);
313313
extern const char *get_git_work_tree(void);
314+
extern const char *read_gitfile_gently(const char *path);
314315

315316
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
316317

environment.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
4949
static void setup_git_env(void)
5050
{
5151
git_dir = getenv(GIT_DIR_ENVIRONMENT);
52+
if (!git_dir)
53+
git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
5254
if (!git_dir)
5355
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
5456
git_object_dir = getenv(DB_ENVIRONMENT);

git-submodule.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ cmd_update()
300300
continue
301301
fi
302302

303-
if ! test -d "$path"/.git
303+
if ! test -d "$path"/.git -o -f "$path"/.git
304304
then
305305
module_clone "$path" "$url" || exit
306306
subsha1=
@@ -555,7 +555,7 @@ cmd_status()
555555
do
556556
name=$(module_name "$path") || exit
557557
url=$(git config submodule."$name".url)
558-
if test -z "$url" || ! test -d "$path"/.git
558+
if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
559559
then
560560
say "-$sha1 $path"
561561
continue;

refs.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,16 +352,27 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
352352
{
353353
int len = strlen(path), retval;
354354
char *gitdir;
355+
const char *tmp;
355356

356357
while (len && path[len-1] == '/')
357358
len--;
358359
if (!len)
359360
return -1;
360361
gitdir = xmalloc(len + MAXREFLEN + 8);
361362
memcpy(gitdir, path, len);
362-
memcpy(gitdir + len, "/.git/", 7);
363-
364-
retval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);
363+
memcpy(gitdir + len, "/.git", 6);
364+
len += 5;
365+
366+
tmp = read_gitfile_gently(gitdir);
367+
if (tmp) {
368+
free(gitdir);
369+
len = strlen(tmp);
370+
gitdir = xmalloc(len + MAXREFLEN + 3);
371+
memcpy(gitdir, tmp, len);
372+
}
373+
gitdir[len] = '/';
374+
gitdir[++len] = '\0';
375+
retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
365376
free(gitdir);
366377
return retval;
367378
}

setup.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,44 @@ static int check_repository_format_gently(int *nongit_ok)
314314
return 0;
315315
}
316316

317+
/*
318+
* Try to read the location of the git directory from the .git file,
319+
* return path to git directory if found.
320+
*/
321+
const char *read_gitfile_gently(const char *path)
322+
{
323+
char *buf;
324+
struct stat st;
325+
int fd;
326+
size_t len;
327+
328+
if (stat(path, &st))
329+
return NULL;
330+
if (!S_ISREG(st.st_mode))
331+
return NULL;
332+
fd = open(path, O_RDONLY);
333+
if (fd < 0)
334+
die("Error opening %s: %s", path, strerror(errno));
335+
buf = xmalloc(st.st_size + 1);
336+
len = read_in_full(fd, buf, st.st_size);
337+
close(fd);
338+
if (len != st.st_size)
339+
die("Error reading %s", path);
340+
buf[len] = '\0';
341+
if (prefixcmp(buf, "gitdir: "))
342+
die("Invalid gitfile format: %s", path);
343+
while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
344+
len--;
345+
if (len < 9)
346+
die("No path in gitfile: %s", path);
347+
buf[len] = '\0';
348+
if (!is_git_directory(buf + 8))
349+
die("Not a git repository: %s", buf + 8);
350+
path = make_absolute_path(buf + 8);
351+
free(buf);
352+
return path;
353+
}
354+
317355
/*
318356
* We cannot decide in this function whether we are in the work tree or
319357
* not, since the config can only be read _after_ this function was called.
@@ -323,6 +361,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
323361
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
324362
static char cwd[PATH_MAX+1];
325363
const char *gitdirenv;
364+
const char *gitfile_dir;
326365
int len, offset;
327366

328367
/*
@@ -377,15 +416,23 @@ const char *setup_git_directory_gently(int *nongit_ok)
377416

378417
/*
379418
* Test in the following order (relative to the cwd):
419+
* - .git (file containing "gitdir: <path>")
380420
* - .git/
381421
* - ./ (bare)
422+
* - ../.git
382423
* - ../.git/
383424
* - ../ (bare)
384425
* - ../../.git/
385426
* etc.
386427
*/
387428
offset = len = strlen(cwd);
388429
for (;;) {
430+
gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
431+
if (gitfile_dir) {
432+
if (set_git_dir(gitfile_dir))
433+
die("Repository setup failed");
434+
break;
435+
}
389436
if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
390437
break;
391438
if (is_git_directory(".")) {

t/t0002-gitfile.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/bin/sh
2+
3+
test_description='.git file
4+
5+
Verify that plumbing commands work when .git is a file
6+
'
7+
. ./test-lib.sh
8+
9+
objpath() {
10+
echo "$1" | sed -e 's|\(..\)|\1/|'
11+
}
12+
13+
objck() {
14+
p=$(objpath "$1")
15+
if test ! -f "$REAL/objects/$p"
16+
then
17+
echo "Object not found: $REAL/objects/$p"
18+
false
19+
fi
20+
}
21+
22+
23+
test_expect_success 'initial setup' '
24+
REAL="$(pwd)/.real" &&
25+
mv .git "$REAL"
26+
'
27+
28+
test_expect_success 'bad setup: invalid .git file format' '
29+
echo "gitdir $REAL" >.git &&
30+
if git rev-parse 2>.err
31+
then
32+
echo "git rev-parse accepted an invalid .git file"
33+
false
34+
fi &&
35+
if ! grep -qe "Invalid gitfile format" .err
36+
then
37+
echo "git rev-parse returned wrong error"
38+
false
39+
fi
40+
'
41+
42+
test_expect_success 'bad setup: invalid .git file path' '
43+
echo "gitdir: $REAL.not" >.git &&
44+
if git rev-parse 2>.err
45+
then
46+
echo "git rev-parse accepted an invalid .git file path"
47+
false
48+
fi &&
49+
if ! grep -qe "Not a git repository" .err
50+
then
51+
echo "git rev-parse returned wrong error"
52+
false
53+
fi
54+
'
55+
56+
test_expect_success 'final setup + check rev-parse --git-dir' '
57+
echo "gitdir: $REAL" >.git &&
58+
test "$REAL" = "$(git rev-parse --git-dir)"
59+
'
60+
61+
test_expect_success 'check hash-object' '
62+
echo "foo" >bar &&
63+
SHA=$(cat bar | git hash-object -w --stdin) &&
64+
objck $SHA
65+
'
66+
67+
test_expect_success 'check cat-file' '
68+
git cat-file blob $SHA >actual &&
69+
diff -u bar actual
70+
'
71+
72+
test_expect_success 'check update-index' '
73+
if test -f "$REAL/index"
74+
then
75+
echo "Hmm, $REAL/index exists?"
76+
false
77+
fi &&
78+
rm -f "$REAL/objects/$(objpath $SHA)" &&
79+
git update-index --add bar &&
80+
if ! test -f "$REAL/index"
81+
then
82+
echo "$REAL/index not found"
83+
false
84+
fi &&
85+
objck $SHA
86+
'
87+
88+
test_expect_success 'check write-tree' '
89+
SHA=$(git write-tree) &&
90+
objck $SHA
91+
'
92+
93+
test_expect_success 'check commit-tree' '
94+
SHA=$(echo "commit bar" | git commit-tree $SHA) &&
95+
objck $SHA
96+
'
97+
98+
test_expect_success 'check rev-list' '
99+
echo $SHA >"$REAL/HEAD" &&
100+
test "$SHA" = "$(git rev-list HEAD)"
101+
'
102+
103+
test_done

0 commit comments

Comments
 (0)