*/
#include "builtin.h"
+#include "tempfile.h"
#include "lockfile.h"
#include "parse-options.h"
#include "run-command.h"
static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
static struct argv_array rerere = ARGV_ARRAY_INIT;
-static char *pidfile;
-
-static void remove_pidfile(void)
-{
- if (pidfile)
- unlink(pidfile);
-}
-
-static void remove_pidfile_on_signal(int signo)
-{
- remove_pidfile();
- sigchain_pop(signo);
- raise(signo);
-}
+static struct tempfile pidfile;
static void git_config_date_string(const char *key, const char **output)
{
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
git_config_get_bool("gc.autodetach", &detach_auto);
git_config_date_string("gc.pruneexpire", &prune_expire);
- git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire);
+ git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
git_config(git_default_config, NULL);
}
uintmax_t pid;
FILE *fp;
int fd;
+ char *pidfile_path;
- if (pidfile)
+ if (is_tempfile_active(&pidfile))
/* already locked */
return NULL;
if (gethostname(my_host, sizeof(my_host)))
strcpy(my_host, "unknown");
- fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
+ pidfile_path = git_pathdup("gc.pid");
+ fd = hold_lock_file_for_update(&lock, pidfile_path,
LOCK_DIE_ON_ERROR);
if (!force) {
static char locking_host[128];
int should_exit;
- fp = fopen(git_path("gc.pid"), "r");
+ fp = fopen(pidfile_path, "r");
memset(locking_host, 0, sizeof(locking_host));
should_exit =
fp != NULL &&
if (fd >= 0)
rollback_lock_file(&lock);
*ret_pid = pid;
+ free(pidfile_path);
return locking_host;
}
}
write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
commit_lock_file(&lock);
-
- pidfile = git_pathdup("gc.pid");
- sigchain_push_common(remove_pidfile_on_signal);
- atexit(remove_pidfile);
-
+ register_tempfile(&pidfile, pidfile_path);
+ free(pidfile_path);
return NULL;
}
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
argv_array_pushl(&prune, "prune", "--expire", NULL);
- argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL);
+ argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
gc_config();
if (gc_before_repack())
return -1;
- if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, repack.argv[0]);
-
- if (prune_expire) {
- argv_array_push(&prune, prune_expire);
- if (quiet)
- argv_array_push(&prune, "--no-progress");
- if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, prune.argv[0]);
+ if (!repository_format_precious_objects) {
+ if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, repack.argv[0]);
+
+ if (prune_expire) {
+ argv_array_push(&prune, prune_expire);
+ if (quiet)
+ argv_array_push(&prune, "--no-progress");
+ if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, prune.argv[0]);
+ }
}
if (prune_worktrees_expire) {
#include "reachable.h"
#include "parse-options.h"
#include "progress.h"
-#include "dir.h"
static const char * const prune_usage[] = {
N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
return 0;
}
-static int prune_worktree(const char *id, struct strbuf *reason)
-{
- struct stat st;
- char *path;
- int fd, len;
-
- if (!is_directory(git_path("worktrees/%s", id))) {
- strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
- return 1;
- }
- if (file_exists(git_path("worktrees/%s/locked", id)))
- return 0;
- if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
- strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
- return 1;
- }
- fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
- if (fd < 0) {
- strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
- id, strerror(errno));
- return 1;
- }
- len = st.st_size;
- path = xmalloc(len + 1);
- read_in_full(fd, path, len);
- close(fd);
- while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
- len--;
- if (!len) {
- strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
- free(path);
- return 1;
- }
- path[len] = '\0';
- if (!file_exists(path)) {
- struct stat st_link;
- free(path);
- /*
- * the repo is moved manually and has not been
- * accessed since?
- */
- if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
- st_link.st_nlink > 1)
- return 0;
- if (st.st_mtime <= expire) {
- strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
- return 1;
- } else {
- return 0;
- }
- }
- free(path);
- return 0;
-}
-
-static void prune_worktrees(void)
-{
- struct strbuf reason = STRBUF_INIT;
- struct strbuf path = STRBUF_INIT;
- DIR *dir = opendir(git_path("worktrees"));
- struct dirent *d;
- int ret;
- if (!dir)
- return;
- while ((d = readdir(dir)) != NULL) {
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
- continue;
- strbuf_reset(&reason);
- if (!prune_worktree(d->d_name, &reason))
- continue;
- if (show_only || verbose)
- printf("%s\n", reason.buf);
- if (show_only)
- continue;
- strbuf_reset(&path);
- strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
- ret = remove_dir_recursively(&path, 0);
- if (ret < 0 && errno == ENOTDIR)
- ret = unlink(path.buf);
- if (ret)
- error(_("failed to remove: %s"), strerror(errno));
- }
- closedir(dir);
- if (!show_only)
- rmdir(git_path("worktrees"));
- strbuf_release(&reason);
- strbuf_release(&path);
-}
-
/*
* Write errors (particularly out of space) can result in
* failed temporary packs (and more rarely indexes and other
{
struct rev_info revs;
struct progress *progress = NULL;
- int do_prune_worktrees = 0;
const struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned objects")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
- OPT_BOOL(0, "worktrees", &do_prune_worktrees, N_("prune .git/worktrees")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire objects older than <time>")),
OPT_END()
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
- if (do_prune_worktrees) {
- if (argc)
- die(_("--worktrees does not take extra arguments"));
- prune_worktrees();
- return 0;
- }
-
+ if (repository_format_precious_objects)
+ die(_("cannot prune in a precious-objects repo"));
+
while (argc--) {
unsigned char sha1[20];
const char *name = *argv++;
argc = parse_options(argc, argv, prefix, builtin_repack_options,
git_repack_usage, 0);
+ if (delete_redundant && repository_format_precious_objects)
+ die(_("cannot delete packs in a precious-objects repo"));
+
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps;
failed = 0;
for_each_string_list_item(item, &names) {
for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
- const char *fname_old;
- char *fname;
+ char *fname, *fname_old;
fname = mkpathdup("%s/pack-%s%s", packdir,
item->string, exts[ext].name);
if (!file_exists(fname)) {
continue;
}
- fname_old = mkpath("%s/old-%s%s", packdir,
+ fname_old = mkpathdup("%s/old-%s%s", packdir,
item->string, exts[ext].name);
if (file_exists(fname_old))
if (unlink(fname_old))
if (!failed && rename(fname, fname_old)) {
free(fname);
+ free(fname_old);
failed = 1;
break;
} else {
string_list_append(&rollback, fname);
+ free(fname_old);
}
}
if (failed)
if (failed) {
struct string_list rollback_failure = STRING_LIST_INIT_DUP;
for_each_string_list_item(item, &rollback) {
- const char *fname_old;
- char *fname;
+ char *fname, *fname_old;
fname = mkpathdup("%s/%s", packdir, item->string);
- fname_old = mkpath("%s/old-%s", packdir, item->string);
+ fname_old = mkpathdup("%s/old-%s", packdir, item->string);
if (rename(fname_old, fname))
string_list_append(&rollback_failure, fname);
free(fname);
+ free(fname_old);
}
if (rollback_failure.nr) {
/* Remove the "old-" files */
for_each_string_list_item(item, &names) {
for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
- const char *fname;
- fname = mkpath("%s/old-%s%s",
- packdir,
- item->string,
- exts[ext].name);
+ char *fname;
+ fname = mkpathdup("%s/old-%s%s",
+ packdir,
+ item->string,
+ exts[ext].name);
if (remove_path(fname))
warning(_("removing '%s' failed"), fname);
+ free(fname);
}
}
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
+#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
#define GITATTRIBUTES_FILE ".gitattributes"
#define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
extern const char *get_git_namespace(void);
extern const char *strip_namespace(const char *namespaced_ref);
extern const char *get_git_work_tree(void);
-extern const char *read_gitfile(const char *path);
+
+#define READ_GITFILE_ERR_STAT_FAILED 1
+#define READ_GITFILE_ERR_NOT_A_FILE 2
+#define READ_GITFILE_ERR_OPEN_FAILED 3
+#define READ_GITFILE_ERR_READ_FAILED 4
+#define READ_GITFILE_ERR_INVALID_FORMAT 5
+#define READ_GITFILE_ERR_NO_PATH 6
+#define READ_GITFILE_ERR_NOT_A_REPO 7
+#define READ_GITFILE_ERR_TOO_LARGE 8
+extern const char *read_gitfile_gently(const char *path, int *return_error_code);
+#define read_gitfile(path) read_gitfile_gently((path), NULL)
extern const char *resolve_gitdir(const char *suspect);
extern void set_git_work_tree(const char *tree);
extern int hold_locked_index(struct lock_file *, int);
extern void set_alternate_index_output(const char *);
-extern int delete_ref(const char *, const unsigned char *sha1, unsigned int flags);
-
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
extern int trust_ctime;
* been sought but there were none.
*/
extern int check_replace_refs;
+extern char *git_replace_ref_base;
extern int fsync_object_files;
extern int core_preload_index;
extern int grafts_replace_parents;
+ /*
+ * GIT_REPO_VERSION is the version we write by default. The
+ * _READ variant is the highest number we know how to
+ * handle.
+ */
#define GIT_REPO_VERSION 0
+ #define GIT_REPO_VERSION_READ 1
extern int repository_format_version;
+ extern int repository_format_precious_objects;
extern int check_repository_format(void);
#define MTIME_CHANGED 0x0001
#define DATA_CHANGED 0x0020
#define TYPE_CHANGED 0x0040
+/*
+ * Return a statically allocated filename, either generically (mkpath), in
+ * the repository directory (git_path), or in a submodule's repository
+ * directory (git_path_submodule). In all cases, note that the result
+ * may be overwritten by another call to _any_ of the functions. Consider
+ * using the safer "dup" or "strbuf" formats below (in some cases, the
+ * unsafe versions have already been removed).
+ */
+extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
+extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
extern char *git_pathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
extern char *mkpathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
-
-/* Return a statically allocated filename matching the sha1 signature */
-extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern const char *git_path_submodule(const char *path, const char *fmt, ...)
+extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
+
extern void report_linked_checkout_garbage(void);
+/*
+ * You can define a static memoized git path like:
+ *
+ * static GIT_PATH_FUNC(git_path_foo, "FOO");
+ *
+ * or use one of the global ones below.
+ */
+#define GIT_PATH_FUNC(func, filename) \
+ const char *func(void) \
+ { \
+ static char *ret; \
+ if (!ret) \
+ ret = git_pathdup(filename); \
+ return ret; \
+ }
+
+const char *git_path_cherry_pick_head(void);
+const char *git_path_revert_head(void);
+const char *git_path_squash_msg(void);
+const char *git_path_merge_msg(void);
+const char *git_path_merge_rr(void);
+const char *git_path_merge_mode(void);
+const char *git_path_merge_head(void);
+const char *git_path_fetch_head(void);
+const char *git_path_shallow(void);
+
/*
* Return the name of the file in the local object database that would
* be used to store a loose object with the specified sha1. The
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
-extern int move_temp_to_file(const char *tmpfile, const char *filename);
+extern int finalize_object_file(const char *tmpfile, const char *filename);
extern int has_sha1_pack(const unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
-extern int read_ref_full(const char *refname, int resolve_flags,
- unsigned char *sha1, int *flags);
-extern int read_ref(const char *refname, unsigned char *sha1);
-/*
- * Resolve a reference, recursively following symbolic refererences.
- *
- * Store the referred-to object's name in sha1 and return the name of
- * the non-symbolic reference that ultimately pointed at it. The
- * return value, if not NULL, is a pointer into either a static buffer
- * or the input ref.
- *
- * If the reference cannot be resolved to an object, the behavior
- * depends on the RESOLVE_REF_READING flag:
- *
- * - If RESOLVE_REF_READING is set, return NULL.
- *
- * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
- * the last reference name in the chain, which will either be a non-symbolic
- * reference or an undefined reference. If this is a prelude to
- * "writing" to the ref, the return value is the name of the ref
- * that will actually be created or changed.
- *
- * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
- * level of symbolic reference. The value stored in sha1 for a symbolic
- * reference will always be null_sha1 in this case, and the return
- * value is the reference that the symref refers to directly.
- *
- * If flags is non-NULL, set the value that it points to the
- * combination of REF_ISPACKED (if the reference was found among the
- * packed references), REF_ISSYMREF (if the initial reference was a
- * symbolic reference), REF_BAD_NAME (if the reference name is ill
- * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
- * (if the ref is malformed or has a bad name). See refs.h for more detail
- * on each flag.
- *
- * If ref is not a properly-formatted, normalized reference, return
- * NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
- * give up and return NULL.
- *
- * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
- * name is invalid according to git-check-ref-format(1). If the name
- * is bad then the value stored in sha1 will be null_sha1 and the two
- * flags REF_ISBROKEN and REF_BAD_NAME will be set.
- *
- * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
- * directory and do not consist of all caps and underscores cannot be
- * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the special refs, such as HEAD,
- * FETCH_HEAD and friends, that all live outside of the refs/ directory.
- */
-#define RESOLVE_REF_READING 0x01
-#define RESOLVE_REF_NO_RECURSE 0x02
-#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
-extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
-extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
-
-extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
-extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
extern int interpret_branch_name(const char *str, int len, struct strbuf *);
extern int get_sha1_mb(const char *str, unsigned char *sha1);
-/*
- * Return true iff abbrev_name is a possible abbreviation for
- * full_name according to the rules defined by ref_rev_parse_rules in
- * refs.c.
- */
-extern int refname_match(const char *abbrev_name, const char *full_name);
-
-extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
extern int validate_headref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern struct object *peel_to_type(const char *name, int namelen,
struct object *o, enum object_type);
-enum date_mode {
- DATE_NORMAL = 0,
- DATE_RELATIVE,
- DATE_SHORT,
- DATE_LOCAL,
- DATE_ISO8601,
- DATE_ISO8601_STRICT,
- DATE_RFC2822,
- DATE_RAW
+struct date_mode {
+ enum date_mode_type {
+ DATE_NORMAL = 0,
+ DATE_RELATIVE,
+ DATE_SHORT,
+ DATE_LOCAL,
+ DATE_ISO8601,
+ DATE_ISO8601_STRICT,
+ DATE_RFC2822,
+ DATE_STRFTIME,
+ DATE_RAW
+ } type;
+ const char *strftime_fmt;
};
-const char *show_date(unsigned long time, int timezone, enum date_mode mode);
+/*
+ * Convenience helper for passing a constant type, like:
+ *
+ * show_date(t, tz, DATE_MODE(NORMAL));
+ */
+#define DATE_MODE(t) date_mode_from_type(DATE_##t)
+struct date_mode *date_mode_from_type(enum date_mode_type type);
+
+const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
void show_date_relative(unsigned long time, int tz, const struct timeval *now,
struct strbuf *timebuf);
int parse_date(const char *date, struct strbuf *out);
#define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
unsigned long approxidate_relative(const char *date, const struct timeval *now);
-enum date_mode parse_date_format(const char *format);
+void parse_date_format(const char *format, struct date_mode *mode);
int date_overflows(unsigned long date);
#define IDENT_STRICT 1
* the ident_split. It will also sanity-check the values and produce
* a well-known sentinel date if they appear bogus.
*/
-const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
+const char *show_ident_date(const struct ident_split *id,
+ const struct date_mode *mode);
/*
* Compare split idents for equality or strict ordering. Note that we
int respect_includes);
extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
extern int git_parse_ulong(const char *, unsigned long *);
+extern int git_parse_maybe_bool(const char *);
extern int git_config_int(const char *, const char *);
extern int64_t git_config_int64(const char *, const char *);
extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_set_in_file(const char *, const char *, const char *);
extern int git_config_set(const char *, const char *);
extern int git_config_parse_key(const char *, char **, int *);
+extern int git_config_key_is_valid(const char *key);
extern int git_config_set_multivar(const char *, const char *, const char *, int);
extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
extern int git_config_rename_section(const char *, const char *);
{
return write_in_full(fd, str, strlen(str));
}
-__attribute__((format (printf, 3, 4)))
-extern int write_file(const char *path, int fatal, const char *fmt, ...);
+
+extern int write_file(const char *path, const char *fmt, ...);
+extern int write_file_gently(const char *path, const char *fmt, ...);
/* pager.c */
extern void setup_pager(void);
int warn_on_object_refname_ambiguity = 1;
int ref_paranoia = -1;
int repository_format_version;
+ int repository_format_precious_objects;
const char *git_commit_encoding;
const char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *excludes_file;
enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
int check_replace_refs = 1;
+char *git_replace_ref_base;
enum eol core_eol = EOL_UNSET;
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
GRAFT_ENVIRONMENT,
INDEX_ENVIRONMENT,
NO_REPLACE_OBJECTS_ENVIRONMENT,
+ GIT_REPLACE_REF_BASE_ENVIRONMENT,
GIT_PREFIX_ENVIRONMENT,
GIT_SHALLOW_FILE_ENVIRONMENT,
GIT_COMMON_DIR_ENVIRONMENT,
struct strbuf sb = STRBUF_INIT;
const char *gitfile;
const char *shallow_file;
+ const char *replace_ref_base;
git_dir = getenv(GIT_DIR_ENVIRONMENT);
if (!git_dir)
"info/grafts", &git_graft_env);
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
check_replace_refs = 0;
+ replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
+ git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
+ : "refs/replace/");
namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
namespace_len = strlen(namespace);
shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
}
git_work_tree_initialized = 1;
work_tree = xstrdup(real_path(new_work_tree));
+ if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1))
+ die("could not set GIT_WORK_TREE to '%s'", work_tree);
}
const char *get_git_work_tree(void)
static int inside_git_dir = -1;
static int inside_work_tree = -1;
static int work_tree_config_is_bogus;
+ static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;
/*
* The input parameter must contain an absolute path, and it must already be
static int check_repo_format(const char *var, const char *value, void *cb)
{
+ const char *ext;
+
if (strcmp(var, "core.repositoryformatversion") == 0)
repository_format_version = git_config_int(var, value);
else if (strcmp(var, "core.sharedrepository") == 0)
shared_repository = git_config_perm(var, value);
+ else if (skip_prefix(var, "extensions.", &ext)) {
+ /*
+ * record any known extensions here; otherwise,
+ * we fall through to recording it as unknown, and
+ * check_repository_format will complain
+ */
+ if (!strcmp(ext, "noop"))
+ ;
+ else if (!strcmp(ext, "preciousobjects"))
+ repository_format_precious_objects = git_config_bool(var, value);
+ else
+ string_list_append(&unknown_extensions, ext);
+ }
return 0;
}
config_fn_t fn;
int ret = 0;
+ string_list_clear(&unknown_extensions, 0);
+
if (get_common_dir(&sb, gitdir))
fn = check_repo_format;
else
* is a good one.
*/
git_config_early(fn, NULL, repo_config);
- if (GIT_REPO_VERSION < repository_format_version) {
+ if (GIT_REPO_VERSION_READ < repository_format_version) {
if (!nongit_ok)
die ("Expected git repo version <= %d, found %d",
- GIT_REPO_VERSION, repository_format_version);
+ GIT_REPO_VERSION_READ, repository_format_version);
warning("Expected git repo version <= %d, found %d",
- GIT_REPO_VERSION, repository_format_version);
+ GIT_REPO_VERSION_READ, repository_format_version);
warning("Please upgrade Git");
*nongit_ok = -1;
ret = -1;
}
+
+ if (repository_format_version >= 1 && unknown_extensions.nr) {
+ int i;
+
+ if (!nongit_ok)
+ die("unknown repository extension: %s",
+ unknown_extensions.items[0].string);
+
+ for (i = 0; i < unknown_extensions.nr; i++)
+ warning("unknown repository extension: %s",
+ unknown_extensions.items[i].string);
+ *nongit_ok = -1;
+ ret = -1;
+ }
+
strbuf_release(&sb);
return ret;
}
struct strbuf path = STRBUF_INIT;
struct stat st;
- strbuf_addf(&path, "%s/gitfile", gitdir);
+ strbuf_addf(&path, "%s/gitdir", gitdir);
if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
- write_file(path.buf, 0, "%s\n", gitfile);
+ write_file(path.buf, "%s", gitfile);
strbuf_release(&path);
}
/*
* Try to read the location of the git directory from the .git file,
* return path to git directory if found.
+ *
+ * On failure, if return_error_code is not NULL, return_error_code
+ * will be set to an error code and NULL will be returned. If
+ * return_error_code is NULL the function will die instead (for most
+ * cases).
*/
-const char *read_gitfile(const char *path)
+const char *read_gitfile_gently(const char *path, int *return_error_code)
{
- char *buf;
- char *dir;
+ const int max_file_size = 1 << 20; /* 1MB */
+ int error_code = 0;
+ char *buf = NULL;
+ char *dir = NULL;
const char *slash;
struct stat st;
int fd;
ssize_t len;
- if (stat(path, &st))
- return NULL;
- if (!S_ISREG(st.st_mode))
- return NULL;
+ if (stat(path, &st)) {
+ error_code = READ_GITFILE_ERR_STAT_FAILED;
+ goto cleanup_return;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ error_code = READ_GITFILE_ERR_NOT_A_FILE;
+ goto cleanup_return;
+ }
+ if (st.st_size > max_file_size) {
+ error_code = READ_GITFILE_ERR_TOO_LARGE;
+ goto cleanup_return;
+ }
fd = open(path, O_RDONLY);
- if (fd < 0)
- die_errno("Error opening '%s'", path);
+ if (fd < 0) {
+ error_code = READ_GITFILE_ERR_OPEN_FAILED;
+ goto cleanup_return;
+ }
buf = xmalloc(st.st_size + 1);
len = read_in_full(fd, buf, st.st_size);
close(fd);
- if (len != st.st_size)
- die("Error reading %s", path);
+ if (len != st.st_size) {
+ error_code = READ_GITFILE_ERR_READ_FAILED;
+ goto cleanup_return;
+ }
buf[len] = '\0';
- if (!starts_with(buf, "gitdir: "))
- die("Invalid gitfile format: %s", path);
+ if (!starts_with(buf, "gitdir: ")) {
+ error_code = READ_GITFILE_ERR_INVALID_FORMAT;
+ goto cleanup_return;
+ }
while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
len--;
- if (len < 9)
- die("No path in gitfile: %s", path);
+ if (len < 9) {
+ error_code = READ_GITFILE_ERR_NO_PATH;
+ goto cleanup_return;
+ }
buf[len] = '\0';
dir = buf + 8;
free(buf);
buf = dir;
}
-
- if (!is_git_directory(dir))
- die("Not a git repository: %s", dir);
-
+ if (!is_git_directory(dir)) {
+ error_code = READ_GITFILE_ERR_NOT_A_REPO;
+ goto cleanup_return;
+ }
update_linked_gitdir(path, dir);
path = real_path(dir);
+cleanup_return:
+ if (return_error_code)
+ *return_error_code = error_code;
+ else if (error_code) {
+ switch (error_code) {
+ case READ_GITFILE_ERR_STAT_FAILED:
+ case READ_GITFILE_ERR_NOT_A_FILE:
+ /* non-fatal; follow return path */
+ break;
+ case READ_GITFILE_ERR_OPEN_FAILED:
+ die_errno("Error opening '%s'", path);
+ case READ_GITFILE_ERR_TOO_LARGE:
+ die("Too large to be a .git file: '%s'", path);
+ case READ_GITFILE_ERR_READ_FAILED:
+ die("Error reading %s", path);
+ case READ_GITFILE_ERR_INVALID_FORMAT:
+ die("Invalid gitfile format: %s", path);
+ case READ_GITFILE_ERR_NO_PATH:
+ die("No path in gitfile: %s", path);
+ case READ_GITFILE_ERR_NOT_A_REPO:
+ die("Not a git repository: %s", dir);
+ default:
+ assert(0);
+ }
+ }
+
free(buf);
- return path;
+ return error_code ? NULL : path;
}
static const char *setup_explicit_git_dir(const char *gitdirenv,