Skip to content

Commit 0dd98b6

Browse files
committed
Add GIT_REPOSITORY_OPEN_FROM_ENV flag to respect $GIT_* environment vars
git_repository_open_ext provides parameters for the start path, whether to search across filesystems, and what ceiling directories to stop at. git commands have standard environment variables and defaults for each of those, as well as various other parameters of the repository. To avoid duplicate environment variable handling in users of libgit2, add a GIT_REPOSITORY_OPEN_FROM_ENV flag, which makes git_repository_open_ext automatically handle the appropriate environment variables. Commands that intend to act just like those built into git itself can use this flag to get the expected default behavior. git_repository_open_ext with the GIT_REPOSITORY_OPEN_FROM_ENV flag respects $GIT_DIR, $GIT_DISCOVERY_ACROSS_FILESYSTEM, $GIT_CEILING_DIRECTORIES, $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and $GIT_ALTERNATE_OBJECT_DIRECTORIES. In the future, when libgit2 gets worktree support, git_repository_open_env will also respect $GIT_WORK_TREE and $GIT_COMMON_DIR; until then, git_repository_open_ext with this flag will error out if either $GIT_WORK_TREE or $GIT_COMMON_DIR is set.
1 parent 39c6fca commit 0dd98b6

File tree

3 files changed

+443
-1
lines changed

3 files changed

+443
-1
lines changed

include/git2/repository.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,26 @@ GIT_EXTERN(int) git_repository_discover(
9898
* * GIT_REPOSITORY_OPEN_NO_DOTGIT - Do not check for a repository by
9999
* appending /.git to the start_path; only open the repository if
100100
* start_path itself points to the git directory.
101+
* * GIT_REPOSITORY_OPEN_FROM_ENV - Find and open a git repository,
102+
* respecting the environment variables used by the git command-line
103+
* tools. If set, `git_repository_open_ext` will ignore the other
104+
* flags and the `ceiling_dirs` argument, and will allow a NULL `path`
105+
* to use `GIT_DIR` or search from the current directory. The search
106+
* for a repository will respect $GIT_CEILING_DIRECTORIES and
107+
* $GIT_DISCOVERY_ACROSS_FILESYSTEM. The opened repository will
108+
* respect $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and
109+
* $GIT_ALTERNATE_OBJECT_DIRECTORIES. In the future, this flag will
110+
* also cause `git_repository_open_ext` to respect $GIT_WORK_TREE and
111+
* $GIT_COMMON_DIR; currently, `git_repository_open_ext` with this
112+
* flag will error out if either $GIT_WORK_TREE or $GIT_COMMON_DIR is
113+
* set.
101114
*/
102115
typedef enum {
103116
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
104117
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
105118
GIT_REPOSITORY_OPEN_BARE = (1 << 2),
106119
GIT_REPOSITORY_OPEN_NO_DOTGIT = (1 << 3),
120+
GIT_REPOSITORY_OPEN_FROM_ENV = (1 << 4),
107121
} git_repository_open_flag_t;
108122

109123
/**
@@ -114,7 +128,8 @@ typedef enum {
114128
* see if a repo at this path could be opened.
115129
* @param path Path to open as git repository. If the flags
116130
* permit "searching", then this can be a path to a subdirectory
117-
* inside the working directory of the repository.
131+
* inside the working directory of the repository. May be NULL if
132+
* flags is GIT_REPOSITORY_OPEN_FROM_ENV.
118133
* @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
119134
* @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
120135
* prefixes at which the search for a containing repository should

src/repository.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,172 @@ int git_repository_open_bare(
491491
return 0;
492492
}
493493

494+
static int _git_repository_open_ext_from_env(
495+
git_repository **out,
496+
const char *start_path)
497+
{
498+
git_repository *repo = NULL;
499+
git_index *index = NULL;
500+
git_odb *odb = NULL;
501+
git_buf dir_buf = GIT_BUF_INIT;
502+
git_buf ceiling_dirs_buf = GIT_BUF_INIT;
503+
git_buf across_fs_buf = GIT_BUF_INIT;
504+
git_buf index_file_buf = GIT_BUF_INIT;
505+
git_buf namespace_buf = GIT_BUF_INIT;
506+
git_buf object_dir_buf = GIT_BUF_INIT;
507+
git_buf alts_buf = GIT_BUF_INIT;
508+
git_buf work_tree_buf = GIT_BUF_INIT;
509+
git_buf common_dir_buf = GIT_BUF_INIT;
510+
const char *ceiling_dirs = NULL;
511+
unsigned flags = 0;
512+
int error;
513+
514+
if (!start_path) {
515+
error = git__getenv(&dir_buf, "GIT_DIR");
516+
if (error == GIT_ENOTFOUND) {
517+
giterr_clear();
518+
start_path = ".";
519+
} else if (error < 0)
520+
goto error;
521+
else {
522+
start_path = git_buf_cstr(&dir_buf);
523+
flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
524+
flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
525+
}
526+
}
527+
528+
error = git__getenv(&ceiling_dirs_buf, "GIT_CEILING_DIRECTORIES");
529+
if (error == GIT_ENOTFOUND)
530+
giterr_clear();
531+
else if (error < 0)
532+
goto error;
533+
else
534+
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
535+
536+
error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM");
537+
if (error == GIT_ENOTFOUND)
538+
giterr_clear();
539+
else if (error < 0)
540+
goto error;
541+
else {
542+
int across_fs = 0;
543+
error = git_config_parse_bool(&across_fs, git_buf_cstr(&across_fs_buf));
544+
if (error < 0)
545+
goto error;
546+
if (across_fs)
547+
flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
548+
}
549+
550+
error = git__getenv(&index_file_buf, "GIT_INDEX_FILE");
551+
if (error == GIT_ENOTFOUND)
552+
giterr_clear();
553+
else if (error < 0)
554+
goto error;
555+
else {
556+
error = git_index_open(&index, git_buf_cstr(&index_file_buf));
557+
if (error < 0)
558+
goto error;
559+
}
560+
561+
error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
562+
if (error == GIT_ENOTFOUND)
563+
giterr_clear();
564+
else if (error < 0)
565+
goto error;
566+
567+
error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY");
568+
if (error == GIT_ENOTFOUND)
569+
giterr_clear();
570+
else if (error < 0)
571+
goto error;
572+
else {
573+
error = git_odb_open(&odb, git_buf_cstr(&object_dir_buf));
574+
if (error < 0)
575+
goto error;
576+
}
577+
578+
error = git__getenv(&work_tree_buf, "GIT_WORK_TREE");
579+
if (error == GIT_ENOTFOUND)
580+
giterr_clear();
581+
else if (error < 0)
582+
goto error;
583+
else {
584+
giterr_set(GITERR_INVALID, "GIT_WORK_TREE unimplemented");
585+
error = GIT_ERROR;
586+
goto error;
587+
}
588+
589+
error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR");
590+
if (error == GIT_ENOTFOUND)
591+
giterr_clear();
592+
else if (error < 0)
593+
goto error;
594+
else {
595+
giterr_set(GITERR_INVALID, "GIT_COMMON_DIR unimplemented");
596+
error = GIT_ERROR;
597+
goto error;
598+
}
599+
600+
error = git_repository_open_ext(&repo, start_path, flags, ceiling_dirs);
601+
if (error < 0)
602+
goto error;
603+
604+
if (odb)
605+
git_repository_set_odb(repo, odb);
606+
607+
error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
608+
if (error == GIT_ENOTFOUND)
609+
giterr_clear();
610+
else if (error < 0)
611+
goto error;
612+
else {
613+
const char *end;
614+
char *alt, *sep;
615+
if (!odb) {
616+
error = git_repository_odb(&odb, repo);
617+
if (error < 0)
618+
goto error;
619+
}
620+
621+
end = git_buf_cstr(&alts_buf) + git_buf_len(&alts_buf);
622+
for (sep = alt = alts_buf.ptr; sep != end; alt = sep+1) {
623+
for (sep = alt; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++)
624+
;
625+
if (*sep)
626+
*sep = '\0';
627+
error = git_odb_add_disk_alternate(odb, alt);
628+
if (error < 0)
629+
goto error;
630+
}
631+
}
632+
633+
error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf));
634+
if (error < 0)
635+
goto error;
636+
637+
git_repository_set_index(repo, index);
638+
639+
if (out) {
640+
*out = repo;
641+
goto success;
642+
}
643+
error:
644+
git_repository_free(repo);
645+
success:
646+
git_odb_free(odb);
647+
git_index_free(index);
648+
git_buf_free(&common_dir_buf);
649+
git_buf_free(&work_tree_buf);
650+
git_buf_free(&alts_buf);
651+
git_buf_free(&object_dir_buf);
652+
git_buf_free(&namespace_buf);
653+
git_buf_free(&index_file_buf);
654+
git_buf_free(&across_fs_buf);
655+
git_buf_free(&ceiling_dirs_buf);
656+
git_buf_free(&dir_buf);
657+
return error;
658+
}
659+
494660
int git_repository_open_ext(
495661
git_repository **repo_ptr,
496662
const char *start_path,
@@ -503,6 +669,9 @@ int git_repository_open_ext(
503669
git_repository *repo;
504670
git_config *config = NULL;
505671

672+
if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
673+
return _git_repository_open_ext_from_env(repo_ptr, start_path);
674+
506675
if (repo_ptr)
507676
*repo_ptr = NULL;
508677

0 commit comments

Comments
 (0)