static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
struct wt_status *s)
{
- unsigned char sha1[20];
+ struct object_id oid;
if (s->relative_paths)
s->prefix = prefix;
s->index_file = index_file;
s->fp = fp;
s->nowarn = nowarn;
- s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+ s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
if (!s->is_initial)
- hashcpy(s->sha1_commit, sha1);
+ hashcpy(s->sha1_commit, oid.hash);
s->status_format = status_format;
s->ignore_submodule_arg = ignore_submodule_arg;
commitable = run_status(s->fp, index_file, prefix, 1, s);
s->use_color = saved_color_setting;
} else {
- unsigned char sha1[20];
+ struct object_id oid;
const char *parent = "HEAD";
if (!active_nr && read_cache() < 0)
if (amend)
parent = "HEAD^1";
- if (get_sha1(parent, sha1)) {
+ if (get_sha1(parent, oid.hash)) {
int i, ita_nr = 0;
for (i = 0; i < active_nr; i++)
{
static struct wt_status s;
int fd;
- unsigned char sha1[20];
+ struct object_id oid;
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT_SET_INT('s', "short", &status_format,
fd = hold_locked_index(&index_lock, 0);
- s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
+ s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
if (!s.is_initial)
- hashcpy(s.sha1_commit, sha1);
+ hashcpy(s.sha1_commit, oid.hash);
s.ignore_submodule_arg = ignore_submodule_arg;
s.status_format = status_format;
static const char *implicit_ident_advice(void)
{
- char *user_config = expand_user_path("~/.gitconfig");
+ char *user_config = expand_user_path("~/.gitconfig", 0);
char *xdg_config = xdg_config_home("config");
int config_exists = file_exists(user_config) || file_exists(xdg_config);
}
-static void print_summary(const char *prefix, const unsigned char *sha1,
+static void print_summary(const char *prefix, const struct object_id *oid,
int initial_commit)
{
struct rev_info rev;
struct commit *commit;
struct strbuf format = STRBUF_INIT;
- unsigned char junk_sha1[20];
+ struct object_id junk_oid;
const char *head;
struct pretty_print_context pctx = {0};
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
- commit = lookup_commit(sha1);
+ commit = lookup_commit(oid->hash);
if (!commit)
die(_("couldn't look up newly created commit"));
if (parse_commit(commit))
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
+ head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
if (!strcmp(head, "HEAD"))
head = _("detached HEAD");
else
return git_status_config(k, v, s);
}
-static int run_rewrite_hook(const unsigned char *oldsha1,
- const unsigned char *newsha1)
+static int run_rewrite_hook(const struct object_id *oldoid,
+ const struct object_id *newoid)
{
struct child_process proc = CHILD_PROCESS_INIT;
const char *argv[3];
code = start_command(&proc);
if (code)
return code;
- strbuf_addf(&sb, "%s %s\n", sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+ strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
sigchain_push(SIGPIPE, SIG_IGN);
write_in_full(proc.in, sb.buf, sb.len);
close(proc.in);
struct strbuf author_ident = STRBUF_INIT;
const char *index_file, *reflog_msg;
char *nl;
- unsigned char sha1[20];
+ struct object_id oid;
struct commit_list *parents = NULL;
struct stat statbuf;
struct commit *current_head = NULL;
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
s.colopts = 0;
- if (get_sha1("HEAD", sha1))
+ if (get_sha1("HEAD", oid.hash))
current_head = NULL;
else {
- current_head = lookup_commit_or_die(sha1, "HEAD");
+ current_head = lookup_commit_or_die(oid.hash, "HEAD");
if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
}
if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
- parents, sha1, author_ident.buf, sign_commit, extra)) {
+ parents, oid.hash, author_ident.buf, sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, "HEAD", sha1,
+ ref_transaction_update(transaction, "HEAD", oid.hash,
current_head
? current_head->object.oid.hash : null_sha1,
0, sb.buf, &err) ||
cfg = init_copy_notes_for_rewrite("amend");
if (cfg) {
/* we are amending, so current_head is not NULL */
- copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1);
+ copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash);
finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
}
- run_rewrite_hook(current_head->object.oid.hash, sha1);
+ run_rewrite_hook(¤t_head->object.oid, &oid);
}
if (!quiet)
- print_summary(prefix, sha1, !current_head);
+ print_summary(prefix, &oid, !current_head);
strbuf_release(&err);
return 0;
}
if (use_global_config) {
- char *user_config = expand_user_path("~/.gitconfig");
+ char *user_config = expand_user_path("~/.gitconfig", 0);
char *xdg_config = xdg_config_home("config");
if (!user_config)
else if (given_config_source.file) {
if (!is_absolute_path(given_config_source.file) && prefix)
given_config_source.file =
- xstrdup(prefix_filename(prefix,
- strlen(prefix),
- given_config_source.file));
+ prefix_filename(prefix, given_config_source.file);
}
if (respect_includes == -1)
#include "trace.h"
#include "string-list.h"
#include "pack-revindex.h"
+#include "hash.h"
-#include SHA1_HEADER
#ifndef platform_SHA_CTX
/*
* platform's underlying implementation of SHA-1; could be OpenSSL,
#define GIT_SHA1_RAWSZ 20
#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
+
struct object_id {
- unsigned char hash[GIT_SHA1_RAWSZ];
+ unsigned char hash[GIT_MAX_RAWSZ];
};
#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
extern struct index_state the_index;
/* Name hashing */
+extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
extern void free_name_hash(struct index_state *istate);
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
+#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
extern void setup_work_tree(void);
+/*
+ * Find GIT_DIR of the repository that contains the current working directory,
+ * without changing the working directory or other global state. The result is
+ * appended to gitdir. The return value is either NULL if no repository was
+ * found, or pointing to the path inside gitdir's buffer.
+ */
+extern const char *discover_git_directory(struct strbuf *gitdir);
extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
extern char *prefix_path(const char *prefix, int len, const char *path);
extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
-extern const char *prefix_filename(const char *prefix, int len, const char *path);
+
+/*
+ * Concatenate "prefix" (if len is non-zero) and "path", with no
+ * connecting characters (so "prefix" should end with a "/").
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ *
+ * The return value is always a newly allocated string (even if the
+ * prefix was empty).
+ */
+extern char *prefix_filename(const char *prefix, const char *path);
+
extern int check_filename(const char *prefix, const char *name);
extern void verify_filename(const char *prefix,
const char *name,
extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
-extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
+extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
extern const struct object_id null_oid;
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
}
-
-int git_mkstemp(char *path, size_t n, const char *template);
-
/* set default permissions by passing mode arguments to open(2) */
int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
int git_mkstemp_mode(char *pattern, int mode);
/*
* Create the directory containing the named path, using care to be
- * somewhat safe against races. Return one of the scld_error values
- * to indicate success/failure.
+ * somewhat safe against races. Return one of the scld_error values to
+ * indicate success/failure. On error, set errno to describe the
+ * problem.
*
* SCLD_VANISHED indicates that one of the ancestor directories of the
* path existed at one point during the function call and then
enum scld_error safe_create_leading_directories(char *path);
enum scld_error safe_create_leading_directories_const(const char *path);
+/*
+ * Callback function for raceproof_create_file(). This function is
+ * expected to do something that makes dirname(path) permanent despite
+ * the fact that other processes might be cleaning up empty
+ * directories at the same time. Usually it will create a file named
+ * path, but alternatively it could create another file in that
+ * directory, or even chdir() into that directory. The function should
+ * return 0 if the action was completed successfully. On error, it
+ * should return a nonzero result and set errno.
+ * raceproof_create_file() treats two errno values specially:
+ *
+ * - ENOENT -- dirname(path) does not exist. In this case,
+ * raceproof_create_file() tries creating dirname(path)
+ * (and any parent directories, if necessary) and calls
+ * the function again.
+ *
+ * - EISDIR -- the file already exists and is a directory. In this
+ * case, raceproof_create_file() removes the directory if
+ * it is empty (and recursively any empty directories that
+ * it contains) and calls the function again.
+ *
+ * Any other errno causes raceproof_create_file() to fail with the
+ * callback's return value and errno.
+ *
+ * Obviously, this function should be OK with being called again if it
+ * fails with ENOENT or EISDIR. In other scenarios it will not be
+ * called again.
+ */
+typedef int create_file_fn(const char *path, void *cb);
+
+/*
+ * Create a file in dirname(path) by calling fn, creating leading
+ * directories if necessary. Retry a few times in case we are racing
+ * with another process that is trying to clean up the directory that
+ * contains path. See the documentation for create_file_fn for more
+ * details.
+ *
+ * Return the value and set the errno that resulted from the most
+ * recent call of fn. fn is always called at least once, and will be
+ * called more than once if it returns ENOENT or EISDIR.
+ */
+int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
+
int mkdir_in_gitdir(const char *path);
- extern char *expand_user_path(const char *path);
+ extern char *expand_user_path(const char *path, int real_home);
const char *enter_repo(const char *path, int strict);
static inline int is_absolute_path(const char *path)
{
int die_on_error);
const char *real_path(const char *path);
const char *real_path_if_valid(const char *path);
-char *real_pathdup(const char *path);
+char *real_pathdup(const char *path, int die_on_error);
const char *absolute_path(const char *path);
char *absolute_pathdup(const char *path);
const char *remove_leading_path(const char *in, const char *prefix);
*/
extern char *xdg_config_home(const char *filename);
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
+ * "$HOME/.cache/git/$filename". Return NULL upon error.
+ */
+extern char *xdg_cache_home(const char *filename);
+
/* object replacement */
#define LOOKUP_REPLACE_OBJECT 1
#define LOOKUP_UNKNOWN_OBJECT 2
extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
+/* Helper to check and "touch" a file */
+extern int check_and_freshen_file(const char *fn, int freshen);
+
extern const signed char hexval_table[256];
static inline unsigned int hexval(unsigned char c)
{
extern int get_oid(const char *str, struct object_id *oid);
-typedef int each_abbrev_fn(const unsigned char *sha1, void *);
+typedef int each_abbrev_fn(const struct object_id *oid, void *);
extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
extern int set_disambiguate_hint_config(const char *var, const char *value);
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 interpret_branch_name(const char *str, int len, struct strbuf *);
+/*
+ * Parse a 40-character hexadecimal object ID starting from hex, updating the
+ * pointer specified by end when parsing stops. The resulting object ID is
+ * stored in oid. Returns 0 on success. Parsing will stop on the first NUL or
+ * other invalid character. end is only updated on success; otherwise, it is
+ * unmodified.
+ */
+extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ * places the name of the branch in the given buf and returns the
+ * number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ * <branch> is configured to merge with (missing <branch> defaults
+ * to the current branch), and places the name of the branch in the
+ * given buf and returns the number of characters parsed if
+ * successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ *
+ * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+ * expansions: local branches ("refs/heads/"), remote branches
+ * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+ * allowed, even ones to refs outside of those namespaces.
+ */
+#define INTERPRET_BRANCH_LOCAL (1<<0)
+#define INTERPRET_BRANCH_REMOTE (1<<1)
+#define INTERPRET_BRANCH_HEAD (1<<2)
+extern int interpret_branch_name(const char *str, int len, struct strbuf *,
+ unsigned allowed);
extern int get_oid_mb(const char *str, struct object_id *oid);
extern int validate_headref(const char *ref);
extern void pack_report(void);
+/*
+ * Create a temporary file rooted in the object database directory, or
+ * die on failure. The filename is taken from "pattern", which should have the
+ * usual "XXXXXX" trailer, and the resulting filename is written into the
+ * "template" buffer. Returns the open descriptor.
+ */
+extern int odb_mkstemp(struct strbuf *template, const char *pattern);
+
+/*
+ * Generate the filename to be used for a pack file with checksum "sha1" and
+ * extension "ext". The result is written into the strbuf "buf", overwriting
+ * any existing contents. A pointer to buf->buf is returned as a convenience.
+ *
+ * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
+ */
+extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+extern int odb_pack_keep(const char *name);
+
/*
* mmap the index file for the specified packfile (if it is not
* already mmapped). Return 0 on success.
* error.
*/
extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
+/*
+ * Like nth_packed_object_sha1, but write the data into the object specified by
+ * the the first argument. Returns the first argument on success, and NULL on
+ * error.
+ */
+extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
/*
* Return the offset of the nth object within the specified packfile.
* scratch buffer, but restored to its original contents before
* the function returns.
*/
-typedef int each_loose_object_fn(const unsigned char *sha1,
+typedef int each_loose_object_fn(const struct object_id *oid,
const char *path,
void *data);
typedef int each_loose_cruft_fn(const char *basename,
* LOCAL_ONLY flag is set).
*/
#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
-typedef int each_packed_object_fn(const unsigned char *sha1,
+typedef int each_packed_object_fn(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
void *data);
const unsigned char *sha1, void *data);
extern void git_config_push_parameter(const char *text);
extern int git_config_from_parameters(config_fn_t fn, void *data);
+extern void read_early_config(config_fn_t cb, void *data);
extern void git_config(config_fn_t fn, void *);
extern int git_config_with_options(config_fn_t fn, void *,
struct git_config_source *config_source,
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 ssize_t git_config_ssize_t(const char *, const char *);
extern int git_config_bool_or_int(const char *, const char *, int *);
extern int git_config_bool(const char *, const char *);
extern int git_config_maybe_bool(const char *, const char *);
*
* (i.e., what gets handed to a config_fn_t). The caller provides the section;
* we return -1 if it does not match, 0 otherwise. The subsection and key
- * out-parameters are filled by the function (and subsection is NULL if it is
+ * out-parameters are filled by the function (and *subsection is NULL if it is
* missing).
+ *
+ * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
+ * there is no subsection at all.
*/
extern int parse_config_key(const char *var,
const char *section,
extern int git_config_get_maybe_bool(const char *key, int *dest);
extern int git_config_get_pathname(const char *key, const char **dest);
extern int git_config_get_untracked_cache(void);
+extern int git_config_get_split_index(void);
+extern int git_config_get_max_percent_split_change(void);
+
+/* This dies if the configured or default date is in the future */
+extern int git_config_get_expiry(const char *key, const char **output);
/*
* This is a hack for test programs like test-dump-untracked-cache to
if (!path)
return config_error_nonbool("include.path");
- expanded = expand_user_path(path);
+ expanded = expand_user_path(path, 0);
if (!expanded)
return error("could not expand include path '%s'", path);
path = expanded;
char *expanded;
int prefix = 0;
- expanded = expand_user_path(pat->buf);
+ expanded = expand_user_path(pat->buf, 1);
if (expanded) {
strbuf_reset(pat);
strbuf_addstr(pat, expanded);
return error(_("relative config include "
"conditionals must come from files"));
- strbuf_add_absolute_path(&path, cf->path);
+ strbuf_realpath(&path, cf->path, 1);
slash = find_last_dir_sep(path.buf);
if (!slash)
die("BUG: how is this possible?");
struct strbuf pattern = STRBUF_INIT;
int ret = 0, prefix;
- strbuf_add_absolute_path(&text, get_git_dir());
+ strbuf_realpath(&text, get_git_dir(), 1);
strbuf_add(&pattern, cond, cond_len);
prefix = prepare_include_condition_pattern(&pattern);
strbuf_release(&env);
}
+static inline int iskeychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+/*
+ * Auxiliary function to sanity-check and split the key into the section
+ * identifier and variable name.
+ *
+ * Returns 0 on success, -1 when there is an invalid character in the key and
+ * -2 if there is no section name in the key.
+ *
+ * store_key - pointer to char* which will hold a copy of the key with
+ * lowercase section and variable name
+ * baselen - pointer to int which will hold the length of the
+ * section + subsection part, can be NULL
+ */
+static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
+{
+ int i, dot, baselen;
+ const char *last_dot = strrchr(key, '.');
+
+ /*
+ * Since "key" actually contains the section name and the real
+ * key name separated by a dot, we have to know where the dot is.
+ */
+
+ if (last_dot == NULL || last_dot == key) {
+ if (!quiet)
+ error("key does not contain a section: %s", key);
+ return -CONFIG_NO_SECTION_OR_NAME;
+ }
+
+ if (!last_dot[1]) {
+ if (!quiet)
+ error("key does not contain variable name: %s", key);
+ return -CONFIG_NO_SECTION_OR_NAME;
+ }
+
+ baselen = last_dot - key;
+ if (baselen_)
+ *baselen_ = baselen;
+
+ /*
+ * Validate the key and while at it, lower case it for matching.
+ */
+ if (store_key)
+ *store_key = xmallocz(strlen(key));
+
+ dot = 0;
+ for (i = 0; key[i]; i++) {
+ unsigned char c = key[i];
+ if (c == '.')
+ dot = 1;
+ /* Leave the extended basename untouched.. */
+ if (!dot || i > baselen) {
+ if (!iskeychar(c) ||
+ (i == baselen + 1 && !isalpha(c))) {
+ if (!quiet)
+ error("invalid key: %s", key);
+ goto out_free_ret_1;
+ }
+ c = tolower(c);
+ } else if (c == '\n') {
+ if (!quiet)
+ error("invalid key (newline): %s", key);
+ goto out_free_ret_1;
+ }
+ if (store_key)
+ (*store_key)[i] = c;
+ }
+
+ return 0;
+
+out_free_ret_1:
+ if (store_key) {
+ free(*store_key);
+ *store_key = NULL;
+ }
+ return -CONFIG_INVALID_KEY;
+}
+
+int git_config_parse_key(const char *key, char **store_key, int *baselen)
+{
+ return git_config_parse_key_1(key, store_key, baselen, 0);
+}
+
+int git_config_key_is_valid(const char *key)
+{
+ return !git_config_parse_key_1(key, NULL, NULL, 1);
+}
+
int git_config_parse_parameter(const char *text,
config_fn_t fn, void *data)
{
const char *value;
+ char *canonical_name;
struct strbuf **pair;
+ int ret;
pair = strbuf_split_str(text, '=', 2);
if (!pair[0])
strbuf_list_free(pair);
return error("bogus config parameter: %s", text);
}
- strbuf_tolower(pair[0]);
- if (fn(pair[0]->buf, value, data) < 0) {
- strbuf_list_free(pair);
- return -1;
+
+ if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) {
+ ret = -1;
+ } else {
+ ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+ free(canonical_name);
}
strbuf_list_free(pair);
- return 0;
+ return ret;
}
int git_config_from_parameters(config_fn_t fn, void *data)
}
}
-static inline int iskeychar(int c)
-{
- return isalnum(c) || c == '-';
-}
-
static int get_value(config_fn_t fn, void *data, struct strbuf *name)
{
int c;
return 1;
}
+static int git_parse_ssize_t(const char *value, ssize_t *ret)
+{
+ intmax_t tmp;
+ if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
+ return 0;
+ *ret = tmp;
+ return 1;
+}
+
NORETURN
static void die_bad_number(const char *name, const char *value)
{
return ret;
}
+ssize_t git_config_ssize_t(const char *name, const char *value)
+{
+ ssize_t ret;
+ if (!git_parse_ssize_t(value, &ret))
+ die_bad_number(name, value);
+ return ret;
+}
+
int git_parse_maybe_bool(const char *value)
{
if (!value)
{
if (!value)
return config_error_nonbool(var);
- *dest = expand_user_path(value);
+ *dest = expand_user_path(value, 0);
if (!*dest)
die(_("failed to expand user dir in: '%s'"), value);
return 0;
{
int ret = 0;
char *xdg_config = xdg_config_home("config");
- char *user_config = expand_user_path("~/.gitconfig");
+ char *user_config = expand_user_path("~/.gitconfig", 0);
char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
current_parsing_scope = CONFIG_SCOPE_SYSTEM;
}
}
+void read_early_config(config_fn_t cb, void *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ git_config_with_options(cb, data, NULL, 1);
+
+ /*
+ * When setup_git_directory() was not yet asked to discover the
+ * GIT_DIR, we ask discover_git_directory() to figure out whether there
+ * is any repository config we should use (but unlike
+ * setup_git_directory_gently(), no global state is changed, most
+ * notably, the current working directory is still the same after the
+ * call).
+ */
+ if (!have_git_dir() && discover_git_directory(&buf)) {
+ struct git_config_source repo_config;
+
+ memset(&repo_config, 0, sizeof(repo_config));
+ strbuf_addstr(&buf, "/config");
+ repo_config.file = buf.buf;
+ git_config_with_options(cb, data, &repo_config, 1);
+ }
+ strbuf_release(&buf);
+}
+
static void git_config_check_init(void);
void git_config(config_fn_t fn, void *data)
return ret;
}
+int git_config_get_expiry(const char *key, const char **output)
+{
+ int ret = git_config_get_string_const(key, output);
+ if (ret)
+ return ret;
+ if (strcmp(*output, "now")) {
+ unsigned long now = approxidate("now");
+ if (approxidate(*output) >= now)
+ git_die_config(key, _("Invalid %s: '%s'"), key, *output);
+ }
+ return ret;
+}
+
int git_config_get_untracked_cache(void)
{
int val = -1;
if (!strcasecmp(v, "keep"))
return -1;
- error("unknown core.untrackedCache value '%s'; "
- "using 'keep' default value", v);
+ error(_("unknown core.untrackedCache value '%s'; "
+ "using 'keep' default value"), v);
return -1;
}
return -1; /* default value */
}
+int git_config_get_split_index(void)
+{
+ int val;
+
+ if (!git_config_get_maybe_bool("core.splitindex", &val))
+ return val;
+
+ return -1; /* default value */
+}
+
+int git_config_get_max_percent_split_change(void)
+{
+ int val = -1;
+
+ if (!git_config_get_int("splitindex.maxpercentchange", &val)) {
+ if (0 <= val && val <= 100)
+ return val;
+
+ return error(_("splitIndex.maxPercentChange value '%d' "
+ "should be between 0 and 100"), val);
+ }
+
+ return -1; /* default value */
+}
+
NORETURN
void git_die_config_linenr(const char *key, const char *filename, int linenr)
{
git_config_set_multivar(key, value, NULL, 0);
}
-/*
- * Auxiliary function to sanity-check and split the key into the section
- * identifier and variable name.
- *
- * Returns 0 on success, -1 when there is an invalid character in the key and
- * -2 if there is no section name in the key.
- *
- * store_key - pointer to char* which will hold a copy of the key with
- * lowercase section and variable name
- * baselen - pointer to int which will hold the length of the
- * section + subsection part, can be NULL
- */
-static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
-{
- int i, dot, baselen;
- const char *last_dot = strrchr(key, '.');
-
- /*
- * Since "key" actually contains the section name and the real
- * key name separated by a dot, we have to know where the dot is.
- */
-
- if (last_dot == NULL || last_dot == key) {
- if (!quiet)
- error("key does not contain a section: %s", key);
- return -CONFIG_NO_SECTION_OR_NAME;
- }
-
- if (!last_dot[1]) {
- if (!quiet)
- error("key does not contain variable name: %s", key);
- return -CONFIG_NO_SECTION_OR_NAME;
- }
-
- baselen = last_dot - key;
- if (baselen_)
- *baselen_ = baselen;
-
- /*
- * Validate the key and while at it, lower case it for matching.
- */
- if (store_key)
- *store_key = xmallocz(strlen(key));
-
- dot = 0;
- for (i = 0; key[i]; i++) {
- unsigned char c = key[i];
- if (c == '.')
- dot = 1;
- /* Leave the extended basename untouched.. */
- if (!dot || i > baselen) {
- if (!iskeychar(c) ||
- (i == baselen + 1 && !isalpha(c))) {
- if (!quiet)
- error("invalid key: %s", key);
- goto out_free_ret_1;
- }
- c = tolower(c);
- } else if (c == '\n') {
- if (!quiet)
- error("invalid key (newline): %s", key);
- goto out_free_ret_1;
- }
- if (store_key)
- (*store_key)[i] = c;
- }
-
- return 0;
-
-out_free_ret_1:
- if (store_key) {
- free(*store_key);
- *store_key = NULL;
- }
- return -CONFIG_INVALID_KEY;
-}
-
-int git_config_parse_key(const char *key, char **store_key, int *baselen)
-{
- return git_config_parse_key_1(key, store_key, baselen, 0);
-}
-
-int git_config_key_is_valid(const char *key)
-{
- return !git_config_parse_key_1(key, NULL, NULL, 1);
-}
-
/*
* If value==NULL, unset in (remove from) config,
* if value_regex!=NULL, disregard key/value pairs where value does not match.
const char **subsection, int *subsection_len,
const char **key)
{
- int section_len = strlen(section);
const char *dot;
/* Does it start with "section." ? */
- if (!starts_with(var, section) || var[section_len] != '.')
+ if (!skip_prefix(var, section, &var) || *var != '.')
return -1;
/*
*key = dot + 1;
/* Did we have a subsection at all? */
- if (dot == var + section_len) {
- *subsection = NULL;
- *subsection_len = 0;
+ if (dot == var) {
+ if (subsection) {
+ *subsection = NULL;
+ *subsection_len = 0;
+ }
}
else {
- *subsection = var + section_len + 1;
+ if (!subsection)
+ return -1;
+ *subsection = var + 1;
*subsection_len = dot - *subsection;
}
strbuf_release(&buf);
}
- old_dir = expand_user_path("~/.git-credential-cache");
+static char *get_socket_path(void)
+{
+ struct stat sb;
+ char *old_dir, *socket;
++ old_dir = expand_user_path("~/.git-credential-cache", 0);
+ if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode))
+ socket = xstrfmt("%s/socket", old_dir);
+ else
+ socket = xdg_cache_home("credential/socket");
+ free(old_dir);
+ return socket;
+}
+
int cmd_main(int argc, const char **argv)
{
char *socket_path = NULL;
op = argv[0];
if (!socket_path)
- socket_path = expand_user_path("~/.git-credential-cache/socket", 0);
+ socket_path = get_socket_path();
if (!socket_path)
die("unable to find a suitable socket path; use --socket");
}
/* Returns 0 on success, negative on failure. */
-#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1
static int do_submodule_path(struct strbuf *buf, const char *path,
const char *fmt, va_list args)
{
- const char *git_dir;
struct strbuf git_submodule_common_dir = STRBUF_INIT;
struct strbuf git_submodule_dir = STRBUF_INIT;
- const struct submodule *sub;
- int err = 0;
+ int ret;
- strbuf_addstr(buf, path);
- strbuf_complete(buf, '/');
- strbuf_addstr(buf, ".git");
-
- git_dir = read_gitfile(buf->buf);
- if (git_dir) {
- strbuf_reset(buf);
- strbuf_addstr(buf, git_dir);
- }
- if (!is_git_directory(buf->buf)) {
- gitmodules_config();
- sub = submodule_from_path(null_sha1, path);
- if (!sub) {
- err = SUBMODULE_PATH_ERR_NOT_CONFIGURED;
- goto cleanup;
- }
- strbuf_reset(buf);
- strbuf_git_path(buf, "%s/%s", "modules", sub->name);
- }
-
- strbuf_addch(buf, '/');
- strbuf_addbuf(&git_submodule_dir, buf);
+ ret = submodule_to_gitdir(&git_submodule_dir, path);
+ if (ret)
+ goto cleanup;
+ strbuf_complete(&git_submodule_dir, '/');
+ strbuf_addbuf(buf, &git_submodule_dir);
strbuf_vaddf(buf, fmt, args);
if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))
cleanup:
strbuf_release(&git_submodule_dir);
strbuf_release(&git_submodule_common_dir);
-
- return err;
+ return ret;
}
char *git_pathdup_submodule(const char *path, const char *fmt, ...)
* Return a string with ~ and ~user expanded via getpw*. If buf != NULL,
* then it is a newly allocated string. Returns NULL on getpw failure or
* if path is NULL.
+ *
+ * If real_home is true, real_path($HOME) is used in the expansion.
*/
- char *expand_user_path(const char *path)
+ char *expand_user_path(const char *path, int real_home)
{
struct strbuf user_path = STRBUF_INIT;
const char *to_copy = path;
const char *home = getenv("HOME");
if (!home)
goto return_null;
- strbuf_addstr(&user_path, home);
+ if (real_home)
+ strbuf_addstr(&user_path, real_path(home));
+ else
+ strbuf_addstr(&user_path, home);
#ifdef GIT_WINDOWS_NATIVE
convert_slashes(user_path.buf);
#endif
strbuf_add(&validated_path, path, len);
if (used_path.buf[0] == '~') {
- char *newpath = expand_user_path(used_path.buf);
+ char *newpath = expand_user_path(used_path.buf, 0);
if (!newpath)
return NULL;
strbuf_attach(&used_path, newpath, strlen(newpath),
return NULL;
}
+char *xdg_cache_home(const char *filename)
+{
+ const char *home, *cache_home;
+
+ assert(filename);
+ cache_home = getenv("XDG_CACHE_HOME");
+ if (cache_home && *cache_home)
+ return mkpathdup("%s/git/%s", cache_home, filename);
+
+ home = getenv("HOME");
+ if (home)
+ return mkpathdup("%s/.cache/git/%s", home, filename);
+ return NULL;
+}
+
GIT_PATH_FUNC(git_path_cherry_pick_head, "CHERRY_PICK_HEAD")
GIT_PATH_FUNC(git_path_revert_head, "REVERT_HEAD")
GIT_PATH_FUNC(git_path_squash_msg, "SQUASH_MSG")