int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *result) { int len = strlen(path), retval; char *gitdir; const char *tmp; while (len && path[len-1] == '/') len--; if (!len) return -1; gitdir = xmalloc(len + MAXREFLEN + 8); memcpy(gitdir, path, len); memcpy(gitdir + len, "/.git", 6); len += 5; tmp = read_gitfile_gently(gitdir); if (tmp) { free(gitdir); len = strlen(tmp); gitdir = xmalloc(len + MAXREFLEN + 3); memcpy(gitdir, tmp, len); } gitdir[len] = '/'; gitdir[++len] = '\0'; retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0); free(gitdir); return retval; }
static void setup_git_env(void) { git_dir = getenv(GIT_DIR_ENVIRONMENT); git_dir = git_dir ? xstrdup(git_dir) : NULL; if (!git_dir) { git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); git_dir = git_dir ? xstrdup(git_dir) : NULL; } if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; git_object_dir = getenv(DB_ENVIRONMENT); if (!git_object_dir) { git_object_dir = xmalloc(strlen(git_dir) + 9); sprintf(git_object_dir, "%s/objects", git_dir); } git_index_file = getenv(INDEX_ENVIRONMENT); if (!git_index_file) { git_index_file = xmalloc(strlen(git_dir) + 7); sprintf(git_index_file, "%s/index", git_dir); } git_graft_file = getenv(GRAFT_ENVIRONMENT); if (!git_graft_file) git_graft_file = git_pathdup("info/grafts"); if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) read_replace_refs = 0; }
int is_nonbare_repository_dir(struct strbuf *path) { int ret = 0; int gitfile_error; size_t orig_path_len = path->len; assert(orig_path_len != 0); strbuf_complete(path, '/'); strbuf_addstr(path, ".git"); if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf)) ret = 1; if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED || gitfile_error == READ_GITFILE_ERR_READ_FAILED) ret = 1; strbuf_setlen(path, orig_path_len); return ret; }
static int add_submodule_odb(const char *path) { struct strbuf objects_directory = STRBUF_INIT; struct alternate_object_database *alt_odb; int ret = 0; const char *git_dir; strbuf_addf(&objects_directory, "%s/.git", path); git_dir = read_gitfile_gently(objects_directory.buf); if (git_dir) { strbuf_reset(&objects_directory); strbuf_addstr(&objects_directory, git_dir); } strbuf_addstr(&objects_directory, "/objects/"); if (!is_directory(objects_directory.buf)) { ret = -1; goto done; } /* avoid adding it twice */ for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next) if (alt_odb->name - alt_odb->base == objects_directory.len && !strncmp(alt_odb->base, objects_directory.buf, objects_directory.len)) goto done; alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb)); alt_odb->next = alt_odb_list; strcpy(alt_odb->base, objects_directory.buf); alt_odb->name = alt_odb->base + objects_directory.len; alt_odb->name[2] = '/'; alt_odb->name[40] = '\0'; alt_odb->name[41] = '\0'; alt_odb_list = alt_odb; prepare_alt_odb(); done: strbuf_release(&objects_directory); return ret; }
static void setup_git_env(void) { git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; git_object_dir = getenv(DB_ENVIRONMENT); if (!git_object_dir) { git_object_dir = xmalloc(strlen(git_dir) + 9); sprintf(git_object_dir, "%s/objects", git_dir); } git_refs_dir = xmalloc(strlen(git_dir) + 6); sprintf(git_refs_dir, "%s/refs", git_dir); git_index_file = getenv(INDEX_ENVIRONMENT); if (!git_index_file) { git_index_file = xmalloc(strlen(git_dir) + 7); sprintf(git_index_file, "%s/index", git_dir); } git_graft_file = getenv(GRAFT_ENVIRONMENT); if (!git_graft_file) git_graft_file = git_pathdup("info/grafts"); }
/* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. */ const char *setup_git_directory_gently(int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); static char cwd[PATH_MAX+1]; const char *gitdirenv; const char *gitfile_dir; int len, offset, ceil_offset; /* * Let's assume that we are in a git repository. * If it turns out later that we are somewhere else, the value will be * updated accordingly. */ if (nongit_ok) *nongit_ok = 0; /* * 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) { if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); if (is_git_directory(gitdirenv)) { static char buffer[1024 + 1]; const char *retval; if (!work_tree_env) { retval = set_work_tree(gitdirenv); /* config may override worktree */ if (check_repository_format_gently(nongit_ok)) return NULL; return retval; } if (check_repository_format_gently(nongit_ok)) return NULL; retval = get_relative_cwd(buffer, sizeof(buffer) - 1, get_git_work_tree()); if (!retval || !*retval) return NULL; set_git_dir(make_absolute_path(gitdirenv)); if (chdir(work_tree_env) < 0) die_errno ("Could not chdir to '%s'", work_tree_env); strcat(buffer, "/"); return retval; } if (nongit_ok) { *nongit_ok = 1; return NULL; } die("Not a git repository: '%s'", gitdirenv); } if (!getcwd(cwd, sizeof(cwd)-1)) die_errno("Unable to read current working directory"); ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) ceil_offset = 1; /* * Test in the following order (relative to the cwd): * - .git (file containing "gitdir: <path>") * - .git/ * - ./ (bare) * - ../.git * - ../.git/ * - ../ (bare) * - ../../.git/ * etc. */ offset = len = strlen(cwd); for (;;) { gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); if (gitfile_dir) { if (set_git_dir(gitfile_dir)) die("Repository setup failed"); break; } if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) break; if (is_git_directory(".")) { inside_git_dir = 1; if (!work_tree_env) inside_work_tree = 0; if (offset != len) { cwd[offset] = '\0'; setenv(GIT_DIR_ENVIRONMENT, cwd, 1); } else setenv(GIT_DIR_ENVIRONMENT, ".", 1); check_repository_format_gently(nongit_ok); return NULL; } while (--offset > ceil_offset && cwd[offset] != '/'); if (offset <= ceil_offset) { if (nongit_ok) { if (chdir(cwd)) die_errno("Cannot come back to cwd"); *nongit_ok = 1; return NULL; } die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT); } if (chdir("..")) die_errno("Cannot change to '%s/..'", cwd); } inside_git_dir = 0; if (!work_tree_env) inside_work_tree = 1; git_work_tree_cfg = xstrndup(cwd, offset); if (check_repository_format_gently(nongit_ok)) return NULL; if (offset == len) return NULL; /* Make "offset" point to past the '/', and add a '/' at the end */ offset++; cwd[len++] = '/'; cwd[len] = 0; return cwd + offset; }
/* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. */ static const char *setup_git_directory_gently_1(int *nongit_ok) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); static char cwd[PATH_MAX+1]; const char *gitdirenv, *ret; char *gitfile; int len, offset, ceil_offset; dev_t current_device = 0; int one_filesystem = 1; /* * Let's assume that we are in a git repository. * If it turns out later that we are somewhere else, the value will be * updated accordingly. */ if (nongit_ok) *nongit_ok = 0; if (!getcwd(cwd, sizeof(cwd)-1)) die_errno("Unable to read current working directory"); offset = len = strlen(cwd); /* * 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) return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) ceil_offset = 1; /* * Test in the following order (relative to the cwd): * - .git (file containing "gitdir: <path>") * - .git/ * - ./ (bare) * - ../.git * - ../.git/ * - ../ (bare) * - ../../.git/ * etc. */ one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0); if (one_filesystem) current_device = get_device_or_die(".", NULL); for (;;) { gitfile = (char*)read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); if (gitfile) gitdirenv = gitfile = xstrdup(gitfile); else { if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; } if (gitdirenv) { ret = setup_discovered_git_dir(gitdirenv, cwd, offset, len, nongit_ok); free(gitfile); return ret; } free(gitfile); if (is_git_directory(".")) return setup_bare_git_dir(cwd, offset, len, nongit_ok); while (--offset > ceil_offset && cwd[offset] != '/'); if (offset <= ceil_offset) return setup_nongit(cwd, nongit_ok); if (one_filesystem) { dev_t parent_device = get_device_or_die("..", cwd); if (parent_device != current_device) { if (nongit_ok) { if (chdir(cwd)) die_errno("Cannot come back to cwd"); *nongit_ok = 1; return NULL; } cwd[offset] = '\0'; die("Not a git repository (or any parent up to mount parent %s)\n" "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd); } } if (chdir("..")) { cwd[offset] = '\0'; die_errno("Cannot change to '%s/..'", cwd); } } }
static const char *setup_explicit_git_dir(const char *gitdirenv, char *cwd, int len, int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); const char *worktree; char *gitfile; if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); gitfile = (char*)read_gitfile_gently(gitdirenv); if (gitfile) { gitfile = xstrdup(gitfile); gitdirenv = gitfile; } if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; free(gitfile); return NULL; } die("Not a git repository: '%s'", gitdirenv); } if (check_repository_format_gently(gitdirenv, nongit_ok)) { free(gitfile); return NULL; } /* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */ if (work_tree_env) set_git_work_tree(work_tree_env); else if (is_bare_repository_cfg > 0) { if (git_work_tree_cfg) /* #22.2, #30 */ die("core.bare and core.worktree do not make sense"); /* #18, #26 */ set_git_dir(gitdirenv); free(gitfile); return NULL; } else if (git_work_tree_cfg) { /* #6, #14 */ if (is_absolute_path(git_work_tree_cfg)) set_git_work_tree(git_work_tree_cfg); else { char core_worktree[PATH_MAX]; if (chdir(gitdirenv)) die_errno("Could not chdir to '%s'", gitdirenv); if (chdir(git_work_tree_cfg)) die_errno("Could not chdir to '%s'", git_work_tree_cfg); if (!getcwd(core_worktree, PATH_MAX)) die_errno("Could not get directory '%s'", git_work_tree_cfg); if (chdir(cwd)) die_errno("Could not come back to cwd"); set_git_work_tree(core_worktree); } } else /* #2, #10 */ set_git_work_tree("."); /* set_git_work_tree() must have been called by now */ worktree = get_git_work_tree(); /* both get_git_work_tree() and cwd are already normalized */ if (!strcmp(cwd, worktree)) { /* cwd == worktree */ set_git_dir(gitdirenv); free(gitfile); return NULL; } if (!prefixcmp(cwd, worktree) && cwd[strlen(worktree)] == '/') { /* cwd inside worktree */ set_git_dir(real_path(gitdirenv)); if (chdir(worktree)) die_errno("Could not chdir to '%s'", worktree); cwd[len++] = '/'; cwd[len] = '\0'; free(gitfile); return cwd + strlen(worktree) + 1; } /* cwd outside worktree */ set_git_dir(gitdirenv); free(gitfile); return NULL; }
int validate_worktree(const struct worktree *wt, struct strbuf *errmsg, unsigned flags) { struct strbuf wt_path = STRBUF_INIT; char *path = NULL; int err, ret = -1; strbuf_addf(&wt_path, "%s/.git", wt->path); if (is_main_worktree(wt)) { if (is_directory(wt_path.buf)) { ret = 0; goto done; } /* * Main worktree using .git file to point to the * repository would make it impossible to know where * the actual worktree is if this function is executed * from another worktree. No .git file support for now. */ strbuf_addf_gently(errmsg, _("'%s' at main working tree is not the repository directory"), wt_path.buf); goto done; } /* * Make sure "gitdir" file points to a real .git file and that * file points back here. */ if (!is_absolute_path(wt->path)) { strbuf_addf_gently(errmsg, _("'%s' file does not contain absolute path to the working tree location"), git_common_path("worktrees/%s/gitdir", wt->id)); goto done; } if (flags & WT_VALIDATE_WORKTREE_MISSING_OK && !file_exists(wt->path)) { ret = 0; goto done; } if (!file_exists(wt_path.buf)) { strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf); goto done; } path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err)); if (!path) { strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"), wt_path.buf, err); goto done; } ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id))); if (ret) strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"), wt->path, git_common_path("worktrees/%s", wt->id)); done: free(path); strbuf_release(&wt_path); return ret; }
unsigned is_submodule_modified(const char *path, int ignore_untracked) { ssize_t len; struct child_process cp; const char *argv[] = { "status", "--porcelain", NULL, NULL, }; struct strbuf buf = STRBUF_INIT; unsigned dirty_submodule = 0; const char *line, *next_line; const char *git_dir; strbuf_addf(&buf, "%s/.git", path); git_dir = read_gitfile_gently(buf.buf); if (!git_dir) git_dir = buf.buf; if (!is_directory(git_dir)) { strbuf_release(&buf); /* The submodule is not checked out, so it is not modified */ return 0; } strbuf_reset(&buf); if (ignore_untracked) argv[2] = "-uno"; memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; cp.out = -1; cp.dir = path; if (start_command(&cp)) die("Could not run git status --porcelain"); len = strbuf_read(&buf, cp.out, 1024); line = buf.buf; while (len > 2) { if ((line[0] == '?') && (line[1] == '?')) { dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED; if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED) break; } else { dirty_submodule |= DIRTY_SUBMODULE_MODIFIED; if (ignore_untracked || (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)) break; } next_line = strchr(line, '\n'); if (!next_line) break; next_line++; len -= (next_line - line); line = next_line; } close(cp.out); if (finish_command(&cp)) die("git status --porcelain failed"); strbuf_release(&buf); return dirty_submodule; }
int fetch_populated_submodules(int num_options, const char **options, const char *prefix, int command_line_option, int quiet) { int i, result = 0, argc = 0, default_argc; struct child_process cp; const char **argv; struct string_list_item *name_for_path; const char *work_tree = get_git_work_tree(); if (!work_tree) goto out; if (!the_index.initialized) if (read_cache() < 0) die("index file corrupt"); /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */ argv = xcalloc(num_options + 6, sizeof(const char *)); argv[argc++] = "fetch"; for (i = 0; i < num_options; i++) argv[argc++] = options[i]; argv[argc++] = "--recurse-submodules-default"; default_argc = argc++; argv[argc++] = "--submodule-prefix"; memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; for (i = 0; i < active_nr; i++) { struct strbuf submodule_path = STRBUF_INIT; struct strbuf submodule_git_dir = STRBUF_INIT; struct strbuf submodule_prefix = STRBUF_INIT; struct cache_entry *ce = active_cache[i]; const char *git_dir, *name, *default_argv; if (!S_ISGITLINK(ce->ce_mode)) continue; name = ce->name; name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name); if (name_for_path) name = name_for_path->util; default_argv = "yes"; if (command_line_option == RECURSE_SUBMODULES_DEFAULT) { struct string_list_item *fetch_recurse_submodules_option; fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name); if (fetch_recurse_submodules_option) { if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF) continue; if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } } else { if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_OFF) continue; if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } } } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name); strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf); strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name); git_dir = read_gitfile_gently(submodule_git_dir.buf); if (!git_dir) git_dir = submodule_git_dir.buf; if (is_directory(git_dir)) { if (!quiet) printf("Fetching submodule %s%s\n", prefix, ce->name); cp.dir = submodule_path.buf; argv[default_argc] = default_argv; argv[argc] = submodule_prefix.buf; if (run_command(&cp)) result = 1; } strbuf_release(&submodule_path); strbuf_release(&submodule_git_dir); strbuf_release(&submodule_prefix); } free(argv); out: string_list_clear(&changed_submodule_paths, 1); return result; }
int fetch_populated_submodules(int num_options, const char **options, const char *prefix, int ignore_config, int quiet) { int i, result = 0, argc = 0; struct child_process cp; const char **argv; struct string_list_item *name_for_path; const char *work_tree = get_git_work_tree(); if (!work_tree) return 0; if (!the_index.initialized) if (read_cache() < 0) die("index file corrupt"); /* 4: "fetch" (options) "--submodule-prefix" prefix NULL */ argv = xcalloc(num_options + 4, sizeof(const char *)); argv[argc++] = "fetch"; for (i = 0; i < num_options; i++) argv[argc++] = options[i]; argv[argc++] = "--submodule-prefix"; memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; for (i = 0; i < active_nr; i++) { struct strbuf submodule_path = STRBUF_INIT; struct strbuf submodule_git_dir = STRBUF_INIT; struct strbuf submodule_prefix = STRBUF_INIT; struct cache_entry *ce = active_cache[i]; const char *git_dir, *name; if (!S_ISGITLINK(ce->ce_mode)) continue; name = ce->name; name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name); if (name_for_path) name = name_for_path->util; if (!ignore_config) { struct string_list_item *fetch_recurse_submodules_option; fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name); if (fetch_recurse_submodules_option) { if (!fetch_recurse_submodules_option->util) continue; } else { if (!config_fetch_recurse_submodules) continue; } } strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name); strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf); strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name); git_dir = read_gitfile_gently(submodule_git_dir.buf); if (!git_dir) git_dir = submodule_git_dir.buf; if (is_directory(git_dir)) { if (!quiet) printf("Fetching submodule %s%s\n", prefix, ce->name); cp.dir = submodule_path.buf; argv[argc] = submodule_prefix.buf; if (run_command(&cp)) result = 1; } strbuf_release(&submodule_path); strbuf_release(&submodule_git_dir); strbuf_release(&submodule_prefix); } free(argv); return result; }