Though, one has to be careful about the fact that str* functions often
stop on NULs and that strbufs may have embedded NULs.
-An strbuf is NUL terminated for convenience, but no function in the
+A strbuf is NUL terminated for convenience, but no function in the
strbuf API actually relies on the string being free of NULs.
-strbufs has some invariants that are very important to keep in mind:
+strbufs have some invariants that are very important to keep in mind:
. The `buf` member is never NULL, so it can be used in any usual C
string operations safely. strbuf's _have_ to be initialized either by
* `struct strbuf`
This is the string buffer structure. The `len` member can be used to
-determine the current length of the string, and `buf` member provides access to
-the string itself.
+determine the current length of the string, and `buf` member provides
+access to the string itself.
Functions
---------
* Related to the contents of the buffer
+`strbuf_trim`::
+
+ Strip whitespace from the beginning and end of a string.
+ Equivalent to performing `strbuf_rtrim()` followed by `strbuf_ltrim()`.
+
`strbuf_rtrim`::
Strip whitespace from the end of a string.
+`strbuf_ltrim`::
+
+ Strip whitespace from the beginning of a string.
+
+`strbuf_reencode`::
+
+ Replace the contents of the strbuf with a reencoded form. Returns -1
+ on error, 0 on success.
+
+`strbuf_tolower`::
+
+ Lowercase each character in the buffer using `tolower`.
+
`strbuf_cmp`::
Compare two buffers. Returns an integer less than, equal to, or greater
`strbuf_addbuf`::
- Copy the contents of an other buffer at the end of the current one.
+ Copy the contents of another buffer at the end of the current one.
`strbuf_adddup`::
use it unless you need the correct position in the file
descriptor.
+ `strbuf_getcwd`::
+
+ Set the buffer to the path of the current working directory.
+
+ `strbuf_add_absolute_path`
+
+ Add a path to a buffer, converting a relative path to an
+ absolute one in the process. Symbolic links are not
+ resolved.
+
`stripspace`::
Strip whitespace from a buffer. The second parameter controls if
* moving the target repo later on in separate_git_dir()
*/
git_link = xstrdup(real_path(git_dir));
+ set_git_dir(real_path(real_git_dir));
}
else {
- real_git_dir = real_path(git_dir);
+ set_git_dir(real_path(git_dir));
git_link = NULL;
}
- set_git_dir(real_path(real_git_dir));
return 0;
}
static int guess_repository_type(const char *git_dir)
{
- char cwd[PATH_MAX];
const char *slash;
+ char *cwd;
+ int cwd_is_git_dir;
/*
* "GIT_DIR=. git init" is always bare.
*/
if (!strcmp(".", git_dir))
return 1;
- if (!getcwd(cwd, sizeof(cwd)))
- die_errno(_("cannot tell cwd"));
- if (!strcmp(git_dir, cwd))
+ cwd = xgetcwd();
+ cwd_is_git_dir = !strcmp(git_dir, cwd);
+ free(cwd);
+ if (cwd_is_git_dir)
return 1;
/*
* "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
usage(init_db_usage[0]);
}
if (is_bare_repository_cfg == 1) {
- static char git_dir[PATH_MAX+1];
-
- setenv(GIT_DIR_ENVIRONMENT,
- getcwd(git_dir, sizeof(git_dir)), argc > 0);
+ char *cwd = xgetcwd();
+ setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0);
+ free(cwd);
}
if (init_shared_repository != -1)
git_work_tree_cfg = xstrdup(real_path(rel));
free(rel);
}
- if (!git_work_tree_cfg) {
- git_work_tree_cfg = xcalloc(PATH_MAX, 1);
- if (!getcwd(git_work_tree_cfg, PATH_MAX))
- die_errno (_("Cannot access current working directory"));
- }
+ if (!git_work_tree_cfg)
+ git_work_tree_cfg = xgetcwd();
if (work_tree)
- set_git_work_tree(real_path(work_tree));
+ set_git_work_tree(work_tree);
else
set_git_work_tree(git_work_tree_cfg);
if (access(get_git_work_tree(), X_OK))
}
else {
if (work_tree)
- set_git_work_tree(real_path(work_tree));
+ set_git_work_tree(work_tree);
}
set_git_dir_init(git_dir, real_git_dir, 1);
#include "parse-options.h"
#include "diff.h"
#include "revision.h"
+#include "split-index.h"
#define DO_REVS 1
#define DO_NOREV 2
error("refname '%s' is ambiguous", name);
break;
}
+ free(full);
} else {
show_with_type(type, name);
}
}
if (!strcmp(arg, "--git-dir")) {
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
- static char cwd[PATH_MAX];
+ char *cwd;
int len;
if (gitdir) {
puts(gitdir);
puts(".git");
continue;
}
- if (!getcwd(cwd, PATH_MAX))
- die_errno("unable to get current working directory");
+ cwd = xgetcwd();
len = strlen(cwd);
printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
+ free(cwd);
continue;
}
if (!strcmp(arg, "--resolve-git-dir")) {
: "false");
continue;
}
+ if (!strcmp(arg, "--shared-index-path")) {
+ if (read_cache() < 0)
+ die(_("Could not read the index"));
+ if (the_index.split_index) {
+ const unsigned char *sha1 = the_index.split_index->base_sha1;
+ puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
+ }
+ continue;
+ }
if (starts_with(arg, "--since=")) {
show_datestring("--max-age=", arg+8);
continue;
buf = xrealloc(buf, size+1);
buf[size++] = '\n';
}
- }
- else {
+ } else {
size = xsize_t(st.st_size);
if (size == 0) {
close(fd);
group = &dir->exclude_list_group[EXC_DIRS];
- /* Pop the exclude lists from the EXCL_DIRS exclude_list_group
+ /*
+ * Pop the exclude lists from the EXCL_DIRS exclude_list_group
* which originate from directories not in the prefix of the
- * path being checked. */
+ * path being checked.
+ */
while ((stk = dir->exclude_stack) != NULL) {
if (stk->baselen <= baselen &&
- !strncmp(dir->basebuf, base, stk->baselen))
+ !strncmp(dir->basebuf.buf, base, stk->baselen))
break;
el = &group->el[dir->exclude_stack->exclude_ix];
dir->exclude_stack = stk->prev;
dir->exclude = NULL;
- free((char *)el->src); /* see strdup() below */
+ free((char *)el->src); /* see strbuf_detach() below */
clear_exclude_list(el);
free(stk);
group->nr--;
if (dir->exclude)
return;
+ /*
+ * Lazy initialization. All call sites currently just
+ * memset(dir, 0, sizeof(*dir)) before use. Changing all of
+ * them seems lots of work for little benefit.
+ */
+ if (!dir->basebuf.buf)
+ strbuf_init(&dir->basebuf, PATH_MAX);
+
/* Read from the parent directories and push them down. */
current = stk ? stk->baselen : -1;
+ strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
while (current < baselen) {
struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
const char *cp;
if (current < 0) {
cp = base;
current = 0;
- }
- else {
+ } else {
cp = strchr(base + current + 1, '/');
if (!cp)
die("oops in prep_exclude");
stk->baselen = cp - base;
stk->exclude_ix = group->nr;
el = add_exclude_list(dir, EXC_DIRS, NULL);
- memcpy(dir->basebuf + current, base + current,
- stk->baselen - current);
+ strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
+ assert(stk->baselen == dir->basebuf.len);
/* Abort if the directory is excluded */
if (stk->baselen) {
int dt = DT_DIR;
- dir->basebuf[stk->baselen - 1] = 0;
+ dir->basebuf.buf[stk->baselen - 1] = 0;
dir->exclude = last_exclude_matching_from_lists(dir,
- dir->basebuf, stk->baselen - 1,
- dir->basebuf + current, &dt);
- dir->basebuf[stk->baselen - 1] = '/';
+ dir->basebuf.buf, stk->baselen - 1,
+ dir->basebuf.buf + current, &dt);
+ dir->basebuf.buf[stk->baselen - 1] = '/';
if (dir->exclude &&
dir->exclude->flags & EXC_FLAG_NEGATIVE)
dir->exclude = NULL;
if (dir->exclude) {
- dir->basebuf[stk->baselen] = 0;
dir->exclude_stack = stk;
return;
}
}
- /* Try to read per-directory file unless path is too long */
- if (dir->exclude_per_dir &&
- stk->baselen + strlen(dir->exclude_per_dir) < PATH_MAX) {
- strcpy(dir->basebuf + stk->baselen,
- dir->exclude_per_dir);
+ /* Try to read per-directory file */
+ if (dir->exclude_per_dir) {
/*
* dir->basebuf gets reused by the traversal, but we
* need fname to remain unchanged to ensure the src
* member of each struct exclude correctly
* back-references its source file. Other invocations
* of add_exclude_list provide stable strings, so we
- * strdup() and free() here in the caller.
+ * strbuf_detach() and free() here in the caller.
*/
- el->src = strdup(dir->basebuf);
- add_excludes_from_file_to_list(dir->basebuf,
- dir->basebuf, stk->baselen, el, 1);
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addbuf(&sb, &dir->basebuf);
+ strbuf_addstr(&sb, dir->exclude_per_dir);
+ el->src = strbuf_detach(&sb, NULL);
+ add_excludes_from_file_to_list(el->src, el->src,
+ stk->baselen, el, 1);
}
dir->exclude_stack = stk;
current = stk->baselen;
}
- dir->basebuf[baselen] = '\0';
+ strbuf_setlen(&dir->basebuf, baselen);
}
/*
const struct dir_entry *e1 = *(const struct dir_entry **)p1;
const struct dir_entry *e2 = *(const struct dir_entry **)p2;
- return cache_name_compare(e1->name, e1->len,
- e2->name, e2->len);
+ return name_compare(e1->name, e1->len, e2->name, e2->len);
}
static struct path_simplify *create_simplify(const char **pathspec)
int is_inside_dir(const char *dir)
{
- char cwd[PATH_MAX];
+ char *cwd;
+ int rc;
+
if (!dir)
return 0;
- if (!getcwd(cwd, sizeof(cwd)))
- die_errno("can't find the current directory");
- return dir_inside_of(cwd, dir) >= 0;
+
+ cwd = xgetcwd();
+ rc = (dir_inside_of(cwd, dir) >= 0);
+ free(cwd);
+ return rc;
}
int is_empty_dir(const char *path)
free(stk);
stk = prev;
}
+ strbuf_release(&dir->basebuf);
}
#define has_dos_drive_prefix(path) 0
#endif
+#ifndef offset_1st_component
+#define offset_1st_component(path) (is_dir_sep((path)[0]))
+#endif
+
#ifndef is_dir_sep
#define is_dir_sep(c) ((c) == '/')
#endif
#else
#define NORETURN
#define NORETURN_PTR
+#ifndef __GNUC__
#ifndef __attribute__
#define __attribute__(x)
#endif
#endif
+#endif
/* The sentinel attribute is valid from gcc version 4.0 */
#if defined(__GNUC__) && (__GNUC__ >= 4)
* trying to help gcc, anyway, it's OK; other compilers will fall back to
* using the function as usual.
*/
-#if defined(__GNUC__) && ! defined(__clang__)
-#define error(...) (error(__VA_ARGS__), -1)
+#if defined(__GNUC__)
+static inline int const_error(void)
+{
+ return -1;
+}
+#define error(...) (error(__VA_ARGS__), const_error())
#endif
extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
extern void set_die_is_recursing_routine(int (*routine)(void));
extern int starts_with(const char *str, const char *prefix);
-extern int ends_with(const char *str, const char *suffix);
-static inline const char *skip_prefix(const char *str, const char *prefix)
+/*
+ * If the string "str" begins with the string found in "prefix", return 1.
+ * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
+ * the string right after the prefix).
+ *
+ * Otherwise, return 0 and leave "out" untouched.
+ *
+ * Examples:
+ *
+ * [extract branch name, fail if not a branch]
+ * if (!skip_prefix(ref, "refs/heads/", &branch)
+ * return -1;
+ *
+ * [skip prefix if present, otherwise use whole string]
+ * skip_prefix(name, "refs/heads/", &name);
+ */
+static inline int skip_prefix(const char *str, const char *prefix,
+ const char **out)
{
do {
- if (!*prefix)
- return str;
+ if (!*prefix) {
+ *out = str;
+ return 1;
+ }
} while (*str++ == *prefix++);
- return NULL;
+ return 0;
+}
+
+/*
+ * If buf ends with suffix, return 1 and subtract the length of the suffix
+ * from *len. Otherwise, return 0 and leave *len untouched.
+ */
+static inline int strip_suffix_mem(const char *buf, size_t *len,
+ const char *suffix)
+{
+ size_t suflen = strlen(suffix);
+ if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
+ return 0;
+ *len -= suflen;
+ return 1;
+}
+
+/*
+ * If str ends with suffix, return 1 and set *len to the size of the string
+ * without the suffix. Otherwise, return 0 and set *len to the size of the
+ * string.
+ *
+ * Note that we do _not_ NUL-terminate str to the new length.
+ */
+static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+{
+ *len = strlen(str);
+ return strip_suffix_mem(str, len, suffix);
+}
+
+static inline int ends_with(const char *str, const char *suffix)
+{
+ size_t len;
+ return strip_suffix(str, suffix, &len);
}
#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
typedef void (*try_to_free_t)(size_t);
extern try_to_free_t set_try_to_free_routine(try_to_free_t);
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# define xalloca(size) (alloca(size))
+# define xalloca_free(p) do {} while (0)
+#else
+# define xalloca(size) (xmalloc(size))
+# define xalloca_free(p) (free(p))
+#endif
extern char *xstrdup(const char *str);
extern void *xmalloc(size_t size);
extern void *xmallocz(size_t size);
extern int xmkstemp_mode(char *template, int mode);
extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
+ extern char *xgetcwd(void);
static inline size_t xsize_t(off_t len)
{
return (size_t)len;
}
-static inline int has_extension(const char *filename, const char *ext)
-{
- size_t len = strlen(filename);
- size_t extlen = strlen(ext);
- return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
-}
-
/* in ctype.c, for kwset users */
extern const char tolower_trans_tbl[256];
#endif
#endif
+#if defined(__GNUC__) || (_MSC_VER >= 1400)
+#define HAVE_VARIADIC_MACROS 1
+#endif
+
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
* Always returns the return value of unlink(2).
static struct startup_info git_startup_info;
static int use_pager = -1;
- static char orig_cwd[PATH_MAX];
++static char *orig_cwd;
+static const char *env_names[] = {
+ GIT_DIR_ENVIRONMENT,
+ GIT_WORK_TREE_ENVIRONMENT,
+ GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
+ GIT_PREFIX_ENVIRONMENT
+};
+static char *orig_env[4];
+static int saved_environment;
+
+static void save_env(void)
+{
+ int i;
+ if (saved_environment)
+ return;
+ saved_environment = 1;
- if (!getcwd(orig_cwd, sizeof(orig_cwd)))
- die_errno("cannot getcwd");
++ orig_cwd = xgetcwd();
+ for (i = 0; i < ARRAY_SIZE(env_names); i++) {
+ orig_env[i] = getenv(env_names[i]);
+ if (orig_env[i])
+ orig_env[i] = xstrdup(orig_env[i]);
+ }
+}
+
+static void restore_env(void)
+{
+ int i;
- if (*orig_cwd && chdir(orig_cwd))
++ if (orig_cwd && chdir(orig_cwd))
+ die_errno("could not move to %s", orig_cwd);
++ free(orig_cwd);
+ for (i = 0; i < ARRAY_SIZE(env_names); i++) {
+ if (orig_env[i])
+ setenv(env_names[i], orig_env[i], 1);
+ else
+ unsetenv(env_names[i]);
+ }
+}
static void commit_pager_choice(void) {
switch (use_pager) {
/*
* Check remaining flags.
*/
- if (starts_with(cmd, "--exec-path")) {
- cmd += 11;
+ if (skip_prefix(cmd, "--exec-path", &cmd)) {
if (*cmd == '=')
git_set_argv_exec_path(cmd + 1);
else {
*envchanged = 1;
(*argv)++;
(*argc)--;
- } else if (starts_with(cmd, "--git-dir=")) {
- setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+ } else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
+ setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--namespace")) {
*envchanged = 1;
(*argv)++;
(*argc)--;
- } else if (starts_with(cmd, "--namespace=")) {
- setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+ } else if (skip_prefix(cmd, "--namespace=", &cmd)) {
+ setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--work-tree")) {
*envchanged = 1;
(*argv)++;
(*argc)--;
- } else if (starts_with(cmd, "--work-tree=")) {
- setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+ } else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
+ setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--bare")) {
- static char git_dir[PATH_MAX+1];
+ char *cwd = xgetcwd();
is_bare_repository_cfg = 1;
- setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
+ setenv(GIT_DIR_ENVIRONMENT, cwd, 0);
+ free(cwd);
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
if (envchanged)
*envchanged = 1;
* RUN_SETUP for reading from the configuration file.
*/
#define NEED_WORK_TREE (1<<3)
+#define NO_SETUP (1<<4)
struct cmd_struct {
const char *cmd;
if (!help) {
if (p->option & RUN_SETUP)
prefix = setup_git_directory();
- if (p->option & RUN_SETUP_GENTLY) {
+ else if (p->option & RUN_SETUP_GENTLY) {
int nongit_ok;
prefix = setup_git_directory_gently(&nongit_ok);
}
{ "cherry", cmd_cherry, RUN_SETUP },
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
- { "clone", cmd_clone },
+ { "clone", cmd_clone, NO_SETUP },
{ "column", cmd_column, RUN_SETUP_GENTLY },
{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "hash-object", cmd_hash_object },
{ "help", cmd_help },
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
- { "init", cmd_init_db },
- { "init-db", cmd_init_db },
+ { "init", cmd_init_db, NO_SETUP },
+ { "init-db", cmd_init_db, NO_SETUP },
{ "log", cmd_log, RUN_SETUP },
{ "ls-files", cmd_ls_files, RUN_SETUP },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
{ "upload-archive", cmd_upload_archive },
{ "upload-archive--writer", cmd_upload_archive_writer },
{ "var", cmd_var, RUN_SETUP_GENTLY },
+ { "verify-commit", cmd_verify_commit, RUN_SETUP },
{ "verify-pack", cmd_verify_pack },
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version },
struct cmd_struct *p = commands+i;
if (strcmp(p->cmd, cmd))
continue;
+ if (saved_environment && (p->option & NO_SETUP)) {
+ restore_env();
+ break;
+ }
exit(run_builtin(p, argc, argv));
}
}
* of overriding "git log" with "git show" by having
* alias.log = show
*/
- if (done_alias || !handle_alias(argcp, argv))
+ if (done_alias)
+ break;
+ save_env();
+ if (!handle_alias(argcp, argv))
break;
done_alias = 1;
}
git_setup_gettext();
+ trace_command_performance(argv);
+
/*
* "git-xxxx" is the same as "git xxxx", but we obviously:
*
* So we just directly call the builtin handler, and die if
* that one cannot handle it.
*/
- if (starts_with(cmd, "git-")) {
- cmd += 4;
+ if (skip_prefix(cmd, "git-", &cmd)) {
argv[0] = cmd;
handle_builtin(argc, argv);
die("cannot handle %s as a builtin", cmd);
argc--;
handle_options(&argv, &argc, NULL);
if (argc > 0) {
- if (starts_with(argv[0], "--"))
- argv[0] += 2;
+ /* translate --help and --version into commands */
+ skip_prefix(argv[0], "--", &argv[0]);
} else {
/* The user didn't specify a command; give them help */
commit_pager_choice();
}
static const char *setup_explicit_git_dir(const char *gitdirenv,
- char *cwd, int len,
+ struct strbuf *cwd,
int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
if (is_absolute_path(git_work_tree_cfg))
set_git_work_tree(git_work_tree_cfg);
else {
- char core_worktree[PATH_MAX];
+ char *core_worktree;
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))
+ core_worktree = xgetcwd();
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
set_git_work_tree(core_worktree);
+ free(core_worktree);
}
}
else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
worktree = get_git_work_tree();
/* both get_git_work_tree() and cwd are already normalized */
- if (!strcmp(cwd, worktree)) { /* cwd == worktree */
+ if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
set_git_dir(gitdirenv);
free(gitfile);
return NULL;
}
- offset = dir_inside_of(cwd, worktree);
+ offset = dir_inside_of(cwd->buf, worktree);
if (offset >= 0) { /* 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';
+ strbuf_addch(cwd, '/');
free(gitfile);
- return cwd + offset;
+ return cwd->buf + offset;
}
/* cwd outside worktree */
}
static const char *setup_discovered_git_dir(const char *gitdir,
- char *cwd, int offset, int len,
+ struct strbuf *cwd, int offset,
int *nongit_ok)
{
if (check_repository_format_gently(gitdir, nongit_ok))
/* --work-tree is set without --git-dir; use discovered one */
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
- if (offset != len && !is_absolute_path(gitdir))
+ if (offset != cwd->len && !is_absolute_path(gitdir))
gitdir = xstrdup(real_path(gitdir));
- if (chdir(cwd))
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
- return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+ return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
}
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
if (is_bare_repository_cfg > 0) {
- set_git_dir(offset == len ? gitdir : real_path(gitdir));
- if (chdir(cwd))
+ set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir));
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
return NULL;
}
set_git_dir(gitdir);
inside_git_dir = 0;
inside_work_tree = 1;
- if (offset == len)
+ if (offset == cwd->len)
return NULL;
/* Make "offset" point to past the '/', and add a '/' at the end */
offset++;
- cwd[len++] = '/';
- cwd[len] = 0;
- return cwd + offset;
+ strbuf_addch(cwd, '/');
+ return cwd->buf + offset;
}
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
- static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongit_ok)
+ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
+ int *nongit_ok)
{
int root_len;
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
const char *gitdir;
- gitdir = offset == len ? "." : xmemdupz(cwd, offset);
- if (chdir(cwd))
+ gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
- return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+ return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
}
inside_git_dir = 1;
inside_work_tree = 0;
- if (offset != len) {
- if (chdir(cwd))
+ if (offset != cwd->len) {
+ if (chdir(cwd->buf))
die_errno("Cannot come back to cwd");
- root_len = offset_1st_component(cwd);
- cwd[offset > root_len ? offset : root_len] = '\0';
- set_git_dir(cwd);
+ root_len = offset_1st_component(cwd->buf);
+ strbuf_setlen(cwd, offset > root_len ? offset : root_len);
+ set_git_dir(cwd->buf);
}
else
set_git_dir(".");
{
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
- static char cwd[PATH_MAX + 1];
+ static struct strbuf cwd = STRBUF_INIT;
const char *gitdirenv, *ret;
char *gitfile;
- int len, offset, offset_parent, ceil_offset = -1;
+ int offset, offset_parent, ceil_offset = -1;
dev_t current_device = 0;
int one_filesystem = 1;
+ /*
+ * We may have read an incomplete configuration before
+ * setting-up the git directory. If so, clear the cache so
+ * that the next queries to the configuration reload complete
+ * configuration (including the per-repo config file that we
+ * ignored previously).
+ */
+ git_config_clear();
+
/*
* Let's assume that we are in a git repository.
* If it turns out later that we are somewhere else, the value will be
if (nongit_ok)
*nongit_ok = 0;
- if (!getcwd(cwd, sizeof(cwd) - 1))
+ if (strbuf_getcwd(&cwd))
die_errno("Unable to read current working directory");
- offset = len = strlen(cwd);
+ offset = cwd.len;
/*
* If GIT_DIR is set explicitly, we're not going
*/
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
if (gitdirenv)
- return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
+ return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok);
if (env_ceiling_dirs) {
int empty_entry_found = 0;
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
canonicalize_ceiling_entry, &empty_entry_found);
- ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
+ ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
}
- if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
+ if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf))
ceil_offset = 1;
/*
if (gitdirenv) {
ret = setup_discovered_git_dir(gitdirenv,
- cwd, offset, len,
+ &cwd, offset,
nongit_ok);
free(gitfile);
return ret;
free(gitfile);
if (is_git_directory("."))
- return setup_bare_git_dir(cwd, offset, len, nongit_ok);
+ return setup_bare_git_dir(&cwd, offset, nongit_ok);
offset_parent = offset;
- while (--offset_parent > ceil_offset && cwd[offset_parent] != '/');
+ while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/');
if (offset_parent <= ceil_offset)
- return setup_nongit(cwd, nongit_ok);
+ return setup_nongit(cwd.buf, nongit_ok);
if (one_filesystem) {
- dev_t parent_device = get_device_or_die("..", cwd, offset);
+ dev_t parent_device = get_device_or_die("..", cwd.buf,
+ offset);
if (parent_device != current_device) {
if (nongit_ok) {
- if (chdir(cwd))
+ if (chdir(cwd.buf))
die_errno("Cannot come back to cwd");
*nongit_ok = 1;
return NULL;
}
- cwd[offset] = '\0';
+ strbuf_setlen(&cwd, offset);
die("Not a git repository (or any parent up to mount point %s)\n"
- "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).",
+ cwd.buf);
}
}
if (chdir("..")) {
- cwd[offset] = '\0';
- die_errno("Cannot change to '%s/..'", cwd);
+ strbuf_setlen(&cwd, offset);
+ die_errno("Cannot change to '%s/..'", cwd.buf);
}
offset = offset_parent;
}
const unsigned char null_sha1[20];
-static const char *no_log_pack_access = "no_log_pack_access";
-static const char *log_pack_access;
-
/*
* This is meant to hold a *small* number of objects that you would
* want read_sha1_file() to be able to return, but yet you do not want
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth)
+static int link_alt_odb_entry(const char *entry, const char *relative_base,
+ int depth, const char *normalized_objdir)
{
- const char *objdir = get_object_directory();
struct alternate_object_database *ent;
struct alternate_object_database *alt;
int pfxlen, entlen;
return -1;
}
}
- if (!strcmp(ent->base, objdir)) {
+ if (!strcmp_icase(ent->base, normalized_objdir)) {
free(ent);
return -1;
}
struct string_list entries = STRING_LIST_INIT_NODUP;
char *alt_copy;
int i;
+ struct strbuf objdirbuf = STRBUF_INIT;
if (depth > 5) {
error("%s: ignoring alternate object stores, nesting too deep.",
return;
}
- strbuf_addstr(&objdirbuf, absolute_path(get_object_directory()));
++ strbuf_add_absolute_path(&objdirbuf, get_object_directory());
+ normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
+
alt_copy = xmemdupz(alt, len);
string_list_split_in_place(&entries, alt_copy, sep, -1);
for (i = 0; i < entries.nr; i++) {
error("%s: ignoring relative alternate object store %s",
relative_base, entry);
} else {
- link_alt_odb_entry(entry, relative_base, depth);
+ link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
}
}
string_list_clear(&entries, 0);
free(alt_copy);
+ strbuf_release(&objdirbuf);
}
void read_info_alternates(const char * relative_base, int depth)
static void prepare_packed_git_one(char *objdir, int local)
{
- /* Ensure that this buffer is large enough so that we can
- append "/pack/" without clobbering the stack even if
- strlen(objdir) were PATH_MAX. */
- char path[PATH_MAX + 1 + 4 + 1 + 1];
- int len;
+ struct strbuf path = STRBUF_INIT;
+ size_t dirnamelen;
DIR *dir;
struct dirent *de;
struct string_list garbage = STRING_LIST_INIT_DUP;
- sprintf(path, "%s/pack", objdir);
- len = strlen(path);
- dir = opendir(path);
+ strbuf_addstr(&path, objdir);
+ strbuf_addstr(&path, "/pack");
+ dir = opendir(path.buf);
if (!dir) {
if (errno != ENOENT)
error("unable to open object pack directory: %s: %s",
- path, strerror(errno));
+ path.buf, strerror(errno));
+ strbuf_release(&path);
return;
}
- path[len++] = '/';
+ strbuf_addch(&path, '/');
+ dirnamelen = path.len;
while ((de = readdir(dir)) != NULL) {
- int namelen = strlen(de->d_name);
struct packed_git *p;
-
- if (len + namelen + 1 > sizeof(path)) {
- if (report_garbage) {
- struct strbuf sb = STRBUF_INIT;
- strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
- report_garbage("path too long", sb.buf);
- strbuf_release(&sb);
- }
- continue;
- }
+ size_t base_len;
if (is_dot_or_dotdot(de->d_name))
continue;
- strcpy(path + len, de->d_name);
+ strbuf_setlen(&path, dirnamelen);
+ strbuf_addstr(&path, de->d_name);
- if (has_extension(de->d_name, ".idx")) {
+ base_len = path.len;
+ if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
/* Don't reopen a pack we already have. */
for (p = packed_git; p; p = p->next) {
- if (!memcmp(path, p->pack_name, len + namelen - 4))
+ size_t len;
+ if (strip_suffix(p->pack_name, ".pack", &len) &&
+ len == base_len &&
+ !memcmp(p->pack_name, path.buf, len))
break;
}
if (p == NULL &&
* See if it really is a valid .idx file with
* corresponding .pack file that we can map.
*/
- (p = add_packed_git(path, len + namelen, local)) != NULL)
+ (p = add_packed_git(path.buf, path.len, local)) != NULL)
install_packed_git(p);
}
if (!report_garbage)
continue;
- if (has_extension(de->d_name, ".idx") ||
- has_extension(de->d_name, ".pack") ||
- has_extension(de->d_name, ".bitmap") ||
- has_extension(de->d_name, ".keep"))
- string_list_append(&garbage, path);
+ if (ends_with(de->d_name, ".idx") ||
+ ends_with(de->d_name, ".pack") ||
+ ends_with(de->d_name, ".bitmap") ||
+ ends_with(de->d_name, ".keep"))
+ string_list_append(&garbage, path.buf);
else
- report_garbage("garbage found", path);
+ report_garbage("garbage found", path.buf);
}
closedir(dir);
report_pack_garbage(&garbage);
string_list_clear(&garbage, 0);
+ strbuf_release(&path);
}
static int sort_pack(const void *a_, const void *b_)
static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
{
- static FILE *log_file;
-
- if (!log_pack_access)
- log_pack_access = getenv("GIT_TRACE_PACK_ACCESS");
- if (!log_pack_access)
- log_pack_access = no_log_pack_access;
- if (log_pack_access == no_log_pack_access)
- return;
-
- if (!log_file) {
- log_file = fopen(log_pack_access, "w");
- if (!log_file) {
- error("cannot open pack access log '%s' for writing: %s",
- log_pack_access, strerror(errno));
- log_pack_access = no_log_pack_access;
- return;
- }
- }
- fprintf(log_file, "%s %"PRIuMAX"\n",
- p->pack_name, (uintmax_t)obj_offset);
- fflush(log_file);
+ static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
+ trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
+ p->pack_name, (uintmax_t)obj_offset);
}
int do_check_packed_object_crc;
int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
int base_from_cache = 0;
- if (log_pack_access != no_log_pack_access)
- write_pack_access_log(p, obj_offset);
+ write_pack_access_log(p, obj_offset);
/* PHASE 1: drill down to the innermost base object */
for (;;) {
#include "cache.h"
#include "refs.h"
+#include "utf8.h"
int starts_with(const char *str, const char *prefix)
{
return 0;
}
-int ends_with(const char *str, const char *suffix)
-{
- int len = strlen(str), suflen = strlen(suffix);
- if (len < suflen)
- return 0;
- else
- return !strcmp(str + len - suflen, suffix);
-}
-
/*
* Used as the default ->buf value, so that people can always assume
* buf is non NULL and ->buf is NUL terminated even for a freshly
void strbuf_trim(struct strbuf *sb)
{
- char *b = sb->buf;
- while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
- sb->len--;
- while (sb->len > 0 && isspace(*b)) {
- b++;
- sb->len--;
- }
- memmove(sb->buf, b, sb->len);
- sb->buf[sb->len] = '\0';
+ strbuf_rtrim(sb);
+ strbuf_ltrim(sb);
}
void strbuf_rtrim(struct strbuf *sb)
{
sb->buf[sb->len] = '\0';
}
+int strbuf_reencode(struct strbuf *sb, const char *from, const char *to)
+{
+ char *out;
+ int len;
+
+ if (same_encoding(from, to))
+ return 0;
+
+ out = reencode_string_len(sb->buf, sb->len, to, from, &len);
+ if (!out)
+ return -1;
+
+ strbuf_attach(sb, out, len, len);
+ return 0;
+}
+
+void strbuf_tolower(struct strbuf *sb)
+{
+ char *p = sb->buf, *end = sb->buf + sb->len;
+ for (; p < end; p++)
+ *p = tolower(*p);
+}
+
struct strbuf **strbuf_split_buf(const char *str, size_t slen,
int terminator, int max)
{
return -1;
}
+ int strbuf_getcwd(struct strbuf *sb)
+ {
+ size_t oldalloc = sb->alloc;
+ size_t guessed_len = 128;
+
+ for (;; guessed_len *= 2) {
+ strbuf_grow(sb, guessed_len);
+ if (getcwd(sb->buf, sb->alloc)) {
+ strbuf_setlen(sb, strlen(sb->buf));
+ return 0;
+ }
+ if (errno != ERANGE)
+ break;
+ }
+ if (oldalloc == 0)
+ strbuf_release(sb);
+ else
+ strbuf_reset(sb);
+ return -1;
+ }
+
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
{
int ch;
}
}
+ void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
+ {
+ if (!*path)
+ die("The empty string is not a valid path");
+ if (!is_absolute_path(path)) {
+ struct stat cwd_stat, pwd_stat;
+ size_t orig_len = sb->len;
+ char *cwd = xgetcwd();
+ char *pwd = getenv("PWD");
+ if (pwd && strcmp(pwd, cwd) &&
+ !stat(cwd, &cwd_stat) &&
+ (cwd_stat.st_dev || cwd_stat.st_ino) &&
+ !stat(pwd, &pwd_stat) &&
+ pwd_stat.st_dev == cwd_stat.st_dev &&
+ pwd_stat.st_ino == cwd_stat.st_ino)
+ strbuf_addstr(sb, pwd);
+ else
+ strbuf_addstr(sb, cwd);
+ if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
+ strbuf_addch(sb, '/');
+ free(cwd);
+ }
+ strbuf_addstr(sb, path);
+ }
+
int printf_ln(const char *fmt, ...)
{
int ret;
return -1;
return ret + 1;
}
+
+char *xstrdup_tolower(const char *string)
+{
+ char *result;
+ size_t len, i;
+
+ len = strlen(string);
+ result = xmalloc(len + 1);
+ for (i = 0; i < len; i++)
+ result[i] = tolower(string[i]);
+ result[i] = '\0';
+ return result;
+}
+
+char *xstrvfmt(const char *fmt, va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_vaddf(&buf, fmt, ap);
+ return strbuf_detach(&buf, NULL);
+}
+
+char *xstrfmt(const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = xstrvfmt(fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
extern void strbuf_trim(struct strbuf *);
extern void strbuf_rtrim(struct strbuf *);
extern void strbuf_ltrim(struct strbuf *);
+extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
+extern void strbuf_tolower(struct strbuf *sb);
extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
+static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
+{
+ if (strip_suffix_mem(sb->buf, &sb->len, suffix)) {
+ strbuf_setlen(sb, sb->len);
+ return 1;
+ } else
+ return 0;
+}
+
/*
* Split str (of length slen) at the specified terminator character.
* Return a null-terminated array of pointers to strbuf objects
extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+ extern int strbuf_getcwd(struct strbuf *sb);
extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
extern int strbuf_getline(struct strbuf *, FILE *, int);
int reserved);
extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
+ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+
__attribute__((format (printf,1,2)))
extern int printf_ln(const char *fmt, ...);
__attribute__((format (printf,2,3)))
extern int fprintf_ln(FILE *fp, const char *fmt, ...);
+char *xstrdup_tolower(const char *);
+
+/*
+ * Create a newly allocated string using printf format. You can do this easily
+ * with a strbuf, but this provides a shortcut to save a few lines.
+ */
+__attribute__((format (printf, 1, 0)))
+char *xstrvfmt(const char *fmt, va_list ap);
+__attribute__((format (printf, 1, 2)))
+char *xstrfmt(const char *fmt, ...);
+
#endif /* STRBUF_H */
#include "quote.h"
/* Get a trace file descriptor from "key" env variable. */
-static int get_trace_fd(const char *key, int *need_close)
+static int get_trace_fd(struct trace_key *key)
{
- char *trace = getenv(key);
+ static struct trace_key trace_default = { "GIT_TRACE" };
+ const char *trace;
+
+ /* use default "GIT_TRACE" if NULL */
+ if (!key)
+ key = &trace_default;
+
+ /* don't open twice */
+ if (key->initialized)
+ return key->fd;
+
+ trace = getenv(key->key);
if (!trace || !strcmp(trace, "") ||
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
- return 0;
- if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
- return STDERR_FILENO;
- if (strlen(trace) == 1 && isdigit(*trace))
- return atoi(trace);
- if (is_absolute_path(trace)) {
+ key->fd = 0;
+ else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
+ key->fd = STDERR_FILENO;
+ else if (strlen(trace) == 1 && isdigit(*trace))
+ key->fd = atoi(trace);
+ else if (is_absolute_path(trace)) {
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd == -1) {
fprintf(stderr,
"Could not open '%s' for tracing: %s\n"
"Defaulting to tracing on stderr...\n",
trace, strerror(errno));
- return STDERR_FILENO;
+ key->fd = STDERR_FILENO;
+ } else {
+ key->fd = fd;
+ key->need_close = 1;
}
- *need_close = 1;
- return fd;
+ } else {
+ fprintf(stderr, "What does '%s' for %s mean?\n"
+ "If you want to trace into a file, then please set "
+ "%s to an absolute pathname (starting with /).\n"
+ "Defaulting to tracing on stderr...\n",
+ trace, key->key, key->key);
+ key->fd = STDERR_FILENO;
}
- fprintf(stderr, "What does '%s' for %s mean?\n", trace, key);
- fprintf(stderr, "If you want to trace into a file, "
- "then please set %s to an absolute pathname "
- "(starting with /).\n", key);
- fprintf(stderr, "Defaulting to tracing on stderr...\n");
+ key->initialized = 1;
+ return key->fd;
+}
- return STDERR_FILENO;
+void trace_disable(struct trace_key *key)
+{
+ if (key->need_close)
+ close(key->fd);
+ key->fd = 0;
+ key->initialized = 1;
+ key->need_close = 0;
}
static const char err_msg[] = "Could not trace into fd given by "
"GIT_TRACE environment variable";
-static void trace_vprintf(const char *key, const char *fmt, va_list ap)
+static int prepare_trace_line(const char *file, int line,
+ struct trace_key *key, struct strbuf *buf)
{
- struct strbuf buf = STRBUF_INIT;
+ static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
+ struct timeval tv;
+ struct tm tm;
+ time_t secs;
if (!trace_want(key))
- return;
+ return 0;
set_try_to_free_routine(NULL); /* is never reset */
- strbuf_vaddf(&buf, fmt, ap);
- trace_strbuf(key, &buf);
- strbuf_release(&buf);
+
+ /* unit tests may want to disable additional trace output */
+ if (trace_want(&trace_bare))
+ return 1;
+
+ /* print current timestamp */
+ gettimeofday(&tv, NULL);
+ secs = tv.tv_sec;
+ localtime_r(&secs, &tm);
+ strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
+ tm.tm_sec, (long) tv.tv_usec);
+
+#ifdef HAVE_VARIADIC_MACROS
+ /* print file:line */
+ strbuf_addf(buf, "%s:%d ", file, line);
+ /* align trace output (column 40 catches most files names in git) */
+ while (buf->len < 40)
+ strbuf_addch(buf, ' ');
+#endif
+
+ return 1;
}
-__attribute__((format (printf, 2, 3)))
-void trace_printf_key(const char *key, const char *fmt, ...)
+static void print_trace_line(struct trace_key *key, struct strbuf *buf)
+{
+ /* append newline if missing */
+ if (buf->len && buf->buf[buf->len - 1] != '\n')
+ strbuf_addch(buf, '\n');
+
+ write_or_whine_pipe(get_trace_fd(key), buf->buf, buf->len, err_msg);
+ strbuf_release(buf);
+}
+
+static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
+ const char *format, va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, key, &buf))
+ return;
+
+ strbuf_vaddf(&buf, format, ap);
+ print_trace_line(key, &buf);
+}
+
+static void trace_argv_vprintf_fl(const char *file, int line,
+ const char **argv, const char *format,
+ va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, NULL, &buf))
+ return;
+
+ strbuf_vaddf(&buf, format, ap);
+
+ sq_quote_argv(&buf, argv, 0);
+ print_trace_line(NULL, &buf);
+}
+
+void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+ const struct strbuf *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, key, &buf))
+ return;
+
+ strbuf_addbuf(&buf, data);
+ print_trace_line(key, &buf);
+}
+
+static struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
+
+static void trace_performance_vprintf_fl(const char *file, int line,
+ uint64_t nanos, const char *format,
+ va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, &trace_perf_key, &buf))
+ return;
+
+ strbuf_addf(&buf, "performance: %.9f s", (double) nanos / 1000000000);
+
+ if (format && *format) {
+ strbuf_addstr(&buf, ": ");
+ strbuf_vaddf(&buf, format, ap);
+ }
+
+ print_trace_line(&trace_perf_key, &buf);
+}
+
+#ifndef HAVE_VARIADIC_MACROS
+
+void trace_printf(const char *format, ...)
{
va_list ap;
- va_start(ap, fmt);
- trace_vprintf(key, fmt, ap);
+ va_start(ap, format);
+ trace_vprintf_fl(NULL, 0, NULL, format, ap);
va_end(ap);
}
-void trace_printf(const char *fmt, ...)
+void trace_printf_key(struct trace_key *key, const char *format, ...)
{
va_list ap;
- va_start(ap, fmt);
- trace_vprintf("GIT_TRACE", fmt, ap);
+ va_start(ap, format);
+ trace_vprintf_fl(NULL, 0, key, format, ap);
va_end(ap);
}
-void trace_strbuf(const char *key, const struct strbuf *buf)
+void trace_argv_printf(const char **argv, const char *format, ...)
{
- int fd, need_close = 0;
-
- fd = get_trace_fd(key, &need_close);
- if (!fd)
- return;
+ va_list ap;
+ va_start(ap, format);
+ trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
+ va_end(ap);
+}
- write_or_whine_pipe(fd, buf->buf, buf->len, err_msg);
+void trace_strbuf(const char *key, const struct strbuf *data)
+{
+ trace_strbuf_fl(NULL, 0, key, data);
+}
- if (need_close)
- close(fd);
+void trace_performance(uint64_t nanos, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
+ va_end(ap);
}
-void trace_argv_printf(const char **argv, const char *fmt, ...)
+void trace_performance_since(uint64_t start, const char *format, ...)
{
- struct strbuf buf = STRBUF_INIT;
va_list ap;
- int fd, need_close = 0;
+ va_start(ap, format);
+ trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
+ format, ap);
+ va_end(ap);
+}
- fd = get_trace_fd("GIT_TRACE", &need_close);
- if (!fd)
- return;
+#else
- set_try_to_free_routine(NULL); /* is never reset */
- va_start(ap, fmt);
- strbuf_vaddf(&buf, fmt, ap);
+void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_vprintf_fl(file, line, key, format, ap);
va_end(ap);
+}
- sq_quote_argv(&buf, argv, 0);
- strbuf_addch(&buf, '\n');
- write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
- strbuf_release(&buf);
+void trace_argv_printf_fl(const char *file, int line, const char **argv,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_argv_vprintf_fl(file, line, argv, format, ap);
+ va_end(ap);
+}
- if (need_close)
- close(fd);
+void trace_performance_fl(const char *file, int line, uint64_t nanos,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_performance_vprintf_fl(file, line, nanos, format, ap);
+ va_end(ap);
}
+#endif /* HAVE_VARIADIC_MACROS */
+
+
static const char *quote_crnl(const char *path)
{
static char new_path[PATH_MAX];
/* FIXME: move prefix to startup_info struct and get rid of this arg */
void trace_repo_setup(const char *prefix)
{
- static const char *key = "GIT_TRACE_SETUP";
+ static struct trace_key key = TRACE_KEY_INIT(SETUP);
const char *git_work_tree;
- char cwd[PATH_MAX];
+ char *cwd;
- if (!trace_want(key))
+ if (!trace_want(&key))
return;
- if (!getcwd(cwd, PATH_MAX))
- die("Unable to get current working directory");
+ cwd = xgetcwd();
if (!(git_work_tree = get_git_work_tree()))
git_work_tree = "(null)";
if (!prefix)
prefix = "(null)";
- trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
- trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
- trace_printf_key(key, "setup: cwd: %s\n", quote_crnl(cwd));
- trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix));
+ trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+ trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
+ trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
+ trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
+
+ free(cwd);
}
-int trace_want(const char *key)
+int trace_want(struct trace_key *key)
{
- const char *trace = getenv(key);
+ return !!get_trace_fd(key);
+}
- if (!trace || !strcmp(trace, "") ||
- !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+#ifdef HAVE_CLOCK_GETTIME
+
+static inline uint64_t highres_nanos(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
return 0;
- return 1;
+ return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+#elif defined (GIT_WINDOWS_NATIVE)
+
+static inline uint64_t highres_nanos(void)
+{
+ static uint64_t high_ns, scaled_low_ns;
+ static int scale;
+ LARGE_INTEGER cnt;
+
+ if (!scale) {
+ if (!QueryPerformanceFrequency(&cnt))
+ return 0;
+
+ /* high_ns = number of ns per cnt.HighPart */
+ high_ns = (1000000000LL << 32) / (uint64_t) cnt.QuadPart;
+
+ /*
+ * Number of ns per cnt.LowPart is 10^9 / frequency (or
+ * high_ns >> 32). For maximum precision, we scale this factor
+ * so that it just fits within 32 bit (i.e. won't overflow if
+ * multiplied with cnt.LowPart).
+ */
+ scaled_low_ns = high_ns;
+ scale = 32;
+ while (scaled_low_ns >= 0x100000000LL) {
+ scaled_low_ns >>= 1;
+ scale--;
+ }
+ }
+
+ /* if QPF worked on initialization, we expect QPC to work as well */
+ QueryPerformanceCounter(&cnt);
+
+ return (high_ns * cnt.HighPart) +
+ ((scaled_low_ns * cnt.LowPart) >> scale);
+}
+
+#else
+# define highres_nanos() 0
+#endif
+
+static inline uint64_t gettimeofday_nanos(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
+}
+
+/*
+ * Returns nanoseconds since the epoch (01/01/1970), for performance tracing
+ * (i.e. favoring high precision over wall clock time accuracy).
+ */
+inline uint64_t getnanotime(void)
+{
+ static uint64_t offset;
+ if (offset > 1) {
+ /* initialization succeeded, return offset + high res time */
+ return offset + highres_nanos();
+ } else if (offset == 1) {
+ /* initialization failed, fall back to gettimeofday */
+ return gettimeofday_nanos();
+ } else {
+ /* initialize offset if high resolution timer works */
+ uint64_t now = gettimeofday_nanos();
+ uint64_t highres = highres_nanos();
+ if (highres)
+ offset = now - highres;
+ else
+ offset = 1;
+ return now;
+ }
+}
+
+static uint64_t command_start_time;
+static struct strbuf command_line = STRBUF_INIT;
+
+static void print_command_performance_atexit(void)
+{
+ trace_performance_since(command_start_time, "git command:%s",
+ command_line.buf);
+}
+
+void trace_command_performance(const char **argv)
+{
+ if (!trace_want(&trace_perf_key))
+ return;
+
+ if (!command_start_time)
+ atexit(print_command_performance_atexit);
+
+ strbuf_reset(&command_line);
+ sq_quote_argv(&command_line, argv, 0);
+ command_start_time = getnanotime();
}
}
struct unix_sockaddr_context {
- char orig_dir[PATH_MAX];
+ char *orig_dir;
};
static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx)
{
- if (!ctx->orig_dir[0])
+ if (!ctx->orig_dir)
return;
/*
* If we fail, we can't just return an error, since we have
*/
if (chdir(ctx->orig_dir) < 0)
die("unable to restore original working directory");
+ free(ctx->orig_dir);
}
static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
{
int size = strlen(path) + 1;
- ctx->orig_dir[0] = '\0';
+ ctx->orig_dir = NULL;
if (size > sizeof(sa->sun_path)) {
const char *slash = find_last_dir_sep(path);
const char *dir;
+ struct strbuf cwd = STRBUF_INIT;
if (!slash) {
errno = ENAMETOOLONG;
errno = ENAMETOOLONG;
return -1;
}
-
- if (!getcwd(ctx->orig_dir, sizeof(ctx->orig_dir))) {
- errno = ENAMETOOLONG;
+ if (strbuf_getcwd(&cwd))
return -1;
- }
+ ctx->orig_dir = strbuf_detach(&cwd, NULL);
if (chdir_len(dir, slash - dir) < 0)
return -1;
}
struct sockaddr_un sa;
struct unix_sockaddr_context ctx;
+ unlink(path);
+
if (unix_sockaddr_init(&sa, path, &ctx) < 0)
return -1;
fd = unix_stream_socket();
- unlink(path);
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
goto fail;