@matheusbsbs / matheus-bagliano / commits / b44ebb19e3
Fork 0

Add platform-independent .git "symlink"

This patch allows .git to be a regular textfile containing the path of the real git directory (prefixed with "gitdir: "), which can be useful on platforms lacking support for real symlinks. Signed-off-by: Lars Hjemli <hjemli@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Lars Hjemli committed Feb 20, 2008 at 23:13 UTC b44ebb19e3234c5dffe9869ceac5408bb44c2e20
5 files changed +157 -1
Documentation/repository-layout.txt
+4 -1
@@ -3,7 +3,10 @@ git repository layout
3
4 You may find these things in your git repository (`.git`
5 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).
10
11 objects::
12 Object store associated with this repository. Usually
cache.h
+1
@@ -311,6 +311,7 @@ extern char *get_index_file(void);
311 extern char *get_graft_file(void);
312 extern int set_git_dir(const char *path);
313 extern const char *get_git_work_tree(void);
314 +extern const char *read_gitfile_gently(const char *path);
315
316 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
317
environment.c
+2
@@ -49,6 +49,8 @@ static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
49 static void setup_git_env(void)
50 {
51 git_dir = getenv(GIT_DIR_ENVIRONMENT);
52 + if (!git_dir)
53 + git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
54 if (!git_dir)
55 git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
56 git_object_dir = getenv(DB_ENVIRONMENT);
setup.c
+47
@@ -314,6 +314,44 @@ static int check_repository_format_gently(int *nongit_ok)
314 return 0;
315 }
316
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 +
355 /*
356 * We cannot decide in this function whether we are in the work tree or
357 * 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)
361 const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
362 static char cwd[PATH_MAX+1];
363 const char *gitdirenv;
364 + const char *gitfile_dir;
365 int len, offset;
366
367 /*
@@ -377,8 +416,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
416
417 /*
418 * Test in the following order (relative to the cwd):
419 + * - .git (file containing "gitdir: <path>")
420 * - .git/
421 * - ./ (bare)
422 + * - ../.git
423 * - ../.git/
424 * - ../ (bare)
425 * - ../../.git/
@@ -386,6 +427,12 @@ const char *setup_git_directory_gently(int *nongit_ok)
427 */
428 offset = len = strlen(cwd);
429 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 + }
436 if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
437 break;
438 if (is_git_directory(".")) {
t/t0002-gitfile.sh new
+103
@@ -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