[RFC] introduce GIT_WORK_DIR environment variable

Subsystems: the rest

21 messages, 4 authors, 2016-06-15 · open the first message on its own page

[RFC] introduce GIT_WORK_DIR environment variable

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

This environment variable can be used with GIT_DIR to
specify the toplevel working directory.  When GIT_DIR is not
set this variable is ignored.  As for GIT_DIR there is also
the option git --work-dir which overrides the environment
variable.
---
Missing:
Documentation update but if this feature should not get accepted..
therefore I'll wait for feedback first.

Idea:
Add some way to configure tho working directory for one repository
and set GIT_WORK_DIR automatically when GIT_DIR is used.  I think of:
 * a subdirectory in the repository directory
   e.g. .git/work_dir which is supposed to be a symlink (or a textfile
   containing the path for windows compatibility?)
or
 * a configuration variable

Considerations:
Without running setup_git_directory_gently is_bare_repository and
is_inside_git_dir may return wrong values.  Except for cmd_init_db I
found no place calling on of the functions without calling
setup_git_directory_gently before.

To do:
git init should probably set bare = false when GIT_WORK_DIR is
exported.  And if the idea about configurable working directories gets
implemented it could also set this option accordingly.

---
 cache.h       |    2 +
 environment.c |    2 +
 git.c         |   12 +++++++++-
 setup.c       |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 80 insertions(+), 6 deletions(-)
diff --git a/cache.h b/cache.h
index ae25759..042734c 100644
--- a/cache.h
+++ b/cache.h
@@ -144,6 +144,7 @@ enum object_type {
 };
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_WORKING_DIR_ENVIRONMENT "GIT_WORK_DIR"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -164,6 +165,7 @@ extern char *get_graft_file(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
+extern int has_working_directory;
 extern const char **get_pathspec(const char *prefix, const char **pathspec);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
diff --git a/environment.c b/environment.c
index 0151ad0..5c30c9b 100644
--- a/environment.c
+++ b/environment.c
@@ -61,6 +61,8 @@ int is_bare_repository(void)
 	const char *dir, *s;
 	if (0 <= is_bare_repository_cfg)
 		return is_bare_repository_cfg;
+	if (0 <= has_working_directory)
+		return !has_working_directory;
 
 	dir = get_git_dir();
 	if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
diff --git a/git.c b/git.c
index 04fc99a..3cf7ce2 100644
--- a/git.c
+++ b/git.c
@@ -4,7 +4,7 @@
 #include "quote.h"
 
 const char git_usage_string[] =
-	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
+	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--work-dir=GIT_WORK_DIR] [--help] COMMAND [ARGS]";
 
 static void prepend_to_path(const char *dir, int len)
 {
@@ -68,6 +68,16 @@ static int handle_options(const char*** argv, int* argc)
 			(*argc)--;
 		} else if (!prefixcmp(cmd, "--git-dir=")) {
 			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+		} else if (!strcmp(cmd, "--work-dir")) {
+			if (*argc < 2) {
+				fprintf(stderr, "No directory given for --work-dir.\n" );
+				usage(git_usage_string);
+			}
+			setenv(GIT_WORKING_DIR_ENVIRONMENT, (*argv)[1], 1);
+			(*argv)++;
+			(*argc)--;
+		} else if (!prefixcmp(cmd, "--work-dir=")) {
+			setenv(GIT_WORKING_DIR_ENVIRONMENT, cmd + 11, 1);
 		} else if (!strcmp(cmd, "--bare")) {
 			static char git_dir[PATH_MAX+1];
 			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 1);
diff --git a/setup.c b/setup.c
index dda67d2..7f5d73b 100644
--- a/setup.c
+++ b/setup.c
@@ -192,6 +192,8 @@ int is_inside_git_dir(void)
 	return inside_git_dir;
 }
 
+int has_working_directory = -1;
+
 const char *setup_git_directory_gently(int *nongit_ok)
 {
 	static char cwd[PATH_MAX+1];
@@ -205,15 +207,73 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	 */
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 	if (gitdirenv) {
+		struct stat st, st_work, st_git;
+		const char *gitwd;
+		char *prefix;
+		char c;
+		int len;
+
 		if (PATH_MAX - 40 < strlen(gitdirenv))
 			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-		if (is_git_directory(gitdirenv))
-			return NULL;
-		if (nongit_ok) {
-			*nongit_ok = 1;
+		if (!is_git_directory(gitdirenv)) {
+			if (nongit_ok) {
+				*nongit_ok = 1;
+				return NULL;
+			}
+			die("Not a git repository: '%s'", gitdirenv);
+		}
+
+		gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+		if (!gitwd || stat(gitwd, &st_work))
 			return NULL;
+		if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
+			die("Unable to stat git directory");
+		if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		len = strlen(cwd);
+
+		prefix = cwd+len;
+		for (;;) {
+			c = *prefix;
+			*prefix = '\0';
+			if (stat(cwd, &st))
+				die("Unable to stat '%s'", cwd);
+			if (st_work.st_dev == st.st_dev &&
+			    st_work.st_ino == st.st_ino)
+				break;
+			if (inside_git_dir == -1 &&
+			    st_git.st_dev == st.st_dev &&
+			    st_git.st_ino == st.st_ino)
+				inside_git_dir = 1;
+			*prefix = c;
+
+			if (prefix == cwd+1) {
+				has_working_directory = 0;
+				return NULL;
+			}
+			while (*(--prefix) != '/')
+				; /* do nothing */
+			if (prefix == cwd)
+				prefix++;
+		}
+
+		if (chdir(cwd))
+			die("Cannot change directory to '%s'", cwd);
+
+		if (c) {
+			*prefix = c;
+			prefix++;
+			cwd[len] = '/';
+			cwd[len+1] = '\0';
+		} else {
+			prefix = NULL;
 		}
-		die("Not a git repository: '%s'", gitdirenv);
+
+		has_working_directory = 1;
+		if (inside_git_dir == -1)
+			inside_git_dir = 0;
+
+		return prefix;
 	}
 
 	if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
-- 
1.5.0.3

Re: [RFC] introduce GIT_WORK_DIR environment variable

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:42:59

On 3/11/07, Matthias Lederhofer [off-list ref] wrote:
+               gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+               if (!gitwd || stat(gitwd, &st_work))
                        return NULL;
I propose the following instead of the last two lines:

                if (!gitwd)
                        return NULL;
                if (stat(gitwd, &st_work))
                        die("Unable to stat git working directory %s",gitwd);
+               if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
+                       die("Unable to stat git directory");
+               if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+                       die("Unable to read current working directory");
+               len = strlen(cwd);
+
+               prefix = cwd+len;
+               for (;;) {
+                       c = *prefix;
+                       *prefix = '\0';
+                       if (stat(cwd, &st))
+                               die("Unable to stat '%s'", cwd);
+                       if (st_work.st_dev == st.st_dev &&
+                           st_work.st_ino == st.st_ino)
+                               break;
+                       if (inside_git_dir == -1 &&
+                           st_git.st_dev == st.st_dev &&
+                           st_git.st_ino == st.st_ino)
+                               inside_git_dir = 1;
+                       *prefix = c;
+
+                       if (prefix == cwd+1) {
+                               has_working_directory = 0;
+                               return NULL;
My case seems a bit complicated than usual. The working directory
(/home/pclouds/blog/data) was not a prefix of cwd (/home/pclouds/blog)
so the code failed silently at this line. If I replace
"has_working_directory = 0; return NULL;" with "strcpy(cwd,gitwd);c =
0;break;", it may work but see below
+                       }
+                       while (*(--prefix) != '/')
+                               ; /* do nothing */
+                       if (prefix == cwd)
+                               prefix++;
+               }
+
+               if (chdir(cwd))
+                       die("Cannot change directory to '%s'", cwd);
+
If cwd changed and GIT_DIR is a relative path, git can no longer
access GIT_DIR properly.
-- 
Duy

Re: [RFC] introduce GIT_WORK_DIR environment variable

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:42:59

On 3/11/07, Matthias Lederhofer [off-list ref] wrote:
quoted hunk
diff --git a/environment.c b/environment.c
index 0151ad0..5c30c9b 100644
--- a/environment.c
+++ b/environment.c
@@ -61,6 +61,8 @@ int is_bare_repository(void)
        const char *dir, *s;
        if (0 <= is_bare_repository_cfg)
                return is_bare_repository_cfg;
+       if (0 <= has_working_directory)
+               return !has_working_directory;

        dir = get_git_dir();
        if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
Um.. git-sh-setup.sh may need special treatment because its
is_bare_directory doesn't call this function.
-- 
Duy

Re: [RFC] introduce GIT_WORK_DIR environment variable

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Nguyen Thai Ngoc Duy [off-list ref] wrote:
My case seems a bit complicated than usual. The working directory
(/home/pclouds/blog/data) was not a prefix of cwd (/home/pclouds/blog)
so the code failed silently at this line. If I replace
"has_working_directory = 0; return NULL;" with "strcpy(cwd,gitwd);c =
0;break;", it may work but see below
If you're outside the specifed working directory the
is_bare_repository will return true, just as if you don't have a
working directory.  Do you expect anything else or doesn't this work?

Re: [RFC] introduce GIT_WORK_DIR environment variable

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:42:59

On 3/11/07, Matthias Lederhofer [off-list ref] wrote:
Nguyen Thai Ngoc Duy [off-list ref] wrote:
quoted
My case seems a bit complicated than usual. The working directory
(/home/pclouds/blog/data) was not a prefix of cwd (/home/pclouds/blog)
so the code failed silently at this line. If I replace
"has_working_directory = 0; return NULL;" with "strcpy(cwd,gitwd);c =
0;break;", it may work but see below
If you're outside the specifed working directory the
is_bare_repository will return true, just as if you don't have a
working directory.  Do you expect anything else or doesn't this work?
Yes I expected it to move the specified working directory as described
in the previous mail (copied/pasted below). However the patch requires
me to be in workdir somewhere already (which is fine if that is your
expectation). If that's the case, maybe you should tell users
something about GIT_WORK_DIR not applicable.
Nguyen Thai Ngoc Duy [off-list ref] wrote:
quoted
By the way, is it plausible to add --git-workdir option to specify
working directory? With that option, I won't need to _chdir_ to the
working directory, run git commands and _chdir back_.
http://article.gmane.org/gmane.comp.version-control.git/38382
Since I did not need this feature that much and no one replied that
there is any interest I did not look any further into it.
-- 
Duy

Re: [RFC] introduce GIT_WORK_DIR environment variable

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Nguyen Thai Ngoc Duy [off-list ref] wrote:
Yes I expected it to move the specified working directory as described
in the previous mail (copied/pasted below). However the patch requires
me to be in workdir somewhere already (which is fine if that is your
expectation). If that's the case, maybe you should tell users
something about GIT_WORK_DIR not applicable.
Let's say we have GIT_DIR=/tmp/git GIT_WORK_DIR=/tmp/foo and are in
/tmp.  You want `git add bar` to chdir to /tmp/foo and add the file
bar from /tmp/foo?
quoted
Nguyen Thai Ngoc Duy [off-list ref] wrote:
quoted
By the way, is it plausible to add --git-workdir option to specify
working directory? With that option, I won't need to _chdir_ to the
working directory, run git commands and _chdir back_.
I thought you meant the chdir to the toplevel directory when you're in
a subdirectory.  For this case I'd rather suggest
mygit() {
    old=$(pwd)
    cd "$MYTOPDIR"
    git "$@"
    ret=$?
    cd "$old"
    return $ret
}

Re: [RFC] introduce GIT_WORK_DIR environment variable

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:42:59

On 3/11/07, Matthias Lederhofer [off-list ref] wrote:
Nguyen Thai Ngoc Duy [off-list ref] wrote:
quoted
Yes I expected it to move the specified working directory as described
in the previous mail (copied/pasted below). However the patch requires
me to be in workdir somewhere already (which is fine if that is your
expectation). If that's the case, maybe you should tell users
something about GIT_WORK_DIR not applicable.
Let's say we have GIT_DIR=/tmp/git GIT_WORK_DIR=/tmp/foo and are in
/tmp.  You want `git add bar` to chdir to /tmp/foo and add the file
bar from /tmp/foo?
Yes. I wanted it for easy scripting but it's not very intuitive from
command line. I'd take it back.
quoted
quoted
Nguyen Thai Ngoc Duy [off-list ref] wrote:
quoted
By the way, is it plausible to add --git-workdir option to specify
working directory? With that option, I won't need to _chdir_ to the
working directory, run git commands and _chdir back_.
I thought you meant the chdir to the toplevel directory when you're in
a subdirectory.  For this case I'd rather suggest
mygit() {
    old=$(pwd)
    cd "$MYTOPDIR"
    git "$@"
    ret=$?
    cd "$old"
    return $ret
}
Thanks, will try

-- 
Duy

[PATCH] rev-parse: --is-bare-repository option

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Signed-off-by: Matthias Lederhofer <redacted>
---
Nguyen Thai Ngoc Duy [off-list ref] wrote:
Um.. git-sh-setup.sh may need special treatment because its
is_bare_directory doesn't call this function.
Here is rev-parse --is-bare-repository to fix this.

I'm not sure if git-sh-setup.sh:is_bare_repository should do other
checks if git-rev-parse --is-bare-repository fails.
---
 Documentation/git-rev-parse.txt |    7 +++++++
 builtin-rev-parse.c             |    5 +++++
 git-sh-setup.sh                 |    6 +-----
 git-svn.perl                    |    2 +-
 4 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index ccc66aa..d024d93 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -89,6 +89,13 @@ OPTIONS
 --git-dir::
 	Show `$GIT_DIR` if defined else show the path to the .git directory.
 
+--is-inside-git-dir::
+	When the current working directory is below the repository
+	directory print "true", otherwise "false".
+
+--is-bare-repository::
+	When the repository is bare print "true", otherwise "false".
+
 --short, --short=number::
 	Instead of outputting the full SHA1 values of object names try to
 	abbreviate them to a shorter unique name. When no length is specified
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 37addb2..71d5162 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -352,6 +352,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 						: "false");
 				continue;
 			}
+			if (!strcmp(arg, "--is-bare-repository")) {
+				printf("%s\n", is_bare_repository() ? "true"
+						: "false");
+				continue;
+			}
 			if (!prefixcmp(arg, "--since=")) {
 				show_datestring("--max-age=", arg+8);
 				continue;
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index f24c7f2..9ac657a 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -29,11 +29,7 @@ set_reflog_action() {
 }
 
 is_bare_repository () {
-	git-config --bool --get core.bare ||
-	case "$GIT_DIR" in
-	.git | */.git) echo false ;;
-	*) echo true ;;
-	esac
+	git-rev-parse --is-bare-repository
 }
 
 cd_to_toplevel () {
diff --git a/git-svn.perl b/git-svn.perl
index 326e89f..ea5da95 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -559,7 +559,7 @@ sub post_fetch_checkout {
 	my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
 	return if -f $index;
 
-	chomp(my $bare = `git config --bool --get core.bare`);
+	chomp(my $bare = `git rev-parse --is-bare-repository`);
 	return if $bare eq 'true';
 	return if command_oneline(qw/rev-parse --is-inside-git-dir/) eq 'true';
 	command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
-- 
1.5.0.3.355.g8488f-dirty

[PATCH(amend)] introduce GIT_WORK_DIR environment variable

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

This environment variable can be used with GIT_DIR to
specify the toplevel working directory.  When GIT_DIR is not
set this variable is ignored.  As for GIT_DIR there is also
the option git --work-dir which overrides the environment
variable.

Signed-off-by: Matthias Lederhofer <redacted>
---
New patch, this should go with the 'rev-parse --is-bare-repository
option' patch.

Documentation added, is_bare_repository check fixed, relative GIT_DIR
fix.  Becaus GIT_DIR is expanded to a full path setup_git_env should
be called after setup_git_directory_gently.  I searched for the
callers of setup_git_directory_gently and think this is fulfilled.

Nguyen Thai Ngoc Duy [off-list ref] wrote:
I propose the following instead of the last two lines:

               if (!gitwd)
                       return NULL;
               if (stat(gitwd, &st_work))
                       die("Unable to stat git working directory %s",gitwd);
It's in this new patch.
If cwd changed and GIT_DIR is a relative path, git can no longer
access GIT_DIR properly.
This too.
---
 Documentation/git.txt |   16 ++++++++-
 cache.h               |    2 +
 environment.c         |   11 +++++-
 git.c                 |   12 ++++++-
 setup.c               |   92 +++++++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 119 insertions(+), 14 deletions(-)
diff --git a/Documentation/git.txt b/Documentation/git.txt
index e875e83..d2f5d27 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -10,7 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
-    [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
+    [--bare] [--git-dir=GIT_DIR] [--work-dir=GIT_WORK_DIR]
+    [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
@@ -81,6 +82,13 @@ OPTIONS
 	Set the path to the repository. This can also be controlled by
 	setting the GIT_DIR environment variable.
 
+--work-dir=<path>::
+	Set the path to the toplevel working directory.  The value will
+	be used only in combination with $GIT_DIR or '--git-dir'.
+	Without this option git will assume that the current directory
+	is also the toplevel directory.  This can also be controlled by
+	setting the GIT_WORK_DIR environment variable.
+
 --bare::
 	Same as --git-dir=`pwd`.
 
@@ -325,6 +333,12 @@ git so take care if using Cogito etc.
 	specifies a path to use instead of the default `.git`
 	for the base of the repository.
 
+'GIT_WORK_DIR'::
+	Set the path to the toplevel working directory.  The value will
+	be used only in combination with $GIT_DIR or '--git-dir'.
+	Without this environment variable git will assume that the
+	current directory is also the toplevel directory.
+
 git Commits
 ~~~~~~~~~~~
 'GIT_AUTHOR_NAME'::
diff --git a/cache.h b/cache.h
index f172d02..e3ca087 100644
--- a/cache.h
+++ b/cache.h
@@ -144,6 +144,7 @@ enum object_type {
 };
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_WORKING_DIR_ENVIRONMENT "GIT_WORK_DIR"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -164,6 +165,7 @@ extern char *get_graft_file(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
+extern int has_working_directory;
 extern const char **get_pathspec(const char *prefix, const char **pathspec);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
diff --git a/environment.c b/environment.c
index 0151ad0..7bf6a87 100644
--- a/environment.c
+++ b/environment.c
@@ -59,8 +59,15 @@ static void setup_git_env(void)
 int is_bare_repository(void)
 {
 	const char *dir, *s;
-	if (0 <= is_bare_repository_cfg)
-		return is_bare_repository_cfg;
+	/* definitely bare */
+	if (is_bare_repository_cfg == 1)
+		return 1;
+	/* GIT_WORK_DIR is set, bare if cwd is outside */
+	if (has_working_directory >= 0)
+		return !has_working_directory;
+	/* configuration says it is not bare */
+	if (is_bare_repository_cfg == 0)
+		return 0;
 
 	dir = get_git_dir();
 	if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
diff --git a/git.c b/git.c
index fe2b74a..8a73648 100644
--- a/git.c
+++ b/git.c
@@ -4,7 +4,7 @@
 #include "quote.h"
 
 const char git_usage_string[] =
-	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
+	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--work-dir=GIT_WORK_DIR] [--help] COMMAND [ARGS]";
 
 static void prepend_to_path(const char *dir, int len)
 {
@@ -68,6 +68,16 @@ static int handle_options(const char*** argv, int* argc)
 			(*argc)--;
 		} else if (!prefixcmp(cmd, "--git-dir=")) {
 			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+		} else if (!strcmp(cmd, "--work-dir")) {
+			if (*argc < 2) {
+				fprintf(stderr, "No directory given for --work-dir.\n" );
+				usage(git_usage_string);
+			}
+			setenv(GIT_WORKING_DIR_ENVIRONMENT, (*argv)[1], 1);
+			(*argv)++;
+			(*argc)--;
+		} else if (!prefixcmp(cmd, "--work-dir=")) {
+			setenv(GIT_WORKING_DIR_ENVIRONMENT, cmd + 11, 1);
 		} else if (!strcmp(cmd, "--bare")) {
 			static char git_dir[PATH_MAX+1];
 			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 1);
diff --git a/setup.c b/setup.c
index a45ea83..ebf628e 100644
--- a/setup.c
+++ b/setup.c
@@ -192,28 +192,100 @@ int is_inside_git_dir(void)
 	return inside_git_dir;
 }
 
+int has_working_directory = -1;
+
 const char *setup_git_directory_gently(int *nongit_ok)
 {
 	static char cwd[PATH_MAX+1];
 	const char *gitdirenv;
 	int len, offset;
 
-	/*
-	 * If GIT_DIR is set explicitly, we're not going
-	 * to do any discovery, but we still do repository
-	 * validation.
-	 */
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 	if (gitdirenv) {
+		struct stat st, st_work, st_git;
+		const char *gitwd;
+		char *prefix;
+		char c;
+		int len;
+
 		if (PATH_MAX - 40 < strlen(gitdirenv))
 			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-		if (is_git_directory(gitdirenv))
-			return NULL;
-		if (nongit_ok) {
-			*nongit_ok = 1;
+		if (!is_git_directory(gitdirenv)) {
+			if (nongit_ok) {
+				*nongit_ok = 1;
+				return NULL;
+			}
+			die("Not a git repository: '%s'", gitdirenv);
+		}
+
+		/* check for working directory */
+		gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+		if (!gitwd)
 			return NULL;
+		if (stat(gitwd, &st_work))
+			die("Unable to stat git working directory '%s'", gitwd);
+		if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
+			die("Unable to stat git directory");
+		if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		len = strlen(cwd);
+
+		prefix = cwd+len;
+		for (;;) {
+			c = *prefix;
+			*prefix = '\0';
+			if (stat(cwd, &st))
+				die("Unable to stat '%s'", cwd);
+			if (st_work.st_dev == st.st_dev &&
+			    st_work.st_ino == st.st_ino)
+				break;
+			if (inside_git_dir == -1 &&
+			    st_git.st_dev == st.st_dev &&
+			    st_git.st_ino == st.st_ino)
+				inside_git_dir = 1;
+			*prefix = c;
+
+			if (prefix == cwd+1) {
+				has_working_directory = 0;
+				return NULL;
+			}
+			while (*(--prefix) != '/')
+				; /* do nothing */
+			if (prefix == cwd)
+				prefix++;
+		}
+
+		/*
+		 * if GIT_DIR is no absolute path it wont work anymore after
+		 * changing the directory, therefore expand it to an absolute
+		 * path
+		 */
+		if (gitdirenv[0] != '/') {
+			char buf[PATH_MAX];
+			if (chdir(gitdirenv))
+				die("Cannot change directory to '%s'",
+				    gitdirenv);
+			if (!getcwd(buf, sizeof(buf)) || buf[0] != '/')
+				die("Unable to read current working directory");
+			setenv(GIT_DIR_ENVIRONMENT, buf, 1);
+		}
+		if (chdir(cwd))
+			die("Cannot change directory to '%s'", cwd);
+
+		if (c) {
+			*prefix = c;
+			prefix++;
+			cwd[len] = '/';
+			cwd[len+1] = '\0';
+		} else {
+			prefix = NULL;
 		}
-		die("Not a git repository: '%s'", gitdirenv);
+
+		has_working_directory = 1;
+		if (inside_git_dir == -1)
+			inside_git_dir = 0;
+
+		return prefix;
 	}
 
 	if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
-- 
1.5.0.3.356.gafc9e-dirty

Re: [PATCH(amend)] introduce GIT_WORK_DIR environment variable

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:42:59

On 3/11/07, Matthias Lederhofer [off-list ref] wrote:
This environment variable can be used with GIT_DIR to
specify the toplevel working directory.  When GIT_DIR is not
set this variable is ignored.  As for GIT_DIR there is also
the option git --work-dir which overrides the environment
variable.

Signed-off-by: Matthias Lederhofer <redacted>
Works for me.

-- 
Duy

[PATCH] use $GIT_DIR/workdir as working directory with $GIT_DIR

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

$GIT_DIR/workdir will be used as fallback if $GIT_WORK_DIR is not set

Signed-off-by: Matthias Lederhofer <redacted>
---
 Documentation/repository-layout.txt |    4 ++
 setup.c                             |   64 ++++++++++++++++++++++++++++++++---
 2 files changed, 63 insertions(+), 5 deletions(-)
diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt
index 0459bd9..8a88080 100644
--- a/Documentation/repository-layout.txt
+++ b/Documentation/repository-layout.txt
@@ -179,3 +179,7 @@ shallow::
 	and maintained by shallow clone mechanism.  See `--depth`
 	option to gitlink:git-clone[1] and gitlink:git-fetch[1].
 
+workdir::
+	Directory to be used as toplevel working directory when GIT_DIR
+	is set.  If this is a file the first line will be used as the
+	name of the directory.  This can be overriden by GIT_WORK_DIR.
diff --git a/setup.c b/setup.c
index ebf628e..a8b9fae 100644
--- a/setup.c
+++ b/setup.c
@@ -192,6 +192,64 @@ int is_inside_git_dir(void)
 	return inside_git_dir;
 }
 
+static int stat_git_work_dir(struct stat *st)
+{
+	char workdir[PATH_MAX], cwd[PATH_MAX];
+	const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+	const char *gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+	int offset;
+	FILE *fp;
+
+	if (gitwd) {
+		if (!stat(gitwd, st))
+			return 1;
+		die("Unable to stat git working directory '%s'", gitwd);
+	}
+
+	/* setup_git_directory_gently: PATH_MAX - 40 >= strlen(gitdirenv) */
+	strcpy(workdir, gitdir);
+	strcat(workdir, "/workdir");
+
+	if (stat(workdir, st))
+		return 0;
+	if (st->st_mode & S_IFDIR)
+		return 1;
+	if (!(st->st_mode & S_IFREG))
+		die("GIT_DIR/workdir is neither a file nor a directory");
+
+	/* GIT_DIR/workdir is a file */
+	fp = fopen(workdir, "r");
+	if (!fp)
+		die("Unable to open '%s'", workdir);
+	if (!fgets(workdir, sizeof(workdir), fp))
+		die("Reading working directory from '%s' failed", workdir);
+	fclose(fp);
+	/* remove newline character(s) */
+	offset = strlen(workdir)-1;
+	while (offset >= 0 && (workdir[offset] == '\r' ||
+			workdir[offset] == '\n'))
+		--offset;
+	workdir[offset+1] = '\0';
+
+	/* relative path: change to gitdir for stat */
+	if (workdir[0] != '/') {
+		if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		if (chdir(gitdir))
+			die("Cannot change directory to '%s'", gitdir);
+	}
+
+	if (stat(workdir, st))
+		die("Unable to stat directory from GIT_DIR/workdir");
+	if (!(st->st_mode & S_IFDIR))
+		die("GIT_DIR/workdir does not point to a directory");
+
+	if (workdir[0] != '/' && chdir(cwd))
+		die("Cannot come back to cwd");
+
+	return 1;
+}
+
 int has_working_directory = -1;
 
 const char *setup_git_directory_gently(int *nongit_ok)
@@ -203,7 +261,6 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 	if (gitdirenv) {
 		struct stat st, st_work, st_git;
-		const char *gitwd;
 		char *prefix;
 		char c;
 		int len;
@@ -219,11 +276,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 		}
 
 		/* check for working directory */
-		gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
-		if (!gitwd)
+		if (!stat_git_work_dir(&st_work))
 			return NULL;
-		if (stat(gitwd, &st_work))
-			die("Unable to stat git working directory '%s'", gitwd);
 		if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
 			die("Unable to stat git directory");
 		if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
-- 
1.5.0.3.357.g0b2cd

[PATCH] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

git-init will always put an absolute path in
GIT_DIR/workdir, relative paths are resolved from the
directory git-init was called from.

Signed-off-by: Matthias Lederhofer <redacted>
---
I found this static char path[PATH_MAX] even though path is neither
reused nor returned.  If there is any reason to have this as a static
buffer please change this back to 'static char'
---
 builtin-init-db.c |   40 ++++++++++++++++++++++++++++++++++++++--
 1 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 4df9fd0..f0b4444 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -176,12 +176,13 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
 static int create_default_files(const char *git_dir, const char *template_path)
 {
 	unsigned len = strlen(git_dir);
-	static char path[PATH_MAX];
+	char path[PATH_MAX], workdir[PATH_MAX];
 	unsigned char sha1[20];
 	struct stat st1;
 	char repo_version_string[10];
 	int reinit;
 	int filemode;
+	const char *gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
 
 	if (len > sizeof(path)-50)
 		die("insane git directory %s", git_dir);
@@ -252,7 +253,42 @@ static int create_default_files(const char *git_dir, const char *template_path)
 	}
 	git_config_set("core.filemode", filemode ? "true" : "false");
 
-	if (is_bare_repository()) {
+	/* make gitwd an absolute path */
+	if (gitwd && gitwd[0] != '/') {
+		char cwd[PATH_MAX];
+
+		if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		if (chdir(gitwd)) {
+			fprintf(stderr, "warning: chdir to specified relative "
+				"working directory failed, ignoring\n");
+			gitwd = NULL;
+		}
+		else if (!getcwd(workdir, sizeof(workdir)) || workdir[0] != '/')
+			die("Unable to read current working directory");
+		else {
+			gitwd = workdir;
+			if (chdir(cwd))
+				die("Cannot come back to cwd");
+		}
+	}
+
+	if (gitwd) {
+		FILE *fp = NULL;
+
+		path[len] = 0;
+		strcpy(path + len, "workdir");
+		if (!(fp = fopen(path, "w")))
+			die("Cannot open GIT_DIR/workdir");
+		fprintf(fp, "%s\n", gitwd);
+		fclose(fp);
+
+		git_config_set("core.bare", "false");
+		/* allow template config file to override the default */
+		if (log_all_ref_updates == -1)
+		    git_config_set("core.logallrefupdates", "true");
+	}
+	else if (is_bare_repository()) {
 		git_config_set("core.bare", "true");
 	}
 	else {
-- 
1.5.0.3.1006.gd633

Re: [PATCH] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Joshua N Pritikin <hidden>
Date: 2016-06-15 22:42:59

On Mon, Mar 12, 2007 at 12:53:50PM +0100, Matthias Lederhofer wrote:
git-init will always put an absolute path in
GIT_DIR/workdir, relative paths are resolved from the
directory git-init was called from.
Does that mean I can't move my GIT trees around without changing a 
config entry? What is that an improvement?

Re: [PATCH] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Joshua N Pritikin [off-list ref] wrote:
On Mon, Mar 12, 2007 at 12:53:50PM +0100, Matthias Lederhofer wrote:
quoted
git-init will always put an absolute path in
GIT_DIR/workdir, relative paths are resolved from the
directory git-init was called from.
Does that mean I can't move my GIT trees around without changing a 
config entry? What is that an improvement?
Only when using $GIT_DIR.  $GIT_DIR/workdir is only the default value
for $GIT_WORK_DIR.

Re: [PATCH] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Joshua N Pritikin [off-list ref] wrote:
On Mon, Mar 12, 2007 at 12:53:50PM +0100, Matthias Lederhofer wrote:
quoted
git-init will always put an absolute path in
GIT_DIR/workdir, relative paths are resolved from the
directory git-init was called from.
Does that mean I can't move my GIT trees around without changing a 
config entry? What is that an improvement?
And for the decision to put an absolute path there by default:

Putting $GIT_WORK_DIR as is into $GIT_DIR/workdir is probably not what
the user expects because the content of $GIT_DIR/workdir is
interpreted relative to $GIT_DIR, not the current working directory.
Example:

    /tmp$ mkdir repository working_directory
    /tmp$ git --git-dir=repository --work-dir=working_directory init

If git init puts 'working_directory' into $GIT_DIR/workdir it would
make the associated working directory $GIT_DIR/working_directory =
/tmp/repository/working_directory and not /tmp/working_directory.

The alternative to use

    /tmp$ git --git-dir=repository --work-dir=../working_directory init

seems quite confusing to me.

If you've any other idea to solve this please tell me.

Re: [PATCH] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:42:59

On 3/12/07, Matthias Lederhofer [off-list ref] wrote:
Putting $GIT_WORK_DIR as is into $GIT_DIR/workdir is probably not what
the user expects because the content of $GIT_DIR/workdir is
interpreted relative to $GIT_DIR, not the current working directory.
Example:

    /tmp$ mkdir repository working_directory
    /tmp$ git --git-dir=repository --work-dir=working_directory init

If git init puts 'working_directory' into $GIT_DIR/workdir it would
make the associated working directory $GIT_DIR/working_directory =
/tmp/repository/working_directory and not /tmp/working_directory.

The alternative to use

    /tmp$ git --git-dir=repository --work-dir=../working_directory init

seems quite confusing to me.

If you've any other idea to solve this please tell me.
Let users create $GIT_DIR/workdir themselves. Your way may be less
confusing to you but might be more confusing to me because I _might_
expect a relative workdir setting (for example I move the repository
and the working directory together to another place).

-- 
Duy

Re: [PATCH] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Nguyen Thai Ngoc Duy [off-list ref] wrote:
Let users create $GIT_DIR/workdir themselves. Your way may be less
confusing to you but might be more confusing to me because I _might_
expect a relative workdir setting (for example I move the repository
and the working directory together to another place).
Well, without this patch you havo to do one of these:

    /tmp$ mkdir repository working_directory
    /tmp$ git --git-dir=repository init
    /tmp$ git --git-dir=repository config core.bare false
    /tmp$ echo ../working_directory > repository/workdir

or

    /tmp$ mkdir repository working_directory
    /tmp/repository$ cd repository
    /tmp$ git init
    /tmp/repository$ mv .git/* .
    /tmp/repository$ rmdir .git
    /tmp$ echo ../working_directory > repository/workdir

Where the second example is probably better because git init creates a
bare repository in the first case which might have more settings
different from a 'normal' repository (it actually sets also
core.logallrefupdates = true which probably should be added to the
first example).

With this patch you'd have to do

    /tmp$ mkdir repository working_directory
    /tmp$ git --git-dir=repository --work-dir=working_directory init
    /tmp$ echo ../working_directory > repository/workdir

in case you really want a relative path.

Because the first two ways are so long I think git init should have
some way to handle this case.

I don't want to put $GIT_WORK_DIR 'as is' to $GIT_DIR/workdir because
$GIT_WORK_DIR is normally interpreted as relative path to the current
working directory and not relative to $GIT_DIR.

Perhaps we could add another flag to git init which will be used 'as is'
for $GIT_DIR/workdir:
    git --git-dir=repository init --work-dir=../working_directory

Other things I can think of:

    Add a --not-bare flag to git init:
    $ git --git-dir=repository init --not-bare
    $ echo ../working_directory > repository/workdir

    Tell the user what to do in case the path should be relative:
    $ git --git-dir=repository --work-dir=working_directory init
    You specified a relative working directory and git has
    automatically expanded this to /tmp/working_directory.  If you
    prefer a relative path you can do:
        echo relative_path > repository/workdir

[PATCH(amend)] git-init: set up GIT_DIR/workdir if GIT_WORK_DIR is set

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

Signed-off-by: Matthias Lederhofer <redacted>
---
It's much easier when $GIT_WORK_DIR is always interpreted relative to
$GIT_DIR.
---
 builtin-init-db.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 4df9fd0..8d0065c 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -182,6 +182,7 @@ static int create_default_files(const char *git_dir, const char *template_path)
 	char repo_version_string[10];
 	int reinit;
 	int filemode;
+	const char *gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
 
 	if (len > sizeof(path)-50)
 		die("insane git directory %s", git_dir);
@@ -252,10 +253,21 @@ static int create_default_files(const char *git_dir, const char *template_path)
 	}
 	git_config_set("core.filemode", filemode ? "true" : "false");
 
-	if (is_bare_repository()) {
+	if (is_bare_repository() && !gitwd) {
 		git_config_set("core.bare", "true");
 	}
 	else {
+		if (gitwd) {
+			FILE *fp = NULL;
+
+			path[len] = 0;
+			strcpy(path + len, "workdir");
+			if (!(fp = fopen(path, "w")))
+				die("Cannot open GIT_DIR/workdir");
+			fprintf(fp, "%s\n", gitwd);
+			fclose(fp);
+		}
+
 		git_config_set("core.bare", "false");
 		/* allow template config file to override the default */
 		if (log_all_ref_updates == -1)
-- 
1.5.0.3.1007.g7ff7

[PATCH] core.workdir config variable

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

core.workdir is used as default value for $GIT_WORK_DIR

Signed-off-by: Matthias Lederhofer <redacted>
---
This one replaces the 'use $GIT_DIR/workdir as working directory with
$GIT_DIR' patch and uses core.workdir instead.  This patch gets
core.workdir only for setup.c from the configuration file.  Perhaps
this should be handled in git_default_config like most other core.*
variables.

I just noticed that dying if the specified workdir is invalid can
cause trouble too:

    $ export GIT_DIR=/path/to/repository
    $ git config core.workdir /non-existent
    $ git config core.workdir ../workdir
    fatal: Unable to stat git working directory '/non-existent'

Workaround is either to edit the file manually or do:

    $ git --work-dir=. config core.workdir ../workdir

---
 Documentation/config.txt |    4 +++
 setup.c                  |   53 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5408dd6..663d82d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -162,6 +162,10 @@ repository that ends in "/.git" is assumed to be not bare (bare =
 false), while all other repositories are assumed to be bare (bare
 = true).
 
+core.workdir::
+	Directory to be used as toplevel working directory when GIT_DIR
+	is set.  This can be overriden by GIT_WORK_DIR.
+
 core.logAllRefUpdates::
 	Updates to a ref <ref> is logged to the file
 	"$GIT_DIR/logs/<ref>", by appending the new and old
diff --git a/setup.c b/setup.c
index ebf628e..913c4b4 100644
--- a/setup.c
+++ b/setup.c
@@ -192,6 +192,53 @@ int is_inside_git_dir(void)
 	return inside_git_dir;
 }
 
+static char **git_work_dir;
+
+static int git_workdir_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "core.workdir")) {
+		strlcpy(value, *git_work_dir, PATH_MAX);
+	}
+	return 0;
+}
+
+static int stat_git_work_dir(struct stat *st)
+{
+	char workdir[PATH_MAX], cwd[PATH_MAX];
+	const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+	const char *gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+
+	if (gitwd) {
+		if (!stat(gitwd, st))
+			return 1;
+		die("Unable to stat git working directory '%s'", gitwd);
+	}
+
+	/* get workdir from config */
+	workdir[0] = '\0';
+	git_work_dir = workdir;
+	git_config(git_workdir_config);
+	git_workdir_config = NULL;
+	if (!workdir[0])
+		return 0;
+
+	/* relative path: change to gitdir for stat */
+	if (workdir[0] != '/') {
+		if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		if (chdir(gitdir))
+			die("Cannot change directory to '%s'", gitdir);
+	}
+
+	if (stat(workdir, st))
+		die("Unable to stat git working directory '%s'", workdir);
+
+	if (workdir[0] != '/' && chdir(cwd))
+		die("Cannot come back to cwd");
+
+	return 1;
+}
+
 int has_working_directory = -1;
 
 const char *setup_git_directory_gently(int *nongit_ok)
@@ -203,7 +250,6 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 	if (gitdirenv) {
 		struct stat st, st_work, st_git;
-		const char *gitwd;
 		char *prefix;
 		char c;
 		int len;
@@ -219,11 +265,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 		}
 
 		/* check for working directory */
-		gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
-		if (!gitwd)
+		if (!stat_git_work_dir(&st_work))
 			return NULL;
-		if (stat(gitwd, &st_work))
-			die("Unable to stat git working directory '%s'", gitwd);
 		if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
 			die("Unable to stat git directory");
 		if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
-- 
1.5.0.3.970.ge984-dirty

[PATCH(amend)] core.workdir config variable

From: Matthias Lederhofer <hidden>
Date: 2016-06-15 22:42:59

core.workdir is used as default value for $GIT_WORK_DIR

Signed-off-by: Matthias Lederhofer <redacted>
---
Sorry, the last one was totally broken.  Testing without recompiling
isn't good..
---
 Documentation/config.txt |    4 +++
 setup.c                  |   53 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5408dd6..663d82d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -162,6 +162,10 @@ repository that ends in "/.git" is assumed to be not bare (bare =
 false), while all other repositories are assumed to be bare (bare
 = true).
 
+core.workdir::
+	Directory to be used as toplevel working directory when GIT_DIR
+	is set.  This can be overriden by GIT_WORK_DIR.
+
 core.logAllRefUpdates::
 	Updates to a ref <ref> is logged to the file
 	"$GIT_DIR/logs/<ref>", by appending the new and old
diff --git a/setup.c b/setup.c
index ebf628e..208124f 100644
--- a/setup.c
+++ b/setup.c
@@ -192,6 +192,53 @@ int is_inside_git_dir(void)
 	return inside_git_dir;
 }
 
+static char *git_work_dir;
+
+static int git_workdir_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "core.workdir")) {
+		strlcpy(git_work_dir, value, PATH_MAX);
+	}
+	return 0;
+}
+
+static int stat_git_work_dir(struct stat *st)
+{
+	char workdir[PATH_MAX], cwd[PATH_MAX];
+	const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+	const char *gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+
+	if (gitwd) {
+		if (!stat(gitwd, st))
+			return 1;
+		die("Unable to stat git working directory '%s'", gitwd);
+	}
+
+	/* get workdir from config */
+	workdir[0] = '\0';
+	git_work_dir = workdir;
+	git_config(git_workdir_config);
+	git_work_dir = NULL;
+	if (!workdir[0])
+		return 0;
+
+	/* relative path: change to gitdir for stat */
+	if (workdir[0] != '/') {
+		if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		if (chdir(gitdir))
+			die("Cannot change directory to '%s'", gitdir);
+	}
+
+	if (stat(workdir, st))
+		die("Unable to stat git working directory '%s'", workdir);
+
+	if (workdir[0] != '/' && chdir(cwd))
+		die("Cannot come back to cwd");
+
+	return 1;
+}
+
 int has_working_directory = -1;
 
 const char *setup_git_directory_gently(int *nongit_ok)
@@ -203,7 +250,6 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 	if (gitdirenv) {
 		struct stat st, st_work, st_git;
-		const char *gitwd;
 		char *prefix;
 		char c;
 		int len;
@@ -219,11 +265,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 		}
 
 		/* check for working directory */
-		gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
-		if (!gitwd)
+		if (!stat_git_work_dir(&st_work))
 			return NULL;
-		if (stat(gitwd, &st_work))
-			die("Unable to stat git working directory '%s'", gitwd);
 		if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
 			die("Unable to stat git directory");
 		if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
-- 
gitgui.0.6.3.g4bccd

Re: [PATCH(amend)] core.workdir config variable

From: Shawn O. Pearce <hidden>
Date: 2016-06-15 22:42:59

Matthias Lederhofer [off-list ref] wrote:
core.workdir is used as default value for $GIT_WORK_DIR
...
-- 
gitgui.0.6.3.g4bccd
Really?  gitgui generates emails now?  ;-)

Matthias, your git binaries were built with an older (pre 1.5.0)
git-describe in the path.  This caused the version number to be
incorrectly determined, due to an old bug in git-describe.

Recompiling with your "gitgui.0.6.3" git-describe should get
the correct version number, as that is most definately after the
git-describe bug fix.

-- 
Shawn.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help