Update various codepaths to avoid manually-counted malloc().
* jk/tighten-alloc: (22 commits)
ewah: convert to REALLOC_ARRAY, etc
convert ewah/bitmap code to use xmalloc
diff_populate_gitlink: use a strbuf
transport_anonymize_url: use xstrfmt
git-compat-util: drop mempcpy compat code
sequencer: simplify memory allocation of get_message
test-path-utils: fix normalize_path_copy output buffer size
fetch-pack: simplify add_sought_entry
fast-import: simplify allocation in start_packfile
write_untracked_extension: use FLEX_ALLOC helper
prepare_{git,shell}_cmd: use argv_array
use st_add and st_mult for allocation size computation
convert trivial cases to FLEX_ARRAY macros
use xmallocz to avoid size arithmetic
convert trivial cases to ALLOC_ARRAY
convert manual allocations to argv_array
argv-array: add detach function
add helpers for allocating flex-array structs
harden REALLOC_ARRAY and xcalloc against size_t overflow
tree-diff: catch integer overflow in combine_diff_path allocation
...
if (!fp)
die_errno("Could not open file '%s'", filename);
- while (strbuf_getline(&str, fp, '\n') != EOF) {
+ while (strbuf_getline_lf(&str, fp) != EOF) {
strbuf_trim(&str);
if (sq_dequote_to_argv_array(str.buf, array))
die("Badly quoted content in file '%s': %s",
if (!fp)
return 0;
- if (strbuf_getline(&str, fp, '\n') != EOF)
+ if (strbuf_getline_lf(&str, fp) != EOF)
res = !strcmp(str.buf, oid_to_hex(oid));
strbuf_release(&str);
static struct commit **get_bad_and_good_commits(int *rev_nr)
{
- int len = 1 + good_revs.nr;
- struct commit **rev = xmalloc(len * sizeof(*rev));
+ struct commit **rev;
int i, n = 0;
+ ALLOC_ARRAY(rev, 1 + good_revs.nr);
rev[n++] = get_commit_reference(current_bad_oid->hash);
for (i = 0; i < good_revs.nr; i++)
rev[n++] = get_commit_reference(good_revs.sha1[i]);
strerror(errno));
}
} else {
- strbuf_getline(&str, fp, '\n');
+ strbuf_getline_lf(&str, fp);
*read_bad = strbuf_detach(&str, NULL);
- strbuf_getline(&str, fp, '\n');
+ strbuf_getline_lf(&str, fp);
*read_good = strbuf_detach(&str, NULL);
}
strbuf_release(&str);
insert_count = postimage->len;
/* Adjust the contents */
- result = xmalloc(img->len + insert_count - remove_count + 1);
+ result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1));
memcpy(result, img->buf, applied_at);
memcpy(result + applied_at, postimage->buf, postimage->len);
memcpy(result + applied_at + postimage->len,
return 0;
}
-static int option_parse_z(const struct option *opt,
- const char *arg, int unset)
-{
- if (unset)
- line_termination = '\n';
- else
- line_termination = 0;
- return 0;
-}
-
static int option_parse_space_change(const struct option *opt,
const char *arg, int unset)
{
N_( "attempt three-way merge if a patch does not apply")),
OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
N_("build a temporary index based on embedded index information")),
- { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
- N_("paths are separated with NUL character"),
- PARSE_OPT_NOARG, option_parse_z },
+ /* Think twice before adding "--nul" synonym to this */
+ OPT_SET_INT('z', NULL, &line_termination,
+ N_("paths are separated with NUL character"), '\0'),
OPT_INTEGER('C', NULL, &p_context,
N_("ensure at least <n> lines of context match")),
{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
#include "line-range.h"
#include "line-log.h"
#include "dir.h"
+#include "progress.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
static int xdl_opts;
static int abbrev = -1;
static int no_whole_file_rename;
+static int show_progress;
static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
char path[FLEX_ARRAY];
};
+struct progress_info {
+ struct progress *progress;
+ int blamed_lines;
+};
+
static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
{
static struct origin *make_origin(struct commit *commit, const char *path)
{
struct origin *o;
- size_t pathlen = strlen(path) + 1;
- o = xcalloc(1, sizeof(*o) + pathlen);
+ FLEX_ALLOC_STR(o, path, path);
o->commit = commit;
o->refcnt = 1;
o->next = commit->util;
commit->util = o;
- memcpy(o->path, path, pathlen); /* includes NUL */
return o;
}
* The blame_entry is found to be guilty for the range.
* Show it in incremental output.
*/
-static void found_guilty_entry(struct blame_entry *ent)
+static void found_guilty_entry(struct blame_entry *ent,
+ struct progress_info *pi)
{
if (incremental) {
struct origin *suspect = ent->suspect;
write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout");
}
+ pi->blamed_lines += ent->num_lines;
+ display_progress(pi->progress, pi->blamed_lines);
}
/*
{
struct rev_info *revs = sb->revs;
struct commit *commit = prio_queue_get(&sb->commits);
+ struct progress_info pi = { NULL, 0 };
+
+ if (show_progress)
+ pi.progress = start_progress_delay(_("Blaming lines"),
+ sb->num_lines, 50, 1);
while (commit) {
struct blame_entry *ent;
suspect->guilty = 1;
for (;;) {
struct blame_entry *next = ent->next;
- found_guilty_entry(ent);
+ found_guilty_entry(ent, &pi);
if (next) {
ent = next;
continue;
if (DEBUG) /* sanity */
sanity_check_refcnt(sb);
}
+
+ stop_progress(&pi.progress);
}
static const char *format_time(unsigned long time, const char *tz_str,
for (p = buf; p < end; p = get_next_line(p, end))
num++;
- sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
+ ALLOC_ARRAY(sb->lineno, num + 1);
+ lineno = sb->lineno;
for (p = buf; p < end; p = get_next_line(p, end))
*lineno++ = p - buf;
ce->ce_mode = create_ce_mode(mode);
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
- /*
- * We are not going to write this out, so this does not matter
- * right now, but someday we might optimize diff-index --cached
- * with cache-tree information.
- */
cache_tree_invalidate_path(&the_index, path);
return commit;
OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
+ OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
save_commit_buffer = 0;
dashdash_pos = 0;
+ show_progress = -1;
parse_options_start(&ctx, argc, argv, prefix, options,
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
argc = parse_options_end(&ctx);
+ if (incremental || (output_option & OUTPUT_PORCELAIN)) {
+ if (show_progress > 0)
+ die("--progress can't be used with --incremental or porcelain formats");
+ show_progress = 0;
+ } else if (show_progress < 0)
+ show_progress = isatty(2);
+
if (0 < abbrev)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
read_mailmap(&mailmap, NULL);
+ assign_blame(&sb, opt);
+
if (!incremental)
setup_pager();
- assign_blame(&sb, opt);
-
free(final_commit_name);
if (incremental)
int eof = 0;
int i;
- chosen = xmalloc(sizeof(int) * stuff->nr);
+ ALLOC_ARRAY(chosen, stuff->nr);
/* set chosen as uninitialized */
for (i = 0; i < stuff->nr; i++)
chosen[i] = -1;
clean_get_color(CLEAN_COLOR_RESET));
}
- if (strbuf_getline(&choice, stdin, '\n') != EOF) {
+ if (strbuf_getline_lf(&choice, stdin) != EOF) {
strbuf_trim(&choice);
} else {
eof = 1;
nr += chosen[i];
}
- result = xcalloc(nr + 1, sizeof(int));
+ result = xcalloc(st_add(nr, 1), sizeof(int));
for (i = 0; i < stuff->nr && j < nr; i++) {
if (chosen[i])
result[j++] = i;
clean_print_color(CLEAN_COLOR_PROMPT);
printf(_("Input ignore patterns>> "));
clean_print_color(CLEAN_COLOR_RESET);
- if (strbuf_getline(&confirm, stdin, '\n') != EOF)
+ if (strbuf_getline_lf(&confirm, stdin) != EOF)
strbuf_trim(&confirm);
else
putchar('\n');
qname = quote_path_relative(item->string, NULL, &buf);
/* TRANSLATORS: Make sure to keep [y/N] as is */
printf(_("Remove %s [y/N]? "), qname);
- if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+ if (strbuf_getline_lf(&confirm, stdin) != EOF) {
strbuf_trim(&confirm);
} else {
putchar('\n');
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
- static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
- const char *name, int namelen)
+ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
+ const char *name)
{
- struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
+ struct ref *ref;
struct object_id oid;
- const int chunksz = GIT_SHA1_HEXSZ + 1;
- if (namelen > chunksz && name[chunksz - 1] == ' ' &&
- !get_oid_hex(name, &oid)) {
- oidcpy(&ref->old_oid, &oid);
- name += chunksz;
- namelen -= chunksz;
- }
+ if (!get_oid_hex(name, &oid) && name[GIT_SHA1_HEXSZ] == ' ')
+ name += GIT_SHA1_HEXSZ + 1;
+ else
+ oidclr(&oid);
- memcpy(ref->name, name, namelen);
- ref->name[namelen] = '\0';
+ ref = alloc_ref(name);
+ oidcpy(&ref->old_oid, &oid);
(*nr)++;
ALLOC_GROW(*sought, *nr, *alloc);
(*sought)[*nr - 1] = ref;
}
- static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
- const char *string)
- {
- add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
- }
-
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
{
int i, ret;
else {
/* read from stdin one ref per line, until EOF */
struct strbuf line = STRBUF_INIT;
- while (strbuf_getline(&line, stdin, '\n') != EOF)
+ while (strbuf_getline_lf(&line, stdin) != EOF)
add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
strbuf_release(&line);
}
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int tags = TAGS_DEFAULT, unshallow, update_shallow;
+static int max_children = 1;
+static enum transport_family family;
static const char *depth;
static const char *upload_pack;
static struct strbuf default_rla = STRBUF_INIT;
N_("fetch all tags and associated objects"), TAGS_SET),
OPT_SET_INT('n', NULL, &tags,
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+ OPT_INTEGER('j', "jobs", &max_children,
+ N_("number of submodules fetched in parallel")),
OPT_BOOL('p', "prune", &prune,
N_("prune remote-tracking branches no longer on remote")),
{ OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
N_("accept refs that update .git/shallow")),
{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+ OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+ TRANSPORT_FAMILY_IPV4),
+ OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+ TRANSPORT_FAMILY_IPV6),
OPT_END()
};
struct transport *transport;
transport = transport_get(remote, NULL);
transport_set_verbosity(transport, verbosity, progress);
+ transport->family = family;
if (upload_pack)
set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
if (keep)
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
- struct remote *remote;
- if (!remote_is_configured(name))
+ struct remote *remote = remote_get(name);
+ if (!remote_is_configured(remote))
return 0;
- remote = remote_get(name);
string_list_append(list, remote->name);
}
return 1;
if (argc > 0) {
int j = 0;
int i;
- refs = xcalloc(argc + 1, sizeof(const char *));
+ refs = xcalloc(st_add(argc, 1), sizeof(const char *));
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "tag")) {
i++;
result = fetch_populated_submodules(&options,
submodule_prefix,
recurse_submodules,
- verbosity < 0);
+ verbosity < 0,
+ max_children);
argv_array_clear(&options);
}
NULL
};
-static int use_threads = 1;
+#define GREP_NUM_THREADS_DEFAULT 8
+static int num_threads;
#ifndef NO_PTHREADS
-#define THREADS 8
-static pthread_t threads[THREADS];
+static pthread_t *threads;
/* We use one producer thread and THREADS consumer
* threads. The producer adds struct work_items to 'todo' and the
static inline void grep_lock(void)
{
- if (use_threads)
+ if (num_threads)
pthread_mutex_lock(&grep_mutex);
}
static inline void grep_unlock(void)
{
- if (use_threads)
+ if (num_threads)
pthread_mutex_unlock(&grep_mutex);
}
strbuf_init(&todo[i].out, 0);
}
- for (i = 0; i < ARRAY_SIZE(threads); i++) {
+ threads = xcalloc(num_threads, sizeof(*threads));
+ for (i = 0; i < num_threads; i++) {
int err;
struct grep_opt *o = grep_opt_dup(opt);
o->output = strbuf_out;
pthread_cond_broadcast(&cond_add);
grep_unlock();
- for (i = 0; i < ARRAY_SIZE(threads); i++) {
+ for (i = 0; i < num_threads; i++) {
void *h;
pthread_join(threads[i], &h);
hit |= (int) (intptr_t) h;
}
+ free(threads);
+
pthread_mutex_destroy(&grep_mutex);
pthread_mutex_destroy(&grep_read_mutex);
pthread_mutex_destroy(&grep_attr_mutex);
int st = grep_config(var, value, cb);
if (git_color_default_config(var, value, cb) < 0)
st = -1;
+
+ if (!strcmp(var, "grep.threads")) {
+ num_threads = git_config_int(var, value);
+ if (num_threads < 0)
+ die(_("invalid number of threads specified (%d) for %s"),
+ num_threads, var);
+ }
+
return st;
}
}
#ifndef NO_PTHREADS
- if (use_threads) {
+ if (num_threads) {
add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
strbuf_release(&pathbuf);
return 0;
strbuf_addstr(&buf, filename);
#ifndef NO_PTHREADS
- if (use_threads) {
+ if (num_threads) {
add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
strbuf_release(&buf);
return 0;
static void run_pager(struct grep_opt *opt, const char *prefix)
{
struct string_list *path_list = opt->output_priv;
- const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
+ struct child_process child = CHILD_PROCESS_INIT;
int i, status;
for (i = 0; i < path_list->nr; i++)
- argv[i] = path_list->items[i].string;
- argv[path_list->nr] = NULL;
+ argv_array_push(&child.args, path_list->items[i].string);
+ child.dir = prefix;
+ child.use_shell = 1;
- status = run_command_v_opt_cd_env(argv, RUN_USING_SHELL, prefix, NULL);
+ status = run_command(&child);
if (status)
exit(status);
- free(argv);
}
static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
for (nr = 0; nr < active_nr; nr++) {
const struct cache_entry *ce = active_cache[nr];
- if (!S_ISREG(ce->ce_mode))
+ if (!S_ISREG(ce->ce_mode) || ce_intent_to_add(ce))
continue;
if (!ce_path_match(ce, pathspec, NULL))
continue;
patterns = from_stdin ? stdin : fopen(arg, "r");
if (!patterns)
die_errno(_("cannot open '%s'"), arg);
- while (strbuf_getline(&sb, patterns, '\n') == 0) {
+ while (strbuf_getline(&sb, patterns) == 0) {
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
N_("show <n> context lines before matches")),
OPT_INTEGER('A', "after-context", &opt.post_context,
N_("show <n> context lines after matches")),
+ OPT_INTEGER(0, "threads", &num_threads,
+ N_("use <n> worker threads")),
OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
context_callback),
OPT_BOOL('p', "show-function", &opt.funcname,
PARSE_OPT_STOP_AT_NON_OPTION);
grep_commit_pattern_type(pattern_type_arg, &opt);
- if (use_index && !startup_info->have_repository)
- /* die the same way as if we did it at the beginning */
- setup_git_directory();
+ if (use_index && !startup_info->have_repository) {
+ int fallback = 0;
+ git_config_get_bool("grep.fallbacktonoindex", &fallback);
+ if (fallback)
+ use_index = 0;
+ else
+ /* die the same way as if we did it at the beginning */
+ setup_git_directory();
+ }
/*
* skip a -- separator; we know it cannot be
opt.output_priv = &path_list;
opt.output = append_path;
string_list_append(&path_list, show_in_pager);
- use_threads = 0;
}
if (!opt.pattern_list)
}
#ifndef NO_PTHREADS
- if (list.nr || cached || online_cpus() == 1)
- use_threads = 0;
+ if (list.nr || cached || show_in_pager)
+ num_threads = 0;
+ else if (num_threads == 0)
+ num_threads = GREP_NUM_THREADS_DEFAULT;
+ else if (num_threads < 0)
+ die(_("invalid number of threads specified (%d)"), num_threads);
#else
- use_threads = 0;
+ num_threads = 0;
#endif
#ifndef NO_PTHREADS
- if (use_threads) {
+ if (num_threads) {
if (!(opt.name_only || opt.unmatch_name_only || opt.count)
&& (opt.pre_context || opt.post_context ||
opt.file_break || opt.funcbody))
hit = grep_objects(&opt, &pathspec, &list);
}
- if (use_threads)
+ if (num_threads)
hit |= wait_all();
if (hit && show_in_pager)
run_pager(&opt, prefix);
static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
{
struct treeent *ent;
- int len = strlen(path);
+ size_t len = strlen(path);
if (strchr(path, '/'))
die("path %s contains slash", path);
- ALLOC_GROW(entries, used + 1, alloc);
- ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
+ FLEX_ALLOC_MEM(ent, name, path, len);
ent->mode = mode;
ent->len = len;
hashcpy(ent->sha1, sha1);
- memcpy(ent->name, path, len+1);
+
+ ALLOC_GROW(entries, used + 1, alloc);
+ entries[used++] = ent;
}
static int ent_compare(const void *a_, const void *b_)
NULL
};
-static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing)
+static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_missing)
{
char *ptr, *ntr;
unsigned mode;
*ntr++ = 0; /* now at the beginning of SHA1 */
path = ntr + 41; /* at the beginning of name */
- if (line_termination && path[0] == '"') {
+ if (!nul_term_line && path[0] == '"') {
struct strbuf p_uq = STRBUF_INIT;
if (unquote_c_style(&p_uq, path, NULL))
die("invalid quoting");
{
struct strbuf sb = STRBUF_INIT;
unsigned char sha1[20];
- int line_termination = '\n';
+ int nul_term_line = 0;
int allow_missing = 0;
int is_batch_mode = 0;
int got_eof = 0;
+ strbuf_getline_fn getline_fn;
const struct option option[] = {
- OPT_SET_INT('z', NULL, &line_termination, N_("input is NUL terminated"), '\0'),
+ OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")),
OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1),
OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1),
OPT_END()
};
ac = parse_options(ac, av, prefix, option, mktree_usage, 0);
+ getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
while (!got_eof) {
while (1) {
- if (strbuf_getline(&sb, stdin, line_termination) == EOF) {
+ if (getline_fn(&sb, stdin) == EOF) {
got_eof = 1;
break;
}
break;
die("input format error: (blank line only valid in batch mode)");
}
- mktree_line(sb.buf, sb.len, line_termination, allow_missing);
+ mktree_line(sb.buf, sb.len, nul_term_line, allow_missing);
}
if (is_batch_mode && got_eof && used < 1) {
/*
{
unsigned int i, wo_end, last_untagged;
- struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo));
+ struct object_entry **wo;
struct object_entry *objects = to_pack.objects;
for (i = 0; i < to_pack.nr_objects; i++) {
* Give the objects in the original recency order until
* we see a tagged tip.
*/
+ ALLOC_ARRAY(wo, to_pack.nr_objects);
for (i = wo_end = 0; i < to_pack.nr_objects; i++) {
if (objects[i].tagged)
break;
if (progress > pack_to_stdout)
progress_state = start_progress(_("Writing objects"), nr_result);
- written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list));
+ ALLOC_ARRAY(written_list, to_pack.nr_objects);
write_order = compute_write_order();
do {
if (!to_pack.nr_objects || !window || !depth)
return;
- delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list));
+ ALLOC_ARRAY(delta_list, to_pack.nr_objects);
nr_deltas = n = 0;
for (i = 0; i < to_pack.nr_objects; i++) {
index_commit_for_bitmap(commit);
}
-static void show_object(struct object *obj,
- const struct name_path *path, const char *last,
- void *data)
+static void show_object(struct object *obj, const char *name, void *data)
{
- char *name = path_name(path, last);
-
add_preferred_base_object(name);
add_object_entry(obj->oid.hash, obj->type, name, 0);
obj->flags |= OBJECT_ADDED;
-
- /*
- * We will have generated the hash from the name,
- * but not saved a pointer to it - we can free it
- */
- free((char *)name);
}
static void show_edge(struct commit *commit)
}
static void record_recent_object(struct object *obj,
- const struct name_path *path,
- const char *last,
+ const char *name,
void *data)
{
sha1_array_append(&recent_objects, obj->oid.hash);
return 1;
}
len = st.st_size;
- path = xmalloc(len + 1);
+ path = xmallocz(len);
read_in_full(fd, path, len);
close(fd);
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
die(_("'%s' already exists"), path);
/* is 'refname' a branch or commit? */
- if (opts->force_new_branch) /* definitely a branch */
- ;
- else if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
+ if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
ref_exists(symref.buf)) { /* it's a branch */
if (!opts->force)
die_if_checked_out(symref.buf);
branch = ac < 2 ? "HEAD" : av[1];
opts.force_new_branch = !!new_branch_force;
- if (opts.force_new_branch)
+ if (opts.force_new_branch) {
+ struct strbuf symref = STRBUF_INIT;
+
opts.new_branch = new_branch_force;
+ if (!opts.force &&
+ !strbuf_check_branch_ref(&symref, opts.new_branch) &&
+ ref_exists(symref.buf))
+ die_if_checked_out(symref.buf);
+ strbuf_release(&symref);
+ }
+
if (ac < 2 && !opts.new_branch && !opts.detach) {
int n;
const char *s = worktree_basename(path, &n);
ALLOC_GROW(it->down, it->subtree_nr + 1, it->subtree_alloc);
it->subtree_nr++;
- down = xmalloc(sizeof(*down) + pathlen + 1);
+ FLEX_ALLOC_MEM(down, name, path, pathlen);
down->cache_tree = NULL;
down->namelen = pathlen;
- memcpy(down->name, path, pathlen);
- down->name[pathlen] = 0;
if (pos < it->subtree_nr)
memmove(it->down + pos + 1,
* they are not part of generated trees. Invalidate up
* to root to force cache-tree users to read elsewhere.
*/
- if (ce->ce_flags & CE_INTENT_TO_ADD) {
+ if (ce_intent_to_add(ce)) {
to_invalidate = 1;
continue;
}
#include "../run-command.h"
#include "../cache.h"
+#define HCAST(type, handle) ((type)(intptr_t)handle)
+
static const int delay[] = { 0, 1, 10, 20, 40 };
int err_win_to_posix(DWORD winerr)
return (time_t)(filetime_to_hnsec(ft) / 10000000);
}
+/**
+ * Verifies that safe_create_leading_directories() would succeed.
+ */
+static int has_valid_directory_prefix(wchar_t *wfilename)
+{
+ int n = wcslen(wfilename);
+
+ while (n > 0) {
+ wchar_t c = wfilename[--n];
+ DWORD attributes;
+
+ if (!is_dir_sep(c))
+ continue;
+
+ wfilename[n] = L'\0';
+ attributes = GetFileAttributesW(wfilename);
+ wfilename[n] = c;
+ if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
+ attributes == FILE_ATTRIBUTE_DEVICE)
+ return 1;
+ if (attributes == INVALID_FILE_ATTRIBUTES)
+ switch (GetLastError()) {
+ case ERROR_PATH_NOT_FOUND:
+ continue;
+ case ERROR_FILE_NOT_FOUND:
+ /* This implies parent directory exists. */
+ return 1;
+ }
+ return 0;
+ }
+ return 1;
+}
+
/* We keep the do_lstat code in a separate function to avoid recursion.
* When a path ends with a slash, the stat will fail with ENOENT. In
* this case, we strip the trailing slashes and stat again.
case ERROR_NOT_ENOUGH_MEMORY:
errno = ENOMEM;
break;
+ case ERROR_PATH_NOT_FOUND:
+ if (!has_valid_directory_prefix(wfilename)) {
+ errno = ENOTDIR;
+ break;
+ }
+ /* fallthru */
default:
errno = ENOENT;
break;
errno = err_win_to_posix(GetLastError());
return -1;
}
- filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
+ filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT);
if (filedes[0] < 0) {
CloseHandle(h[0]);
CloseHandle(h[1]);
return -1;
}
- filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
+ filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT);
if (filedes[1] < 0) {
close(filedes[0]);
CloseHandle(h[1]);
return arg;
/* insert \ where necessary */
- d = q = xmalloc(len+n+3);
+ d = q = xmalloc(st_add3(len, n, 3));
*d++ = '"';
while (*arg) {
if (*arg == '"')
if (!n)
return NULL;
- path = xmalloc((n+1)*sizeof(char *));
+ ALLOC_ARRAY(path, n + 1);
p = envpath;
i = 0;
do {
i++;
/* copy the environment, leaving space for changes */
- tmpenv = xmalloc((size + i) * sizeof(char*));
+ ALLOC_ARRAY(tmpenv, size + i);
memcpy(tmpenv, environ, size * sizeof(char*));
/* merge supplied environment changes into the temporary environment */
free(quoted);
}
- wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
+ wargs = xmalloc_array(st_add(st_mult(2, args.len), 1), sizeof(wchar_t));
xutftowcs(wargs, args.buf, 2 * args.len + 1);
strbuf_release(&args);
int argc = 0;
const char **argv2;
while (argv[argc]) argc++;
- argv2 = xmalloc(sizeof(*argv) * (argc+1));
+ ALLOC_ARRAY(argv2, argc + 1);
argv2[0] = (char *)cmd; /* full path to the script file */
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
pid = mingw_spawnv(prog, argv2, 1);
if (gle == ERROR_ACCESS_DENIED &&
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
- errno = EISDIR;
+ DWORD attrsold = GetFileAttributesW(wpold);
+ if (attrsold == INVALID_FILE_ATTRIBUTES ||
+ !(attrsold & FILE_ATTRIBUTE_DIRECTORY))
+ errno = EISDIR;
+ else if (!_wrmdir(wpnew))
+ goto repeat;
return -1;
}
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
die("cannot run browser");
printf("Launching default browser to display HTML ...\n");
- r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
+ r = HCAST(int, ShellExecute(NULL, "open", htmlpath,
+ NULL, "\\", SW_SHOWNORMAL));
FreeLibrary(shell32);
/* see the MSDN documentation referring to the result codes here */
if (r <= 32) {
return -1;
}
+static void setup_windows_environment()
+{
+ char *tmp = getenv("TMPDIR");
+
+ /* on Windows it is TMP and TEMP */
+ if (!tmp) {
+ if (!(tmp = getenv("TMP")))
+ tmp = getenv("TEMP");
+ if (tmp) {
+ setenv("TMPDIR", tmp, 1);
+ tmp = getenv("TMPDIR");
+ }
+ }
+
+ if (tmp) {
+ /*
+ * Convert all dir separators to forward slashes,
+ * to help shell commands called from the Git
+ * executable (by not mistaking the dir separators
+ * for escape characters).
+ */
+ for (; *tmp; tmp++)
+ if (*tmp == '\\')
+ *tmp = '/';
+ }
+
+ /* simulate TERM to enable auto-color (see color.c) */
+ if (!getenv("TERM"))
+ setenv("TERM", "cygwin", 1);
+}
+
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
* mingw startup code, see init.c in mingw runtime).
qsort(environ, i, sizeof(char*), compareenv);
/* fix Windows specific environment settings */
-
- /* on Windows it is TMP and TEMP */
- if (!mingw_getenv("TMPDIR")) {
- const char *tmp = mingw_getenv("TMP");
- if (!tmp)
- tmp = mingw_getenv("TEMP");
- if (tmp)
- setenv("TMPDIR", tmp, 1);
- }
-
- /* simulate TERM to enable auto-color (see color.c) */
- if (!getenv("TERM"))
- setenv("TERM", "cygwin", 1);
+ setup_windows_environment();
/* initialize critical section for waitpid pinfo_t list */
InitializeCriticalSection(&pinfo_cs);
return ret;
}
+int git_config_get_untracked_cache(void)
+{
+ int val = -1;
+ const char *v;
+
+ /* Hack for test programs like test-dump-untracked-cache */
+ if (ignore_untracked_cache_config)
+ return -1;
+
+ if (!git_config_get_maybe_bool("core.untrackedcache", &val))
+ return val;
+
+ if (!git_config_get_value("core.untrackedcache", &v)) {
+ if (!strcasecmp(v, "keep"))
+ return -1;
+
+ error("unknown core.untrackedCache value '%s'; "
+ "using 'keep' default value", v);
+ return -1;
+ }
+
+ return -1; /* default value */
+}
+
NORETURN
void git_die_config_linenr(const char *key, const char *filename, int linenr)
{
* Validate the key and while at it, lower case it for matching.
*/
if (store_key)
- *store_key = xmalloc(strlen(key) + 1);
+ *store_key = xmallocz(strlen(key));
dot = 0;
for (i = 0; key[i]; i++) {
if (store_key)
(*store_key)[i] = c;
}
- if (store_key)
- (*store_key)[i] = 0;
return 0;
return;
}
- while (strbuf_getline(&line, fp, '\n') != EOF) {
+ while (strbuf_getline_lf(&line, fp) != EOF) {
logerror("%s", line.buf);
strbuf_setlen(&line, 0);
}
cradle = &blanket->next;
}
- static char **cld_argv;
+ static struct argv_array cld_argv = ARGV_ARRAY_INIT;
static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
{
struct child_process cld = CHILD_PROCESS_INIT;
#endif
}
- cld.argv = (const char **)cld_argv;
+ cld.argv = cld_argv.argv;
cld.in = incoming;
cld.out = dup(incoming);
write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
/* prepare argv for serving-processes */
- cld_argv = xmalloc(sizeof (char *) * (argc + 2));
- cld_argv[0] = argv[0]; /* git-daemon */
- cld_argv[1] = "--serve";
+ argv_array_push(&cld_argv, argv[0]); /* git-daemon */
+ argv_array_push(&cld_argv, "--serve");
for (i = 1; i < argc; ++i)
- cld_argv[i+1] = argv[i];
- cld_argv[argc+1] = NULL;
+ argv_array_push(&cld_argv, argv[i]);
return serve(&listen_addr, listen_port, cred);
}
struct diff_filespec *alloc_filespec(const char *path)
{
- int namelen = strlen(path);
- struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
+ struct diff_filespec *spec;
- memset(spec, 0, sizeof(*spec));
- spec->path = (char *)(spec + 1);
- memcpy(spec->path, path, namelen+1);
+ FLEXPTR_ALLOC_STR(spec, path, path);
spec->count = 1;
spec->is_binary = -1;
return spec;
static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
{
- int len;
- char *data = xmalloc(100), *dirty = "";
+ struct strbuf buf = STRBUF_INIT;
+ char *dirty = "";
/* Are we looking at the work tree? */
if (s->dirty_submodule)
dirty = "-dirty";
- len = snprintf(data, 100,
- "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
- s->data = data;
- s->size = len;
- s->should_free = 1;
+ strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
+ s->size = buf.len;
if (size_only) {
s->data = NULL;
- free(data);
+ strbuf_release(&buf);
+ } else {
+ s->data = strbuf_detach(&buf, NULL);
+ s->should_free = 1;
}
return 0;
}
{
size_t size;
- if (!driver || !driver->textconv) {
+ if (!driver) {
if (!DIFF_FILE_VALID(df)) {
*outbuf = "";
return 0;
return df->size;
}
+ if (!driver->textconv)
+ die("BUG: fill_textconv called with non-textconv driver");
+
if (driver->textconv_cache && df->sha1_valid) {
*outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
&size);
} parent[FLEX_ARRAY];
};
#define combine_diff_path_size(n, l) \
- (sizeof(struct combine_diff_path) + \
- sizeof(struct combine_diff_parent) * (n) + (l) + 1)
+ st_add4(sizeof(struct combine_diff_path), (l), 1, \
+ st_mult(sizeof(struct combine_diff_parent), (n)))
extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
int dense, struct rev_info *);
extern int index_differs_from(const char *def, int diff_flags);
+/*
+ * Fill the contents of the filespec "df", respecting any textconv defined by
+ * its userdiff driver. The "driver" parameter must come from a
+ * previous call to get_textconv(), and therefore should either be NULL or have
+ * textconv enabled.
+ *
+ * Note that the memory ownership of the resulting buffer depends on whether
+ * the driver field is NULL. If it is, then the memory belongs to the filespec
+ * struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer
+ * that should be freed by the caller.
+ */
extern size_t fill_textconv(struct userdiff_driver *driver,
struct diff_filespec *df,
char **outbuf);
+/*
+ * Look up the userdiff driver for the given filespec, and return it if
+ * and only if it has textconv enabled (otherwise return NULL). The result
+ * can be passed to fill_textconv().
+ */
extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
extern int parse_rename_score(const char **cp_p);
int check_only, const struct path_simplify *simplify);
static int get_dtype(struct dirent *de, const char *path, int len);
+static struct trace_key trace_exclude = TRACE_KEY_INIT(EXCLUDE);
+
/* helper string functions with support for the ignore_case flag */
int strcmp_icase(const char *a, const char *b)
{
parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
if (flags & EXC_FLAG_MUSTBEDIR) {
- char *s;
- x = xmalloc(sizeof(*x) + patternlen + 1);
- s = (char *)(x+1);
- memcpy(s, string, patternlen);
- s[patternlen] = '\0';
- x->pattern = s;
+ FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen);
} else {
x = xmalloc(sizeof(*x));
x->pattern = string;
x->baselen = baselen;
x->flags = flags;
x->srcpos = srcpos;
+ string_list_init(&x->sticky_paths, 1);
ALLOC_GROW(el->excludes, el->nr + 1, el->alloc);
el->excludes[el->nr++] = x;
x->el = el;
{
int i;
- for (i = 0; i < el->nr; i++)
+ for (i = 0; i < el->nr; i++) {
+ string_list_clear(&el->excludes[i]->sticky_paths, 0);
free(el->excludes[i]);
+ }
free(el->excludes);
free(el->filebuf);
}
uc->dir_created++;
- d = xmalloc(sizeof(*d) + len + 1);
- memset(d, 0, sizeof(*d));
- memcpy(d->name, name, len);
- d->name[len] = '\0';
+ FLEX_ALLOC_MEM(d, name, name, len);
ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
memmove(dir->dirs + first + 1, dir->dirs + first,
return 0;
}
if (buf[size-1] != '\n') {
- buf = xrealloc(buf, size+1);
+ buf = xrealloc(buf, st_add(size, 1));
buf[size++] = '\n';
}
} else {
close(fd);
return 0;
}
- buf = xmalloc(size+1);
+ buf = xmallocz(size);
if (read_in_full(fd, buf, size) != size) {
free(buf);
close(fd);
* then our prefix match is all we need; we
* do not need to call fnmatch at all.
*/
- if (!patternlen && !namelen)
+ if (!patternlen && (!namelen || *name == '/'))
return 1;
}
WM_PATHNAME) == 0;
}
+static void add_sticky(struct exclude *exc, const char *pathname, int pathlen)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int i;
+
+ for (i = exc->sticky_paths.nr - 1; i >= 0; i--) {
+ const char *sticky = exc->sticky_paths.items[i].string;
+ int len = strlen(sticky);
+
+ if (pathlen < len && sticky[pathlen] == '/' &&
+ !strncmp(pathname, sticky, pathlen))
+ return;
+ }
+
+ strbuf_add(&sb, pathname, pathlen);
+ string_list_append_nodup(&exc->sticky_paths, strbuf_detach(&sb, NULL));
+}
+
+static int match_sticky(struct exclude *exc, const char *pathname, int pathlen, int dtype)
+{
+ int i;
+
+ for (i = exc->sticky_paths.nr - 1; i >= 0; i--) {
+ const char *sticky = exc->sticky_paths.items[i].string;
+ int len = strlen(sticky);
+
+ if (pathlen == len && dtype == DT_DIR &&
+ !strncmp(pathname, sticky, len))
+ return 1;
+
+ if (pathlen > len && pathname[len] == '/' &&
+ !strncmp(pathname, sticky, len))
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int different_decisions(const struct exclude *a,
+ const struct exclude *b)
+{
+ return (a->flags & EXC_FLAG_NEGATIVE) != (b->flags & EXC_FLAG_NEGATIVE);
+}
+
+/*
+ * Return non-zero if pathname is a directory and an ancestor of the
+ * literal path in a pattern.
+ */
+static int match_directory_part(const char *pathname, int pathlen,
+ int *dtype, struct exclude *x)
+{
+ const char *base = x->base;
+ int baselen = x->baselen ? x->baselen - 1 : 0;
+ const char *pattern = x->pattern;
+ int prefix = x->nowildcardlen;
+ int patternlen = x->patternlen;
+
+ if (*dtype == DT_UNKNOWN)
+ *dtype = get_dtype(NULL, pathname, pathlen);
+ if (*dtype != DT_DIR)
+ return 0;
+
+ if (*pattern == '/') {
+ pattern++;
+ patternlen--;
+ prefix--;
+ }
+
+ if (baselen) {
+ if (((pathlen < baselen && base[pathlen] == '/') ||
+ pathlen == baselen) &&
+ !strncmp_icase(pathname, base, pathlen))
+ return 1;
+ pathname += baselen + 1;
+ pathlen -= baselen + 1;
+ }
+
+
+ if (prefix &&
+ (((pathlen < prefix && pattern[pathlen] == '/') ||
+ pathlen == prefix) &&
+ !strncmp_icase(pathname, pattern, pathlen)))
+ return 1;
+
+ return 0;
+}
+
+static struct exclude *should_descend(const char *pathname, int pathlen,
+ int *dtype, struct exclude_list *el,
+ struct exclude *exc)
+{
+ int i;
+
+ for (i = el->nr - 1; 0 <= i; i--) {
+ struct exclude *x = el->excludes[i];
+
+ if (x == exc)
+ break;
+
+ if (!(x->flags & EXC_FLAG_NODIR) &&
+ different_decisions(x, exc) &&
+ match_directory_part(pathname, pathlen, dtype, x))
+ return x;
+ }
+ return NULL;
+}
+
/*
* Scan the given exclude list in reverse to see whether pathname
* should be ignored. The first match (i.e. the last on the list), if
struct exclude_list *el)
{
struct exclude *exc = NULL; /* undecided */
- int i;
+ int i, maybe_descend = 0;
if (!el->nr)
return NULL; /* undefined */
+ trace_printf_key(&trace_exclude, "exclude: from %s\n", el->src);
+
for (i = el->nr - 1; 0 <= i; i--) {
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
int prefix = x->nowildcardlen;
+ if (!maybe_descend && i < el->nr - 1 &&
+ different_decisions(x, el->excludes[i+1]))
+ maybe_descend = 1;
+
+ if (x->sticky_paths.nr) {
+ if (*dtype == DT_UNKNOWN)
+ *dtype = get_dtype(NULL, pathname, pathlen);
+ if (match_sticky(x, pathname, pathlen, *dtype)) {
+ exc = x;
+ break;
+ }
+ continue;
+ }
+
if (x->flags & EXC_FLAG_MUSTBEDIR) {
if (*dtype == DT_UNKNOWN)
*dtype = get_dtype(NULL, pathname, pathlen);
break;
}
}
+
+ if (!exc) {
+ trace_printf_key(&trace_exclude, "exclude: %.*s => n/a\n",
+ pathlen, pathname);
+ return NULL;
+ }
+
+ /*
+ * We have found a matching pattern "exc" that may exclude whole
+ * directory. We also found that there may be a pattern that matches
+ * something inside the directory and reincludes stuff.
+ *
+ * Go through the patterns again, find that pattern and double check.
+ * If it's true, return "undecided" and keep descending in. "exc" is
+ * marked sticky so that it continues to match inside the directory.
+ */
+ if (!(exc->flags & EXC_FLAG_NEGATIVE) && maybe_descend) {
+ struct exclude *x;
+
+ if (*dtype == DT_UNKNOWN)
+ *dtype = get_dtype(NULL, pathname, pathlen);
+
+ if (*dtype == DT_DIR &&
+ (x = should_descend(pathname, pathlen, dtype, el, exc))) {
+ add_sticky(exc, pathname, pathlen);
+ trace_printf_key(&trace_exclude,
+ "exclude: %.*s vs %s at line %d => %s,"
+ " forced open by %s at line %d => n/a\n",
+ pathlen, pathname, exc->pattern, exc->srcpos,
+ exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes",
+ x->pattern, x->srcpos);
+ return NULL;
+ }
+ }
+
+ trace_printf_key(&trace_exclude, "exclude: %.*s vs %s at line %d => %s%s\n",
+ pathlen, pathname, exc->pattern, exc->srcpos,
+ exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes",
+ exc->sticky_paths.nr ? " (stuck)" : "");
return exc;
}
{
struct dir_entry *ent;
- ent = xmalloc(sizeof(*ent) + len + 1);
+ FLEX_ALLOC_MEM(ent, name, pathname, len);
ent->len = len;
- memcpy(ent->name, pathname, len);
- ent->name[len] = 0;
return ent;
}
struct cached_dir cdir;
enum path_treatment state, subdir_state, dir_state = path_none;
struct strbuf path = STRBUF_INIT;
+ static int level = 0;
strbuf_add(&path, base, baselen);
+ trace_printf_key(&trace_exclude, "exclude: [%d] enter '%.*s'\n",
+ level++, baselen, base);
+
if (open_cached_dir(&cdir, dir, untracked, &path, check_only))
goto out;
}
close_cached_dir(&cdir);
out:
+ trace_printf_key(&trace_exclude, "exclude: [%d] leave '%.*s'\n",
+ --level, baselen, base);
strbuf_release(&path);
return dir_state;
return sb.buf;
if (uname(&uts) < 0)
die_errno(_("failed to get kernel name and information"));
- strbuf_addf(&sb, "Location %s, system %s %s %s", get_git_work_tree(),
- uts.sysname, uts.release, uts.version);
+ strbuf_addf(&sb, "Location %s, system %s", get_git_work_tree(),
+ uts.sysname);
return sb.buf;
}
static int ident_in_untracked(const struct untracked_cache *uc)
{
- const char *end = uc->ident.buf + uc->ident.len;
- const char *p = uc->ident.buf;
+ /*
+ * Previous git versions may have saved many NUL separated
+ * strings in the "ident" field, but it is insane to manage
+ * many locations, so just take care of the first one.
+ */
- for (p = uc->ident.buf; p < end; p += strlen(p) + 1)
- if (!strcmp(p, get_ident_string()))
- return 1;
- return 0;
+ return !strcmp(uc->ident.buf, get_ident_string());
}
-void add_untracked_ident(struct untracked_cache *uc)
+static void set_untracked_ident(struct untracked_cache *uc)
{
- if (ident_in_untracked(uc))
- return;
+ strbuf_reset(&uc->ident);
strbuf_addstr(&uc->ident, get_ident_string());
- /* this strbuf contains a list of strings, save NUL too */
+
+ /*
+ * This strbuf used to contain a list of NUL separated
+ * strings, so save NUL too for backward compatibility.
+ */
strbuf_addch(&uc->ident, 0);
}
+static void new_untracked_cache(struct index_state *istate)
+{
+ struct untracked_cache *uc = xcalloc(1, sizeof(*uc));
+ strbuf_init(&uc->ident, 100);
+ uc->exclude_per_dir = ".gitignore";
+ /* should be the same flags used by git-status */
+ uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+ set_untracked_ident(uc);
+ istate->untracked = uc;
+ istate->cache_changed |= UNTRACKED_CHANGED;
+}
+
+void add_untracked_cache(struct index_state *istate)
+{
+ if (!istate->untracked) {
+ new_untracked_cache(istate);
+ } else {
+ if (!ident_in_untracked(istate->untracked)) {
+ free_untracked_cache(istate->untracked);
+ new_untracked_cache(istate);
+ }
+ }
+}
+
+void remove_untracked_cache(struct index_state *istate)
+{
+ if (istate->untracked) {
+ free_untracked_cache(istate->untracked);
+ istate->untracked = NULL;
+ istate->cache_changed |= UNTRACKED_CHANGED;
+ }
+}
+
static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
int base_len,
const struct pathspec *pathspec)
return NULL;
if (!ident_in_untracked(dir->untracked)) {
- warning(_("Untracked cache is disabled on this system."));
+ warning(_("Untracked cache is disabled on this system or location."));
return NULL;
}
return root;
}
+static void clear_sticky(struct dir_struct *dir)
+{
+ struct exclude_list_group *g;
+ struct exclude_list *el;
+ struct exclude *x;
+ int i, j, k;
+
+ for (i = EXC_CMDL; i <= EXC_FILE; i++) {
+ g = &dir->exclude_list_group[i];
+ for (j = g->nr - 1; j >= 0; j--) {
+ el = &g->el[j];
+ for (k = el->nr - 1; 0 <= k; k--) {
+ x = el->excludes[k];
+ string_list_clear(&x->sticky_paths, 0);
+ }
+ }
+ }
+}
+
int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
{
struct path_simplify *simplify;
if (has_symlink_leading_path(path, len))
return dir->nr;
+ /*
+ * Stay on the safe side. if read_directory() has run once on
+ * "dir", some sticky flag may have been left. Clear them all.
+ */
+ clear_sticky(dir);
+
/*
* exclude patterns are treated like positive ones in
* create_simplify. Usually exclude patterns should be a
struct ondisk_untracked_cache *ouc;
struct write_data wd;
unsigned char varbuf[16];
- int len = 0, varint_len;
- if (untracked->exclude_per_dir)
- len = strlen(untracked->exclude_per_dir);
- ouc = xmalloc(sizeof(*ouc) + len + 1);
+ int varint_len;
+ size_t len = strlen(untracked->exclude_per_dir);
+
+ FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
ouc->dir_flags = htonl(untracked->dir_flags);
- memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1);
varint_len = encode_varint(untracked->ident.len, varbuf);
strbuf_add(out, varbuf, varint_len);
ud.untracked_alloc = value;
ud.untracked_nr = value;
if (ud.untracked_nr)
- ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr);
+ ALLOC_ARRAY(ud.untracked, ud.untracked_nr);
data = next;
next = data;
ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
if (next > end)
return -1;
- ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr);
+ ALLOC_ARRAY(ud.dirs, ud.dirs_nr);
data = next;
len = strlen((const char *)data);
next = data + len + 1;
if (next > rd->end)
return -1;
- *untracked_ = untracked = xmalloc(sizeof(*untracked) + len);
+ *untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len));
memcpy(untracked, &ud, sizeof(ud));
memcpy(untracked->name, data, len + 1);
data = next;
rd.data = next;
rd.end = end;
rd.index = 0;
- rd.ucd = xmalloc(sizeof(*rd.ucd) * len);
+ ALLOC_ARRAY(rd.ucd, len);
if (read_one_dir(&uc->root, &rd) || rd.index != len)
goto done;
#include "cache.h"
#include "exec_cmd.h"
#include "quote.h"
+ #include "argv-array.h"
#define MAX_ARGS 32
static const char *argv_exec_path;
if (!argv0 || !*argv0)
return NULL;
- slash = argv0 + strlen(argv0);
- while (argv0 <= slash && !is_dir_sep(*slash))
- slash--;
+ slash = find_last_dir_sep(argv0);
- if (slash >= argv0) {
+ if (slash) {
argv0_path = xstrndup(argv0, slash - argv0);
return slash + 1;
}
strbuf_release(&new_path);
}
- const char **prepare_git_cmd(const char **argv)
+ const char **prepare_git_cmd(struct argv_array *out, const char **argv)
{
- int argc;
- const char **nargv;
-
- for (argc = 0; argv[argc]; argc++)
- ; /* just counting */
- nargv = xmalloc(sizeof(*nargv) * (argc + 2));
-
- nargv[0] = "git";
- for (argc = 0; argv[argc]; argc++)
- nargv[argc + 1] = argv[argc];
- nargv[argc + 1] = NULL;
- return nargv;
+ argv_array_push(out, "git");
+ argv_array_pushv(out, argv);
+ return out->argv;
}
int execv_git_cmd(const char **argv) {
- const char **nargv = prepare_git_cmd(argv);
- trace_argv_printf(nargv, "trace: exec:");
+ struct argv_array nargv = ARGV_ARRAY_INIT;
+
+ prepare_git_cmd(&nargv, argv);
+ trace_argv_printf(nargv.argv, "trace: exec:");
/* execvp() can only ever return if it fails */
- sane_execvp("git", (char **)nargv);
+ sane_execvp("git", (char **)nargv.argv);
trace_printf("trace: exec failed: %s\n", strerror(errno));
- free(nargv);
+ argv_array_clear(&nargv);
return -1;
}
return xmalloc(len);
}
total_allocd += sizeof(struct mem_pool) + mem_pool_alloc;
- p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc);
+ p = xmalloc(st_add(sizeof(struct mem_pool), mem_pool_alloc));
p->next_pool = mem_pool;
p->next_free = (char *) p->space;
p->end = p->next_free + mem_pool_alloc;
if (!avail_tree_entry) {
unsigned int n = tree_entry_alloc;
total_allocd += n * sizeof(struct tree_entry);
- avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry));
+ ALLOC_ARRAY(e, n);
+ avail_tree_entry = e;
while (n-- > 1) {
*((void**)e) = e + 1;
e++;
{
static char tmp_file[PATH_MAX];
struct packed_git *p;
- int namelen;
struct pack_header hdr;
int pack_fd;
pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
"pack/tmp_pack_XXXXXX");
- namelen = strlen(tmp_file) + 2;
- p = xcalloc(1, sizeof(*p) + namelen);
- xsnprintf(p->pack_name, namelen, "%s", tmp_file);
+ FLEX_ALLOC_STR(p, pack_name, tmp_file);
p->pack_fd = pack_fd;
p->do_not_close = 1;
pack_file = sha1fd(pack_fd, p->pack_name);
struct object_entry_pool *o;
/* Build the table of object IDs. */
- idx = xmalloc(object_count * sizeof(*idx));
+ ALLOC_ARRAY(idx, object_count);
c = idx;
for (o = blocks; o; o = o->next_pool)
for (e = o->next_free; e-- != o->entries;)
struct recent_command *rc;
strbuf_detach(&command_buf, NULL);
- stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
+ stdin_eof = strbuf_getline_lf(&command_buf, stdin);
if (stdin_eof)
return EOF;
strbuf_detach(&command_buf, NULL);
for (;;) {
- if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
+ if (strbuf_getline_lf(&command_buf, stdin) == EOF)
die("EOF in data (terminator '%s' not found)", term);
if (term_len == command_buf.len
&& !strcmp(term, command_buf.buf))
#define unsigned_add_overflows(a, b) \
((b) > maximum_unsigned_value_of_type(a) - (a))
+ /*
+ * Returns true if the multiplication of "a" and "b" will
+ * overflow. The types of "a" and "b" must match and must be unsigned.
+ * Note that this macro evaluates "a" twice!
+ */
+ #define unsigned_mult_overflows(a, b) \
+ ((a) && (b) > maximum_unsigned_value_of_type(a) / (a))
+
#ifdef __GNUC__
#define TYPEOF(x) (__typeof__(x))
#else
#define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
#endif
-#ifndef STRIP_EXTENSION
-#define STRIP_EXTENSION ""
-#endif
-
#ifndef has_dos_drive_prefix
static inline int git_has_dos_drive_prefix(const char *path)
{
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 1)
#define HAVE_STRCHRNUL
- #define HAVE_MEMPCPY
#endif
#endif
}
#endif
- #ifndef HAVE_MEMPCPY
- #define mempcpy gitmempcpy
- static inline void *gitmempcpy(void *dest, const void *src, size_t n)
- {
- return (char *)memcpy(dest, src, n) + n;
- }
- #endif
-
#ifdef NO_INET_PTON
int inet_pton(int af, const char *src, void *dst);
#endif
typedef void (*try_to_free_t)(size_t);
extern try_to_free_t set_try_to_free_routine(try_to_free_t);
+ static inline size_t st_add(size_t a, size_t b)
+ {
+ if (unsigned_add_overflows(a, b))
+ die("size_t overflow: %"PRIuMAX" + %"PRIuMAX,
+ (uintmax_t)a, (uintmax_t)b);
+ return a + b;
+ }
+ #define st_add3(a,b,c) st_add((a),st_add((b),(c)))
+ #define st_add4(a,b,c,d) st_add((a),st_add3((b),(c),(d)))
+
+ static inline size_t st_mult(size_t a, size_t b)
+ {
+ if (unsigned_mult_overflows(a, b))
+ die("size_t overflow: %"PRIuMAX" * %"PRIuMAX,
+ (uintmax_t)a, (uintmax_t)b);
+ return a * b;
+ }
+
+ static inline size_t st_sub(size_t a, size_t b)
+ {
+ if (a < b)
+ die("size_t underflow: %"PRIuMAX" - %"PRIuMAX,
+ (uintmax_t)a, (uintmax_t)b);
+ return a - b;
+ }
+
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
# define xalloca(size) (alloca(size))
extern char *xgetcwd(void);
extern FILE *fopen_for_writing(const char *path);
- #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x)))
+ #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
+ #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
+
+ /*
+ * These functions help you allocate structs with flex arrays, and copy
+ * the data directly into the array. For example, if you had:
+ *
+ * struct foo {
+ * int bar;
+ * char name[FLEX_ARRAY];
+ * };
+ *
+ * you can do:
+ *
+ * struct foo *f;
+ * FLEX_ALLOC_MEM(f, name, src, len);
+ *
+ * to allocate a "foo" with the contents of "src" in the "name" field.
+ * The resulting struct is automatically zero'd, and the flex-array field
+ * is NUL-terminated (whether the incoming src buffer was or not).
+ *
+ * The FLEXPTR_* variants operate on structs that don't use flex-arrays,
+ * but do want to store a pointer to some extra data in the same allocated
+ * block. For example, if you have:
+ *
+ * struct foo {
+ * char *name;
+ * int bar;
+ * };
+ *
+ * you can do:
+ *
+ * struct foo *f;
+ * FLEX_ALLOC_STR(f, name, src);
+ *
+ * and "name" will point to a block of memory after the struct, which will be
+ * freed along with the struct (but the pointer can be repointed anywhere).
+ *
+ * The *_STR variants accept a string parameter rather than a ptr/len
+ * combination.
+ *
+ * Note that these macros will evaluate the first parameter multiple
+ * times, and it must be assignable as an lvalue.
+ */
+ #define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \
+ (x) = NULL; /* silence -Wuninitialized for offset calculation */ \
+ (x) = xalloc_flex(sizeof(*(x)), (char *)(&((x)->flexname)) - (char *)(x), (buf), (len)); \
+ } while (0)
+ #define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \
+ (x) = xalloc_flex(sizeof(*(x)), sizeof(*(x)), (buf), (len)); \
+ (x)->ptrname = (void *)((x)+1); \
+ } while(0)
+ #define FLEX_ALLOC_STR(x, flexname, str) \
+ FLEX_ALLOC_MEM((x), flexname, (str), strlen(str))
+ #define FLEXPTR_ALLOC_STR(x, ptrname, str) \
+ FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
+
+ static inline void *xalloc_flex(size_t base_len, size_t offset,
+ const void *src, size_t src_len)
+ {
+ unsigned char *ret = xcalloc(1, st_add3(base_len, src_len, 1));
+ memcpy(ret + offset, src, src_len);
+ return ret;
+ }
static inline char *xstrdup_or_null(const char *str)
{
GIT_PREFIX_ENVIRONMENT
};
static char *orig_env[4];
-static int saved_environment;
+static int save_restore_env_balance;
-static void save_env(void)
+static void save_env_before_alias(void)
{
int i;
- if (saved_environment)
- return;
- saved_environment = 1;
+
+ assert(save_restore_env_balance == 0);
+ save_restore_env_balance = 1;
orig_cwd = xgetcwd();
for (i = 0; i < ARRAY_SIZE(env_names); i++) {
orig_env[i] = getenv(env_names[i]);
}
}
-static void restore_env(void)
+static void restore_env(int external_alias)
{
int i;
- if (orig_cwd && chdir(orig_cwd))
+
+ assert(save_restore_env_balance == 1);
+ save_restore_env_balance = 0;
+ if (!external_alias && 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])
+ if (external_alias &&
+ !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
+ continue;
+ if (orig_env[i]) {
setenv(env_names[i], orig_env[i], 1);
- else
+ free(orig_env[i]);
+ } else {
unsetenv(env_names[i]);
+ }
}
}
static int handle_alias(int *argcp, const char ***argv)
{
int envchanged = 0, ret = 0, saved_errno = errno;
- const char *subdir;
int count, option_count;
const char **new_argv;
const char *alias_command;
char *alias_string;
int unused_nongit;
- subdir = setup_git_directory_gently(&unused_nongit);
+ save_env_before_alias();
+ setup_git_directory_gently(&unused_nongit);
alias_command = (*argv)[0];
alias_string = alias_lookup(alias_command);
if (alias_string) {
if (alias_string[0] == '!') {
- const char **alias_argv;
- int argc = *argcp, i;
+ struct child_process child = CHILD_PROCESS_INIT;
commit_pager_choice();
+ restore_env(1);
- /* build alias_argv */
- alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
- alias_argv[0] = alias_string + 1;
- for (i = 1; i < argc; ++i)
- alias_argv[i] = (*argv)[i];
- alias_argv[argc] = NULL;
+ child.use_shell = 1;
+ argv_array_push(&child.args, alias_string + 1);
+ argv_array_pushv(&child.args, (*argv) + 1);
- ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+ ret = run_command(&child);
if (ret >= 0) /* normal exit */
exit(ret);
ret = 1;
}
- if (subdir && chdir(subdir))
- die_errno("Cannot change to '%s'", subdir);
+ restore_env(0);
errno = saved_errno;
* 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;
{ "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, NO_SETUP },
+ { "clone", cmd_clone },
{ "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, NO_SETUP },
- { "init-db", cmd_init_db, NO_SETUP },
+ { "init", cmd_init_db },
+ { "init-db", cmd_init_db },
{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
{ "log", cmd_log, RUN_SETUP },
{ "ls-files", cmd_ls_files, RUN_SETUP },
return !!get_builtin(s);
}
+#ifdef STRIP_EXTENSION
+static void strip_extension(const char **argv)
+{
+ size_t len;
+
+ if (strip_suffix(argv[0], STRIP_EXTENSION, &len))
+ argv[0] = xmemdupz(argv[0], len);
+}
+#else
+#define strip_extension(cmd)
+#endif
+
static void handle_builtin(int argc, const char **argv)
{
- const char *cmd = argv[0];
- int i;
- static const char ext[] = STRIP_EXTENSION;
+ const char *cmd;
struct cmd_struct *builtin;
- if (sizeof(ext) > 1) {
- i = strlen(argv[0]) - strlen(ext);
- if (i > 0 && !strcmp(argv[0] + i, ext)) {
- char *argv0 = xstrdup(argv[0]);
- argv[0] = cmd = argv0;
- argv0[i] = '\0';
- }
- }
+ strip_extension(argv);
+ cmd = argv[0];
/* Turn "git cmd --help" into "git help cmd" */
if (argc > 1 && !strcmp(argv[1], "--help")) {
}
builtin = get_builtin(cmd);
- if (builtin) {
- if (saved_environment && (builtin->option & NO_SETUP))
- restore_env();
- else
- exit(run_builtin(builtin, argc, argv));
- }
+ if (builtin)
+ exit(run_builtin(builtin, argc, argv));
}
static void execv_dashed_external(const char **argv)
int done_alias = 0;
while (1) {
- /* See if it's a builtin */
- handle_builtin(*argcp, *argv);
+ /*
+ * If we tried alias and futzed with our environment,
+ * it no longer is safe to invoke builtins directly in
+ * general. We have to spawn them as dashed externals.
+ *
+ * NEEDSWORK: if we can figure out cases
+ * where it is safe to do, we can avoid spawning a new
+ * process.
+ */
+ if (!done_alias)
+ handle_builtin(*argcp, *argv);
/* .. then try the external ones */
execv_dashed_external(*argv);
*/
if (done_alias)
break;
- save_env();
if (!handle_alias(argcp, argv))
break;
done_alias = 1;
t->first_non_note = NULL;
t->prev_non_note = NULL;
t->ref = xstrdup_or_null(notes_ref);
+ t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL;
t->combine_notes = combine_notes;
t->initialized = 1;
t->dirty = 0;
if (flags & NOTES_INIT_EMPTY || !notes_ref ||
- read_ref(notes_ref, object_sha1))
+ get_sha1_treeish(notes_ref, object_sha1))
return;
+ if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, object_sha1))
+ die("Cannot use notes ref %s", notes_ref);
if (get_tree_entry(object_sha1, "", sha1, &mode))
die("Failed to read notes tree referenced by %s (%s)",
notes_ref, sha1_to_hex(object_sha1));
load_subtree(t, &root_tree, t->root, 0);
}
-struct notes_tree **load_notes_trees(struct string_list *refs)
+struct notes_tree **load_notes_trees(struct string_list *refs, int flags)
{
struct string_list_item *item;
int counter = 0;
struct notes_tree **trees;
- trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
+ ALLOC_ARRAY(trees, refs->nr + 1);
for_each_string_list_item(item, refs) {
struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
- init_notes(t, item->string, combine_notes_ignore, 0);
+ init_notes(t, item->string, combine_notes_ignore, flags);
trees[counter++] = t;
}
trees[counter] = NULL;
item->string);
}
- display_notes_trees = load_notes_trees(&display_notes_refs);
+ display_notes_trees = load_notes_trees(&display_notes_refs, 0);
string_list_clear(&display_notes_refs, 0);
}
else
strbuf_insert(sb, 0, "refs/notes/", 11);
}
+
+void expand_loose_notes_ref(struct strbuf *sb)
+{
+ unsigned char object[20];
+
+ if (get_sha1(sb->buf, object)) {
+ /* fallback to expand_notes_ref */
+ expand_notes_ref(sb);
+ }
+}
* size is easily available by examining the pack entry header). It is
* also rather expensive to find the sha1 for an object given its offset.
*
- * We build a hashtable of existing packs (pack_revindex), and keep reverse
- * index here -- pack index file is sorted by object name mapping to offset;
- * this pack_revindex[].revindex array is a list of offset/index_nr pairs
+ * The pack index file is sorted by object name mapping to offset;
+ * this revindex array is a list of offset/index_nr pairs
* ordered by offset, so if you know the offset of an object, next offset
* is where its packed representation ends and the index_nr can be used to
* get the object sha1 from the main index.
*/
-static struct pack_revindex *pack_revindex;
-static int pack_revindex_hashsz;
-
-static int pack_revindex_ix(struct packed_git *p)
-{
- unsigned long ui = (unsigned long)(intptr_t)p;
- int i;
-
- ui = ui ^ (ui >> 16); /* defeat structure alignment */
- i = (int)(ui % pack_revindex_hashsz);
- while (pack_revindex[i].p) {
- if (pack_revindex[i].p == p)
- return i;
- if (++i == pack_revindex_hashsz)
- i = 0;
- }
- return -1 - i;
-}
-
-static void init_pack_revindex(void)
-{
- int num;
- struct packed_git *p;
-
- for (num = 0, p = packed_git; p; p = p->next)
- num++;
- if (!num)
- return;
- pack_revindex_hashsz = num * 11;
- pack_revindex = xcalloc(pack_revindex_hashsz, sizeof(*pack_revindex));
- for (p = packed_git; p; p = p->next) {
- num = pack_revindex_ix(p);
- num = - 1 - num;
- pack_revindex[num].p = p;
- }
- /* revindex elements are lazily initialized */
-}
-
/*
* This is a least-significant-digit radix sort.
*
* keep track of them with alias pointers, always sorting from "from"
* to "to".
*/
- struct revindex_entry *tmp = xmalloc(n * sizeof(*tmp));
- struct revindex_entry *from = entries, *to = tmp;
+ struct revindex_entry *tmp, *from, *to;
int bits;
- unsigned *pos = xmalloc(BUCKETS * sizeof(*pos));
+ unsigned *pos;
+
+ ALLOC_ARRAY(pos, BUCKETS);
+ ALLOC_ARRAY(tmp, n);
+ from = entries;
+ to = tmp;
/*
* If (max >> bits) is zero, then we know that the radix digit we are
/*
* Ordered list of offsets of objects in the pack.
*/
-static void create_pack_revindex(struct pack_revindex *rix)
+static void create_pack_revindex(struct packed_git *p)
{
- struct packed_git *p = rix->p;
unsigned num_ent = p->num_objects;
unsigned i;
const char *index = p->index_data;
- p->revindex = xmalloc(sizeof(*p->revindex) * (num_ent + 1));
- ALLOC_ARRAY(rix->revindex, num_ent + 1);
++ ALLOC_ARRAY(p->revindex, num_ent + 1);
index += 4 * 256;
if (p->index_version > 1) {
for (i = 0; i < num_ent; i++) {
uint32_t off = ntohl(*off_32++);
if (!(off & 0x80000000)) {
- rix->revindex[i].offset = off;
+ p->revindex[i].offset = off;
} else {
- rix->revindex[i].offset =
+ p->revindex[i].offset =
((uint64_t)ntohl(*off_64++)) << 32;
- rix->revindex[i].offset |=
+ p->revindex[i].offset |=
ntohl(*off_64++);
}
- rix->revindex[i].nr = i;
+ p->revindex[i].nr = i;
}
} else {
for (i = 0; i < num_ent; i++) {
uint32_t hl = *((uint32_t *)(index + 24 * i));
- rix->revindex[i].offset = ntohl(hl);
- rix->revindex[i].nr = i;
+ p->revindex[i].offset = ntohl(hl);
+ p->revindex[i].nr = i;
}
}
/* This knows the pack format -- the 20-byte trailer
* follows immediately after the last object data.
*/
- rix->revindex[num_ent].offset = p->pack_size - 20;
- rix->revindex[num_ent].nr = -1;
- sort_revindex(rix->revindex, num_ent, p->pack_size);
+ p->revindex[num_ent].offset = p->pack_size - 20;
+ p->revindex[num_ent].nr = -1;
+ sort_revindex(p->revindex, num_ent, p->pack_size);
}
-struct pack_revindex *revindex_for_pack(struct packed_git *p)
+void load_pack_revindex(struct packed_git *p)
{
- int num;
- struct pack_revindex *rix;
-
- if (!pack_revindex_hashsz)
- init_pack_revindex();
-
- num = pack_revindex_ix(p);
- if (num < 0)
- die("internal error: pack revindex fubar");
-
- rix = &pack_revindex[num];
- if (!rix->revindex)
- create_pack_revindex(rix);
-
- return rix;
+ if (!p->revindex)
+ create_pack_revindex(p);
}
-int find_revindex_position(struct pack_revindex *pridx, off_t ofs)
+int find_revindex_position(struct packed_git *p, off_t ofs)
{
int lo = 0;
- int hi = pridx->p->num_objects + 1;
- struct revindex_entry *revindex = pridx->revindex;
+ int hi = p->num_objects + 1;
+ struct revindex_entry *revindex = p->revindex;
do {
unsigned mi = lo + (hi - lo) / 2;
struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
{
- struct pack_revindex *pridx = revindex_for_pack(p);
- int pos = find_revindex_position(pridx, ofs);
+ int pos;
+
+ load_pack_revindex(p);
+ pos = find_revindex_position(p, ofs);
if (pos < 0)
return NULL;
- return pridx->revindex + pos;
+ return p->revindex + pos;
}
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
+struct align {
+ align_type position;
+ unsigned int width;
+};
+
+/*
+ * An atom is a valid field atom listed below, possibly prefixed with
+ * a "*" to denote deref_tag().
+ *
+ * We parse given format string and sort specifiers, and make a list
+ * of properties that we need to extract out of objects. ref_array_item
+ * structure will hold an array of values extracted that can be
+ * indexed with the "atom number", which is an index into this
+ * array.
+ */
+static struct used_atom {
+ const char *name;
+ cmp_type type;
+ union {
+ char color[COLOR_MAXLEN];
+ struct align align;
+ enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
+ remote_ref;
+ struct {
+ enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;
+ unsigned int nlines;
+ } contents;
+ enum { O_FULL, O_SHORT } objectname;
+ } u;
+} *used_atom;
+static int used_atom_cnt, need_tagged, need_symref;
+static int need_color_reset_at_eol;
+
+static void color_atom_parser(struct used_atom *atom, const char *color_value)
+{
+ if (!color_value)
+ die(_("expected format: %%(color:<color>)"));
+ if (color_parse(color_value, atom->u.color) < 0)
+ die(_("unrecognized color: %%(color:%s)"), color_value);
+}
+
+static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
+{
+ if (!arg)
+ atom->u.remote_ref = RR_NORMAL;
+ else if (!strcmp(arg, "short"))
+ atom->u.remote_ref = RR_SHORTEN;
+ else if (!strcmp(arg, "track"))
+ atom->u.remote_ref = RR_TRACK;
+ else if (!strcmp(arg, "trackshort"))
+ atom->u.remote_ref = RR_TRACKSHORT;
+ else
+ die(_("unrecognized format: %%(%s)"), atom->name);
+}
+
+static void body_atom_parser(struct used_atom *atom, const char *arg)
+{
+ if (arg)
+ die("%%(body) does not take arguments");
+ atom->u.contents.option = C_BODY_DEP;
+}
+
+static void subject_atom_parser(struct used_atom *atom, const char *arg)
+{
+ if (arg)
+ die("%%(subject) does not take arguments");
+ atom->u.contents.option = C_SUB;
+}
+
+static void contents_atom_parser(struct used_atom *atom, const char *arg)
+{
+ if (!arg)
+ atom->u.contents.option = C_BARE;
+ else if (!strcmp(arg, "body"))
+ atom->u.contents.option = C_BODY;
+ else if (!strcmp(arg, "signature"))
+ atom->u.contents.option = C_SIG;
+ else if (!strcmp(arg, "subject"))
+ atom->u.contents.option = C_SUB;
+ else if (skip_prefix(arg, "lines=", &arg)) {
+ atom->u.contents.option = C_LINES;
+ if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
+ die(_("positive value expected contents:lines=%s"), arg);
+ } else
+ die(_("unrecognized %%(contents) argument: %s"), arg);
+}
+
+static void objectname_atom_parser(struct used_atom *atom, const char *arg)
+{
+ if (!arg)
+ atom->u.objectname = O_FULL;
+ else if (!strcmp(arg, "short"))
+ atom->u.objectname = O_SHORT;
+ else
+ die(_("unrecognized %%(objectname) argument: %s"), arg);
+}
+
+static align_type parse_align_position(const char *s)
+{
+ if (!strcmp(s, "right"))
+ return ALIGN_RIGHT;
+ else if (!strcmp(s, "middle"))
+ return ALIGN_MIDDLE;
+ else if (!strcmp(s, "left"))
+ return ALIGN_LEFT;
+ return -1;
+}
+
+static void align_atom_parser(struct used_atom *atom, const char *arg)
+{
+ struct align *align = &atom->u.align;
+ struct string_list params = STRING_LIST_INIT_DUP;
+ int i;
+ unsigned int width = ~0U;
+
+ if (!arg)
+ die(_("expected format: %%(align:<width>,<position>)"));
+
+ align->position = ALIGN_LEFT;
+
+ string_list_split(¶ms, arg, ',', -1);
+ for (i = 0; i < params.nr; i++) {
+ const char *s = params.items[i].string;
+ int position;
+
+ if (skip_prefix(s, "position=", &s)) {
+ position = parse_align_position(s);
+ if (position < 0)
+ die(_("unrecognized position:%s"), s);
+ align->position = position;
+ } else if (skip_prefix(s, "width=", &s)) {
+ if (strtoul_ui(s, 10, &width))
+ die(_("unrecognized width:%s"), s);
+ } else if (!strtoul_ui(s, 10, &width))
+ ;
+ else if ((position = parse_align_position(s)) >= 0)
+ align->position = position;
+ else
+ die(_("unrecognized %%(align) argument: %s"), s);
+ }
+
+ if (width == ~0U)
+ die(_("positive width expected with the %%(align) atom"));
+ align->width = width;
+ string_list_clear(¶ms, 0);
+}
+
static struct {
const char *name;
cmp_type cmp_type;
+ void (*parser)(struct used_atom *atom, const char *arg);
} valid_atom[] = {
{ "refname" },
{ "objecttype" },
{ "objectsize", FIELD_ULONG },
- { "objectname" },
+ { "objectname", FIELD_STR, objectname_atom_parser },
{ "tree" },
{ "parent" },
{ "numparent", FIELD_ULONG },
{ "taggerdate", FIELD_TIME },
{ "creator" },
{ "creatordate", FIELD_TIME },
- { "subject" },
- { "body" },
- { "contents" },
- { "upstream" },
- { "push" },
+ { "subject", FIELD_STR, subject_atom_parser },
+ { "body", FIELD_STR, body_atom_parser },
+ { "contents", FIELD_STR, contents_atom_parser },
+ { "upstream", FIELD_STR, remote_ref_atom_parser },
+ { "push", FIELD_STR, remote_ref_atom_parser },
{ "symref" },
{ "flag" },
{ "HEAD" },
- { "color" },
- { "align" },
+ { "color", FIELD_STR, color_atom_parser },
+ { "align", FIELD_STR, align_atom_parser },
{ "end" },
};
#define REF_FORMATTING_STATE_INIT { 0, NULL }
-struct align {
- align_type position;
- unsigned int width;
-};
-
-struct contents {
- unsigned int lines;
- struct object_id oid;
-};
-
struct ref_formatting_stack {
struct ref_formatting_stack *prev;
struct strbuf output;
const char *s;
union {
struct align align;
- struct contents contents;
} u;
void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
unsigned long ul; /* used for sorting when not FIELD_STR */
};
-/*
- * An atom is a valid field atom listed above, possibly prefixed with
- * a "*" to denote deref_tag().
- *
- * We parse given format string and sort specifiers, and make a list
- * of properties that we need to extract out of objects. ref_array_item
- * structure will hold an array of values extracted that can be
- * indexed with the "atom number", which is an index into this
- * array.
- */
-static const char **used_atom;
-static cmp_type *used_atom_type;
-static int used_atom_cnt, need_tagged, need_symref;
-static int need_color_reset_at_eol;
-
/*
* Used to parse format string and sort specifiers
*/
int parse_ref_filter_atom(const char *atom, const char *ep)
{
const char *sp;
+ const char *arg;
int i, at;
sp = atom;
/* Do we have the atom already used elsewhere? */
for (i = 0; i < used_atom_cnt; i++) {
- int len = strlen(used_atom[i]);
- if (len == ep - atom && !memcmp(used_atom[i], atom, len))
+ int len = strlen(used_atom[i].name);
+ if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
return i;
}
/* Is the atom a valid one? */
for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
int len = strlen(valid_atom[i].name);
+
/*
* If the atom name has a colon, strip it and everything after
* it off - it specifies the format for this entry, and
* shouldn't be used for checking against the valid_atom
* table.
*/
- const char *formatp = strchr(sp, ':');
- if (!formatp || ep < formatp)
- formatp = ep;
- if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
+ arg = memchr(sp, ':', ep - sp);
+ if (len == (arg ? arg : ep) - sp &&
+ !memcmp(valid_atom[i].name, sp, len))
break;
}
at = used_atom_cnt;
used_atom_cnt++;
REALLOC_ARRAY(used_atom, used_atom_cnt);
- REALLOC_ARRAY(used_atom_type, used_atom_cnt);
- used_atom[at] = xmemdupz(atom, ep - atom);
- used_atom_type[at] = valid_atom[i].cmp_type;
+ used_atom[at].name = xmemdupz(atom, ep - atom);
+ used_atom[at].type = valid_atom[i].cmp_type;
+ if (arg)
+ arg = used_atom[at].name + (arg - atom) + 1;
+ memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
+ if (valid_atom[i].parser)
+ valid_atom[i].parser(&used_atom[at], arg);
if (*atom == '*')
need_tagged = 1;
- if (!strcmp(used_atom[at], "symref"))
+ if (!strcmp(used_atom[at].name, "symref"))
need_symref = 1;
return at;
}
pop_stack_element(&state->stack);
}
-static int match_atom_name(const char *name, const char *atom_name, const char **val)
-{
- const char *body;
-
- if (!skip_prefix(name, atom_name, &body))
- return 0; /* doesn't even begin with "atom_name" */
- if (!body[0]) {
- *val = NULL; /* %(atom_name) and no customization */
- return 1;
- }
- if (body[0] != ':')
- return 0; /* "atom_namefoo" is not "atom_name" or "atom_name:..." */
- *val = body + 1; /* "atom_name:val" */
- return 1;
-}
-
/*
* In a format string, find the next occurrence of %(atom).
*/
at = parse_ref_filter_atom(sp + 2, ep);
cp = ep + 1;
- if (skip_prefix(used_atom[at], "color:", &color))
+ if (skip_prefix(used_atom[at].name, "color:", &color))
need_color_reset_at_eol = !!strcmp(color, "reset");
}
return 0;
}
static int grab_objectname(const char *name, const unsigned char *sha1,
- struct atom_value *v)
+ struct atom_value *v, struct used_atom *atom)
{
- if (!strcmp(name, "objectname")) {
- v->s = xstrdup(sha1_to_hex(sha1));
- return 1;
- }
- if (!strcmp(name, "objectname:short")) {
- v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
- return 1;
+ if (starts_with(name, "objectname")) {
+ if (atom->u.objectname == O_SHORT) {
+ v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ return 1;
+ } else if (atom->u.objectname == O_FULL) {
+ v->s = xstrdup(sha1_to_hex(sha1));
+ return 1;
+ } else
+ die("BUG: unknown %%(objectname) option");
}
return 0;
}
int i;
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ const char *name = used_atom[i].name;
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
v->s = xstrfmt("%lu", sz);
}
else if (deref)
- grab_objectname(name, obj->oid.hash, v);
+ grab_objectname(name, obj->oid.hash, v, &used_atom[i]);
}
}
struct tag *tag = (struct tag *) obj;
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ const char *name = used_atom[i].name;
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
struct commit *commit = (struct commit *) obj;
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ const char *name = used_atom[i].name;
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
const char *wholine = NULL;
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ const char *name = used_atom[i].name;
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
if (!wholine)
return;
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ const char *name = used_atom[i].name;
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ struct used_atom *atom = &used_atom[i];
+ const char *name = atom->name;
struct atom_value *v = &val[i];
- const char *valp = NULL;
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
if (strcmp(name, "subject") &&
strcmp(name, "body") &&
- strcmp(name, "contents") &&
- strcmp(name, "contents:subject") &&
- strcmp(name, "contents:body") &&
- strcmp(name, "contents:signature") &&
- !starts_with(name, "contents:lines="))
+ !starts_with(name, "contents"))
continue;
if (!subpos)
find_subpos(buf, sz,
&bodypos, &bodylen, &nonsiglen,
&sigpos, &siglen);
- if (!strcmp(name, "subject"))
+ if (atom->u.contents.option == C_SUB)
v->s = copy_subject(subpos, sublen);
- else if (!strcmp(name, "contents:subject"))
- v->s = copy_subject(subpos, sublen);
- else if (!strcmp(name, "body"))
+ else if (atom->u.contents.option == C_BODY_DEP)
v->s = xmemdupz(bodypos, bodylen);
- else if (!strcmp(name, "contents:body"))
+ else if (atom->u.contents.option == C_BODY)
v->s = xmemdupz(bodypos, nonsiglen);
- else if (!strcmp(name, "contents:signature"))
+ else if (atom->u.contents.option == C_SIG)
v->s = xmemdupz(sigpos, siglen);
- else if (!strcmp(name, "contents"))
- v->s = xstrdup(subpos);
- else if (skip_prefix(name, "contents:lines=", &valp)) {
+ else if (atom->u.contents.option == C_LINES) {
struct strbuf s = STRBUF_INIT;
const char *contents_end = bodylen + bodypos - siglen;
- if (strtoul_ui(valp, 10, &v->u.contents.lines))
- die(_("positive value expected contents:lines=%s"), valp);
/* Size is the length of the message after removing the signature */
- append_lines(&s, subpos, contents_end - subpos, v->u.contents.lines);
+ append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
v->s = strbuf_detach(&s, NULL);
- }
+ } else if (atom->u.contents.option == C_BARE)
+ v->s = xstrdup(subpos);
}
}
return start;
}
+static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
+ struct branch *branch, const char **s)
+{
+ int num_ours, num_theirs;
+ if (atom->u.remote_ref == RR_SHORTEN)
+ *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+ else if (atom->u.remote_ref == RR_TRACK) {
+ if (stat_tracking_info(branch, &num_ours,
+ &num_theirs, NULL))
+ return;
+
+ if (!num_ours && !num_theirs)
+ *s = "";
+ else if (!num_ours)
+ *s = xstrfmt("[behind %d]", num_theirs);
+ else if (!num_theirs)
+ *s = xstrfmt("[ahead %d]", num_ours);
+ else
+ *s = xstrfmt("[ahead %d, behind %d]",
+ num_ours, num_theirs);
+ } else if (atom->u.remote_ref == RR_TRACKSHORT) {
+ if (stat_tracking_info(branch, &num_ours,
+ &num_theirs, NULL))
+ return;
+
+ if (!num_ours && !num_theirs)
+ *s = "=";
+ else if (!num_ours)
+ *s = "<";
+ else if (!num_theirs)
+ *s = ">";
+ else
+ *s = "<>";
+ } else /* RR_NORMAL */
+ *s = refname;
+}
+
/*
* Parse the object referred by ref, and grab needed value.
*/
/* Fill in specials first */
for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
+ struct used_atom *atom = &used_atom[i];
+ const char *name = used_atom[i].name;
struct atom_value *v = &ref->value[i];
int deref = 0;
const char *refname;
const char *formatp;
- const char *valp;
struct branch *branch = NULL;
v->handler = append_atom;
branch = branch_get(branch_name);
refname = branch_get_upstream(branch, NULL);
- if (!refname)
- continue;
+ if (refname)
+ fill_remote_ref_details(atom, refname, branch, &v->s);
+ continue;
} else if (starts_with(name, "push")) {
const char *branch_name;
if (!skip_prefix(ref->refname, "refs/heads/",
refname = branch_get_push(branch, NULL);
if (!refname)
continue;
- } else if (match_atom_name(name, "color", &valp)) {
- char color[COLOR_MAXLEN] = "";
-
- if (!valp)
- die(_("expected format: %%(color:<color>)"));
- if (color_parse(valp, color) < 0)
- die(_("unable to parse format"));
- v->s = xstrdup(color);
+ fill_remote_ref_details(atom, refname, branch, &v->s);
+ continue;
+ } else if (starts_with(name, "color:")) {
+ v->s = atom->u.color;
continue;
} else if (!strcmp(name, "flag")) {
char buf[256], *cp = buf;
v->s = xstrdup(buf + 1);
}
continue;
- } else if (!deref && grab_objectname(name, ref->objectname, v)) {
+ } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) {
continue;
} else if (!strcmp(name, "HEAD")) {
const char *head;
else
v->s = " ";
continue;
- } else if (match_atom_name(name, "align", &valp)) {
- struct align *align = &v->u.align;
- struct strbuf **s, **to_free;
- int width = -1;
-
- if (!valp)
- die(_("expected format: %%(align:<width>,<position>)"));
-
- /*
- * TODO: Implement a function similar to strbuf_split_str()
- * which would omit the separator from the end of each value.
- */
- s = to_free = strbuf_split_str(valp, ',', 0);
-
- align->position = ALIGN_LEFT;
-
- while (*s) {
- /* Strip trailing comma */
- if (s[1])
- strbuf_setlen(s[0], s[0]->len - 1);
- if (!strtoul_ui(s[0]->buf, 10, (unsigned int *)&width))
- ;
- else if (!strcmp(s[0]->buf, "left"))
- align->position = ALIGN_LEFT;
- else if (!strcmp(s[0]->buf, "right"))
- align->position = ALIGN_RIGHT;
- else if (!strcmp(s[0]->buf, "middle"))
- align->position = ALIGN_MIDDLE;
- else
- die(_("improper format entered align:%s"), s[0]->buf);
- s++;
- }
-
- if (width < 0)
- die(_("positive width expected with the %%(align) atom"));
- align->width = width;
- strbuf_list_free(to_free);
+ } else if (starts_with(name, "align")) {
+ v->u.align = atom->u.align;
v->handler = align_atom_handler;
continue;
} else if (!strcmp(name, "end")) {
formatp = strchr(name, ':');
if (formatp) {
- int num_ours, num_theirs;
const char *arg;
formatp++;
warn_ambiguous_refs);
else if (skip_prefix(formatp, "strip=", &arg))
refname = strip_ref_components(refname, arg);
- else if (!strcmp(formatp, "track") &&
- (starts_with(name, "upstream") ||
- starts_with(name, "push"))) {
-
- if (stat_tracking_info(branch, &num_ours,
- &num_theirs, NULL))
- continue;
-
- if (!num_ours && !num_theirs)
- v->s = "";
- else if (!num_ours)
- v->s = xstrfmt("[behind %d]", num_theirs);
- else if (!num_theirs)
- v->s = xstrfmt("[ahead %d]", num_ours);
- else
- v->s = xstrfmt("[ahead %d, behind %d]",
- num_ours, num_theirs);
- continue;
- } else if (!strcmp(formatp, "trackshort") &&
- (starts_with(name, "upstream") ||
- starts_with(name, "push"))) {
- assert(branch);
-
- if (stat_tracking_info(branch, &num_ours,
- &num_theirs, NULL))
- continue;
-
- if (!num_ours && !num_theirs)
- v->s = "=";
- else if (!num_ours)
- v->s = "<";
- else if (!num_theirs)
- v->s = ">";
- else
- v->s = "<>";
- continue;
- } else
+ else
die("unknown %.*s format %s",
(int)(formatp - name), name, formatp);
}
const unsigned char *objectname,
int flag)
{
- size_t len = strlen(refname);
- struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);
- memcpy(ref->refname, refname, len);
- ref->refname[len] = '\0';
+ struct ref_array_item *ref;
+ FLEX_ALLOC_STR(ref, refname, refname);
hashcpy(ref->objectname, objectname);
ref->flag = flag;
{
struct atom_value *va, *vb;
int cmp;
- cmp_type cmp_type = used_atom_type[s->atom];
+ cmp_type cmp_type = used_atom[s->atom].type;
get_ref_atom_value(a, s->atom, &va);
get_ref_atom_value(b, s->atom, &vb);
const unsigned char *sha1, int flag,
int check_name)
{
- int len;
struct ref_entry *ref;
if (check_name &&
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die("Reference has invalid format: '%s'", refname);
- len = strlen(refname) + 1;
- ref = xmalloc(sizeof(struct ref_entry) + len);
+ FLEX_ALLOC_STR(ref, name, refname);
hashcpy(ref->u.value.oid.hash, sha1);
oidclr(&ref->u.value.peeled);
- memcpy(ref->name, refname, len);
ref->flag = flag;
return ref;
}
int incomplete)
{
struct ref_entry *direntry;
- direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
- memcpy(direntry->name, dirname, len);
- direntry->name[len] = '\0';
+ FLEX_ALLOC_MEM(direntry, name, dirname, len);
direntry->u.subdir.ref_cache = ref_cache;
direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
return direntry;
*/
static struct ref_cache *create_ref_cache(const char *submodule)
{
- int len;
struct ref_cache *refs;
if (!submodule)
submodule = "";
- len = strlen(submodule) + 1;
- refs = xcalloc(1, sizeof(struct ref_cache) + len);
- memcpy(refs->name, submodule, len);
+ FLEX_ALLOC_STR(refs, name, submodule);
refs->next = submodule_ref_caches;
submodule_ref_caches = refs;
return refs;
if (read_ref_full(lock->ref_name,
mustexist ? RESOLVE_REF_READING : 0,
lock->old_oid.hash, NULL)) {
- int save_errno = errno;
- strbuf_addf(err, "can't verify ref %s", lock->ref_name);
- errno = save_errno;
- return -1;
+ if (old_sha1) {
+ int save_errno = errno;
+ strbuf_addf(err, "can't verify ref %s", lock->ref_name);
+ errno = save_errno;
+ return -1;
+ } else {
+ hashclr(lock->old_oid.hash);
+ return 0;
+ }
}
- if (hashcmp(lock->old_oid.hash, old_sha1)) {
+ if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
strbuf_addf(err, "ref %s is at %s but expected %s",
lock->ref_name,
sha1_to_hex(lock->old_oid.hash),
const char *orig_refname = refname;
struct ref_lock *lock;
int last_errno = 0;
- int type, lflags;
+ int type;
+ int lflags = 0;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
int resolve_flags = 0;
int attempts_remaining = 3;
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
- if (flags & REF_DELETING) {
+ if (flags & REF_DELETING)
resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
- if (flags & REF_NODEREF)
- resolve_flags |= RESOLVE_REF_NO_RECURSE;
+ if (flags & REF_NODEREF) {
+ resolve_flags |= RESOLVE_REF_NO_RECURSE;
+ lflags |= LOCK_NO_DEREF;
}
refname = resolve_ref_unsafe(refname, resolve_flags,
goto error_return;
}
+
+ if (flags & REF_NODEREF)
+ refname = orig_refname;
+
/*
* If the ref did not exist and we are creating it, make sure
* there is no existing packed ref whose name begins with our
lock->lk = xcalloc(1, sizeof(struct lock_file));
- lflags = 0;
- if (flags & REF_NODEREF) {
- refname = orig_refname;
- lflags |= LOCK_NO_DEREF;
- }
lock->ref_name = xstrdup(refname);
lock->orig_ref_name = xstrdup(orig_refname);
strbuf_git_path(&ref_file, "%s", refname);
goto error_return;
}
}
- if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
+ if (verify_lock(lock, old_sha1, mustexist, err)) {
last_errno = errno;
goto error_return;
}
/* Schedule the loose reference for pruning if requested. */
if ((cb->flags & PACK_REFS_PRUNE)) {
- int namelen = strlen(entry->name) + 1;
- struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+ struct ref_to_prune *n;
+ FLEX_ALLOC_STR(n, name, entry->name);
hashcpy(n->sha1, entry->u.value.oid.hash);
- memcpy(n->name, entry->name, namelen); /* includes NUL */
n->next = cb->ref_to_prune;
cb->ref_to_prune = n;
}
return 0;
}
-int create_symref(const char *ref_target, const char *refs_heads_master,
- const char *logmsg)
+static int create_ref_symlink(struct ref_lock *lock, const char *target)
{
- char *lockpath = NULL;
- char ref[1000];
- int fd, len, written;
- char *git_HEAD = git_pathdup("%s", ref_target);
- unsigned char old_sha1[20], new_sha1[20];
- struct strbuf err = STRBUF_INIT;
-
- if (logmsg && read_ref(ref_target, old_sha1))
- hashclr(old_sha1);
-
- if (safe_create_leading_directories(git_HEAD) < 0)
- return error("unable to create directory for %s", git_HEAD);
-
+ int ret = -1;
#ifndef NO_SYMLINK_HEAD
- if (prefer_symlink_refs) {
- unlink(git_HEAD);
- if (!symlink(refs_heads_master, git_HEAD))
- goto done;
+ char *ref_path = get_locked_file_path(lock->lk);
+ unlink(ref_path);
+ ret = symlink(target, ref_path);
+ free(ref_path);
+
+ if (ret)
fprintf(stderr, "no symlink - falling back to symbolic ref\n");
- }
#endif
+ return ret;
+}
- len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
- if (sizeof(ref) <= len) {
- error("refname too long: %s", refs_heads_master);
- goto error_free_return;
- }
- lockpath = mkpathdup("%s.lock", git_HEAD);
- fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd < 0) {
- error("Unable to open %s for writing", lockpath);
- goto error_free_return;
- }
- written = write_in_full(fd, ref, len);
- if (close(fd) != 0 || written != len) {
- error("Unable to write to %s", lockpath);
- goto error_unlink_return;
- }
- if (rename(lockpath, git_HEAD) < 0) {
- error("Unable to create %s", git_HEAD);
- goto error_unlink_return;
- }
- if (adjust_shared_perm(git_HEAD)) {
- error("Unable to fix permissions on %s", lockpath);
- error_unlink_return:
- unlink_or_warn(lockpath);
- error_free_return:
- free(lockpath);
- free(git_HEAD);
- return -1;
+static void update_symref_reflog(struct ref_lock *lock, const char *refname,
+ const char *target, const char *logmsg)
+{
+ struct strbuf err = STRBUF_INIT;
+ unsigned char new_sha1[20];
+ if (logmsg && !read_ref(target, new_sha1) &&
+ log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {
+ error("%s", err.buf);
+ strbuf_release(&err);
}
- free(lockpath);
+}
-#ifndef NO_SYMLINK_HEAD
- done:
-#endif
- if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
- log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
+static int create_symref_locked(struct ref_lock *lock, const char *refname,
+ const char *target, const char *logmsg)
+{
+ if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
+ update_symref_reflog(lock, refname, target, logmsg);
+ return 0;
+ }
+
+ if (!fdopen_lock_file(lock->lk, "w"))
+ return error("unable to fdopen %s: %s",
+ lock->lk->tempfile.filename.buf, strerror(errno));
+
+ update_symref_reflog(lock, refname, target, logmsg);
+
+ /* no error check; commit_ref will check ferror */
+ fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
+ if (commit_ref(lock) < 0)
+ return error("unable to write symref for %s: %s", refname,
+ strerror(errno));
+ return 0;
+}
+
+int create_symref(const char *refname, const char *target, const char *logmsg)
+{
+ struct strbuf err = STRBUF_INIT;
+ struct ref_lock *lock;
+ int ret;
+
+ lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL,
+ &err);
+ if (!lock) {
error("%s", err.buf);
strbuf_release(&err);
+ return -1;
}
- free(git_HEAD);
- return 0;
+ ret = create_symref_locked(lock, refname, target, logmsg);
+ unlock_ref(lock);
+ return ret;
}
int reflog_exists(const char *refname)
else
return -1;
return 0;
+
+#if LIBCURL_VERSION_NUM >= 0x070a08
+ } else if (!strcmp(name, "family")) {
+ if (!strcmp(value, "ipv4"))
+ git_curl_ipresolve = CURL_IPRESOLVE_V4;
+ else if (!strcmp(value, "ipv6"))
+ git_curl_ipresolve = CURL_IPRESOLVE_V6;
+ else if (!strcmp(value, "all"))
+ git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
+ else
+ return -1;
+ return 0;
+#endif /* LIBCURL_VERSION_NUM >= 0x070a08 */
} else {
return 1 /* unsupported */;
}
err = run_one_slot(slot, results);
if (err != HTTP_OK && err != HTTP_REAUTH) {
- error("RPC failed; result=%d, HTTP code = %ld",
- results->curl_result, results->http_code);
+ struct strbuf msg = STRBUF_INIT;
+ if (results->http_code && results->http_code != 200)
+ strbuf_addf(&msg, "HTTP %ld", results->http_code);
+ if (results->curl_result != CURLE_OK) {
+ if (msg.len)
+ strbuf_addch(&msg, ' ');
+ strbuf_addf(&msg, "curl %d", results->curl_result);
+ if (curl_errorstr[0]) {
+ strbuf_addch(&msg, ' ');
+ strbuf_addstr(&msg, curl_errorstr);
+ }
+ }
+ error("RPC failed; %s", msg.buf);
+ strbuf_release(&msg);
}
return err;
static int fetch_dumb(int nr_heads, struct ref **to_fetch)
{
struct walker *walker;
- char **targets = xmalloc(nr_heads * sizeof(char*));
+ char **targets;
int ret, i;
+ ALLOC_ARRAY(targets, nr_heads);
if (options.depth)
die("dumb http transport does not support --depth");
for (i = 0; i < nr_heads; i++)
die("http transport does not support %s", buf->buf);
strbuf_reset(buf);
- if (strbuf_getline(buf, stdin, '\n') == EOF)
+ if (strbuf_getline_lf(buf, stdin) == EOF)
return;
if (!*buf->buf)
break;
static int push_dav(int nr_spec, char **specs)
{
- const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
- int argc = 0, i;
+ struct child_process child = CHILD_PROCESS_INIT;
+ size_t i;
- argv[argc++] = "http-push";
- argv[argc++] = "--helper-status";
+ child.git_cmd = 1;
+ argv_array_push(&child.args, "http-push");
+ argv_array_push(&child.args, "--helper-status");
if (options.dry_run)
- argv[argc++] = "--dry-run";
+ argv_array_push(&child.args, "--dry-run");
if (options.verbosity > 1)
- argv[argc++] = "--verbose";
- argv[argc++] = url.buf;
+ argv_array_push(&child.args, "--verbose");
+ argv_array_push(&child.args, url.buf);
for (i = 0; i < nr_spec; i++)
- argv[argc++] = specs[i];
- argv[argc++] = NULL;
+ argv_array_push(&child.args, specs[i]);
- if (run_command_v_opt(argv, RUN_GIT_CMD))
- die("git-%s failed", argv[0]);
- free(argv);
+ if (run_command(&child))
+ die("git-http-push failed");
return 0;
}
die("http transport does not support %s", buf->buf);
strbuf_reset(buf);
- if (strbuf_getline(buf, stdin, '\n') == EOF)
+ if (strbuf_getline_lf(buf, stdin) == EOF)
goto free_specs;
if (!*buf->buf)
break;
do {
const char *arg;
- if (strbuf_getline(&buf, stdin, '\n') == EOF) {
+ if (strbuf_getline_lf(&buf, stdin) == EOF) {
if (ferror(stdin))
error("remote-curl: error reading command stream from git");
return 1;
if (!f)
return;
remote->origin = REMOTE_REMOTES;
- while (strbuf_getline(&buf, f, '\n') != EOF) {
+ while (strbuf_getline(&buf, f) != EOF) {
const char *v;
strbuf_rtrim(&buf);
if (!f)
return;
- strbuf_getline(&buf, f, '\n');
+ strbuf_getline_lf(&buf, f);
fclose(f);
strbuf_trim(&buf);
if (!buf.len) {
static int handle_config(const char *key, const char *value, void *cb)
{
const char *name;
+ int namelen;
const char *subkey;
struct remote *remote;
struct branch *branch;
- if (starts_with(key, "branch.")) {
- name = key + 7;
- subkey = strrchr(name, '.');
- if (!subkey)
+ if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
+ if (!name)
return 0;
- branch = make_branch(name, subkey - name);
- if (!strcmp(subkey, ".remote")) {
+ branch = make_branch(name, namelen);
+ if (!strcmp(subkey, "remote")) {
return git_config_string(&branch->remote_name, key, value);
- } else if (!strcmp(subkey, ".pushremote")) {
+ } else if (!strcmp(subkey, "pushremote")) {
return git_config_string(&branch->pushremote_name, key, value);
- } else if (!strcmp(subkey, ".merge")) {
+ } else if (!strcmp(subkey, "merge")) {
if (!value)
return config_error_nonbool(key);
add_merge(branch, xstrdup(value));
}
return 0;
}
- if (starts_with(key, "url.")) {
+ if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) {
struct rewrite *rewrite;
- name = key + 4;
- subkey = strrchr(name, '.');
- if (!subkey)
+ if (!name)
return 0;
- if (!strcmp(subkey, ".insteadof")) {
- rewrite = make_rewrite(&rewrites, name, subkey - name);
+ if (!strcmp(subkey, "insteadof")) {
+ rewrite = make_rewrite(&rewrites, name, namelen);
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
- } else if (!strcmp(subkey, ".pushinsteadof")) {
- rewrite = make_rewrite(&rewrites_push, name, subkey - name);
+ } else if (!strcmp(subkey, "pushinsteadof")) {
+ rewrite = make_rewrite(&rewrites_push, name, namelen);
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
}
}
- if (!starts_with(key, "remote."))
+ if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0)
return 0;
- name = key + 7;
/* Handle remote.* variables */
- if (!strcmp(name, "pushdefault"))
+ if (!name && !strcmp(subkey, "pushdefault"))
return git_config_string(&pushremote_name, key, value);
+ if (!name)
+ return 0;
/* Handle remote.<name>.* variables */
if (*name == '/') {
warning("Config remote shorthand cannot begin with '/': %s",
name);
return 0;
}
- subkey = strrchr(name, '.');
- if (!subkey)
- return 0;
- remote = make_remote(name, subkey - name);
+ remote = make_remote(name, namelen);
remote->origin = REMOTE_CONFIG;
- if (!strcmp(subkey, ".mirror"))
+ if (!strcmp(subkey, "mirror"))
remote->mirror = git_config_bool(key, value);
- else if (!strcmp(subkey, ".skipdefaultupdate"))
+ else if (!strcmp(subkey, "skipdefaultupdate"))
remote->skip_default_update = git_config_bool(key, value);
- else if (!strcmp(subkey, ".skipfetchall"))
+ else if (!strcmp(subkey, "skipfetchall"))
remote->skip_default_update = git_config_bool(key, value);
- else if (!strcmp(subkey, ".prune"))
+ else if (!strcmp(subkey, "prune"))
remote->prune = git_config_bool(key, value);
- else if (!strcmp(subkey, ".url")) {
+ else if (!strcmp(subkey, "url")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_url(remote, v);
- } else if (!strcmp(subkey, ".pushurl")) {
+ } else if (!strcmp(subkey, "pushurl")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_pushurl(remote, v);
- } else if (!strcmp(subkey, ".push")) {
+ } else if (!strcmp(subkey, "push")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_push_refspec(remote, v);
- } else if (!strcmp(subkey, ".fetch")) {
+ } else if (!strcmp(subkey, "fetch")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_fetch_refspec(remote, v);
- } else if (!strcmp(subkey, ".receivepack")) {
+ } else if (!strcmp(subkey, "receivepack")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
remote->receivepack = v;
else
error("more than one receivepack given, using the first");
- } else if (!strcmp(subkey, ".uploadpack")) {
+ } else if (!strcmp(subkey, "uploadpack")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
remote->uploadpack = v;
else
error("more than one uploadpack given, using the first");
- } else if (!strcmp(subkey, ".tagopt")) {
+ } else if (!strcmp(subkey, "tagopt")) {
if (!strcmp(value, "--no-tags"))
remote->fetch_tags = -1;
else if (!strcmp(value, "--tags"))
remote->fetch_tags = 2;
- } else if (!strcmp(subkey, ".proxy")) {
+ } else if (!strcmp(subkey, "proxy")) {
return git_config_string((const char **)&remote->http_proxy,
key, value);
- } else if (!strcmp(subkey, ".vcs")) {
+ } else if (!strcmp(subkey, "proxyauthmethod")) {
+ return git_config_string((const char **)&remote->http_proxy_authmethod,
+ key, value);
+ } else if (!strcmp(subkey, "vcs")) {
return git_config_string(&remote->foreign_vcs, key, value);
}
return 0;
return remote_get_1(name, pushremote_for_branch);
}
-int remote_is_configured(const char *name)
+int remote_is_configured(struct remote *remote)
{
- struct remotes_hash_key lookup;
- struct hashmap_entry lookup_entry;
- read_config();
-
- init_remotes_hash();
- lookup.str = name;
- lookup.len = strlen(name);
- hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
-
- return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
+ return remote && remote->origin;
}
int for_each_remote(each_remote_fn fn, void *priv)
const char *name)
{
size_t len = strlen(name);
- struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
+ struct ref *ref = xcalloc(1, st_add4(sizeof(*ref), prefixlen, len, 1));
memcpy(ref->name, prefix, prefixlen);
memcpy(ref->name + prefixlen, name, len);
return ref;
size_t len;
if (!ref)
return NULL;
- len = strlen(ref->name);
- cpy = xmalloc(sizeof(struct ref) + len + 1);
- memcpy(cpy, ref, sizeof(struct ref) + len + 1);
+ len = st_add3(sizeof(struct ref), strlen(ref->name), 1);
+ cpy = xmalloc(len);
+ memcpy(cpy, ref, len);
cpy->next = NULL;
cpy->symref = xstrdup_or_null(ref->symref);
cpy->remote_status = xstrdup_or_null(ref->remote_status);
}
/*
- * Bypass the usual "must fast-forward" check but
- * replace it with a weaker "the old value must be
- * this value we observed". If the remote ref has
- * moved and is now different from what we expect,
- * reject any push.
+ * If the remote ref has moved and is now different
+ * from what we expect, reject any push.
*
* It also is an error if the user told us to check
* with the remote-tracking branch to find the value
if (ref->expect_old_no_trackback ||
oidcmp(&ref->old_oid, &ref->old_oid_expect))
reject_reason = REF_STATUS_REJECT_STALE;
+ else
+ /* If the ref isn't stale then force the update. */
+ force_ref_update = 1;
}
/*
- * The usual "must fast-forward" rules.
+ * If the update isn't already rejected then check
+ * the usual "must fast-forward" rules.
*
* Decide whether an individual refspec A:B can be
* pushed. The push will succeed if any of the
* passing the --force argument
*/
- else if (!ref->deletion && !is_null_oid(&ref->old_oid)) {
+ if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
if (starts_with(ref->name, "refs/tags/"))
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
else if (!has_object_file(&ref->old_oid))
{
struct ref ***local_tail = cb_data;
struct ref *ref;
- int len;
/* we already know it starts with refs/ to get here */
if (check_refname_format(refname + 5, 0))
return 0;
- len = strlen(refname) + 1;
- ref = xcalloc(1, sizeof(*ref) + len);
+ ref = alloc_ref(refname);
oidcpy(&ref->new_oid, oid);
- memcpy(ref->name, refname, len);
**local_tail = ref;
*local_tail = &ref->next;
return 0;
static const char *term_bad;
static const char *term_good;
-char *path_name(const struct name_path *path, const char *name)
+void show_object_with_name(FILE *out, struct object *obj, const char *name)
{
- const struct name_path *p;
- char *n, *m;
- int nlen = strlen(name);
- int len = nlen + 1;
-
- for (p = path; p; p = p->up) {
- if (p->elem_len)
- len += p->elem_len + 1;
- }
- n = xmalloc(len);
- m = n + len - (nlen + 1);
- memcpy(m, name, nlen + 1);
- for (p = path; p; p = p->up) {
- if (p->elem_len) {
- m -= p->elem_len + 1;
- memcpy(m, p->elem, p->elem_len);
- m[p->elem_len] = '/';
- }
- }
- return n;
-}
-
-static int show_path_component_truncated(FILE *out, const char *name, int len)
-{
- int cnt;
- for (cnt = 0; cnt < len; cnt++) {
- int ch = name[cnt];
- if (!ch || ch == '\n')
- return -1;
- fputc(ch, out);
- }
- return len;
-}
-
-static int show_path_truncated(FILE *out, const struct name_path *path)
-{
- int emitted, ours;
-
- if (!path)
- return 0;
- emitted = show_path_truncated(out, path->up);
- if (emitted < 0)
- return emitted;
- if (emitted)
- fputc('/', out);
- ours = show_path_component_truncated(out, path->elem, path->elem_len);
- if (ours < 0)
- return ours;
- return ours || emitted;
-}
-
-void show_object_with_name(FILE *out, struct object *obj,
- const struct name_path *path, const char *component)
-{
- struct name_path leaf;
- leaf.up = (struct name_path *)path;
- leaf.elem = component;
- leaf.elem_len = strlen(component);
+ const char *p;
fprintf(out, "%s ", oid_to_hex(&obj->oid));
- show_path_truncated(out, &leaf);
+ for (p = name; *p && *p != '\n'; p++)
+ fputc(*p, out);
fputc('\n', out);
}
static struct treesame_state *initialise_treesame(struct rev_info *revs, struct commit *commit)
{
unsigned n = commit_list_count(commit->parents);
- struct treesame_state *st = xcalloc(1, sizeof(*st) + n);
+ struct treesame_state *st = xcalloc(1, st_add(sizeof(*st), n));
st->nparents = n;
add_decoration(&revs->treesame, &commit->object, st);
return st;
static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
struct cmdline_pathspec *prune)
{
- while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
- int len = sb->len;
- if (len && sb->buf[len - 1] == '\n')
- sb->buf[--len] = '\0';
+ while (strbuf_getline(sb, stdin) != EOF) {
ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
prune->path[prune->nr++] = xstrdup(sb->buf);
}
warn_on_object_refname_ambiguity = 0;
strbuf_init(&sb, 1000);
- while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+ while (strbuf_getline(&sb, stdin) != EOF) {
int len = sb.len;
- if (len && sb.buf[len - 1] == '\n')
- sb.buf[--len] = '\0';
if (!len)
break;
if (sb.buf[0] == '-') {
#include "exec_cmd.h"
#include "sigchain.h"
#include "argv-array.h"
+#include "thread-utils.h"
+#include "strbuf.h"
void child_process_init(struct child_process *child)
{
return -1;
}
- static const char **prepare_shell_cmd(const char **argv)
+ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
{
- int argc, nargc = 0;
- const char **nargv;
-
- for (argc = 0; argv[argc]; argc++)
- ; /* just counting */
- /* +1 for NULL, +3 for "sh -c" plus extra $0 */
- nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
-
- if (argc < 1)
+ if (!argv[0])
die("BUG: shell command is empty");
if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
#ifndef GIT_WINDOWS_NATIVE
- nargv[nargc++] = SHELL_PATH;
+ argv_array_push(out, SHELL_PATH);
#else
- nargv[nargc++] = "sh";
+ argv_array_push(out, "sh");
#endif
- nargv[nargc++] = "-c";
-
- if (argc < 2)
- nargv[nargc++] = argv[0];
- else {
- struct strbuf arg0 = STRBUF_INIT;
- strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
- nargv[nargc++] = strbuf_detach(&arg0, NULL);
- }
- }
+ argv_array_push(out, "-c");
- for (argc = 0; argv[argc]; argc++)
- nargv[nargc++] = argv[argc];
- nargv[nargc] = NULL;
+ /*
+ * If we have no extra arguments, we do not even need to
+ * bother with the "$@" magic.
+ */
+ if (!argv[1])
+ argv_array_push(out, argv[0]);
+ else
+ argv_array_pushf(out, "%s \"$@\"", argv[0]);
+ }
- return nargv;
+ argv_array_pushv(out, argv);
+ return out->argv;
}
#ifndef GIT_WINDOWS_NATIVE
static int execv_shell_cmd(const char **argv)
{
- const char **nargv = prepare_shell_cmd(argv);
- trace_argv_printf(nargv, "trace: exec:");
- sane_execvp(nargv[0], (char **)nargv);
- free(nargv);
+ struct argv_array nargv = ARGV_ARRAY_INIT;
+ prepare_shell_cmd(&nargv, argv);
+ trace_argv_printf(nargv.argv, "trace: exec:");
+ sane_execvp(nargv.argv[0], (char **)nargv.argv);
+ argv_array_clear(&nargv);
return -1;
}
#endif
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
code = WTERMSIG(status);
- if (code != SIGINT && code != SIGQUIT)
+ if (code != SIGINT && code != SIGQUIT && code != SIGPIPE)
error("%s died of signal %d", argv0, code);
/*
* This return value is chosen so that code & 0xff
{
int fhin = 0, fhout = 1, fherr = 2;
const char **sargv = cmd->argv;
+ struct argv_array nargv = ARGV_ARRAY_INIT;
if (cmd->no_stdin)
fhin = open("/dev/null", O_RDWR);
fhout = dup(cmd->out);
if (cmd->git_cmd)
- cmd->argv = prepare_git_cmd(cmd->argv);
+ cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
else if (cmd->use_shell)
- cmd->argv = prepare_shell_cmd(cmd->argv);
+ cmd->argv = prepare_shell_cmd(&nargv, cmd->argv);
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env,
cmd->dir, fhin, fhout, fherr);
if (cmd->clean_on_exit && cmd->pid >= 0)
mark_child_for_cleanup(cmd->pid);
- if (cmd->git_cmd)
- free(cmd->argv);
-
+ argv_array_clear(&nargv);
cmd->argv = sargv;
if (fhin != 0)
close(fhin);
close(cmd->out);
return finish_command(cmd);
}
+
+enum child_state {
+ GIT_CP_FREE,
+ GIT_CP_WORKING,
+ GIT_CP_WAIT_CLEANUP,
+};
+
+struct parallel_processes {
+ void *data;
+
+ int max_processes;
+ int nr_processes;
+
+ get_next_task_fn get_next_task;
+ start_failure_fn start_failure;
+ task_finished_fn task_finished;
+
+ struct {
+ enum child_state state;
+ struct child_process process;
+ struct strbuf err;
+ void *data;
+ } *children;
+ /*
+ * The struct pollfd is logically part of *children,
+ * but the system call expects it as its own array.
+ */
+ struct pollfd *pfd;
+
+ unsigned shutdown : 1;
+
+ int output_owner;
+ struct strbuf buffered_output; /* of finished children */
+};
+
+static int default_start_failure(struct child_process *cp,
+ struct strbuf *err,
+ void *pp_cb,
+ void *pp_task_cb)
+{
+ int i;
+
+ strbuf_addstr(err, "Starting a child failed:");
+ for (i = 0; cp->argv[i]; i++)
+ strbuf_addf(err, " %s", cp->argv[i]);
+
+ return 0;
+}
+
+static int default_task_finished(int result,
+ struct child_process *cp,
+ struct strbuf *err,
+ void *pp_cb,
+ void *pp_task_cb)
+{
+ int i;
+
+ if (!result)
+ return 0;
+
+ strbuf_addf(err, "A child failed with return code %d:", result);
+ for (i = 0; cp->argv[i]; i++)
+ strbuf_addf(err, " %s", cp->argv[i]);
+
+ return 0;
+}
+
+static void kill_children(struct parallel_processes *pp, int signo)
+{
+ int i, n = pp->max_processes;
+
+ for (i = 0; i < n; i++)
+ if (pp->children[i].state == GIT_CP_WORKING)
+ kill(pp->children[i].process.pid, signo);
+}
+
+static struct parallel_processes *pp_for_signal;
+
+static void handle_children_on_signal(int signo)
+{
+ kill_children(pp_for_signal, signo);
+ sigchain_pop(signo);
+ raise(signo);
+}
+
+static void pp_init(struct parallel_processes *pp,
+ int n,
+ get_next_task_fn get_next_task,
+ start_failure_fn start_failure,
+ task_finished_fn task_finished,
+ void *data)
+{
+ int i;
+
+ if (n < 1)
+ n = online_cpus();
+
+ pp->max_processes = n;
+
+ trace_printf("run_processes_parallel: preparing to run up to %d tasks", n);
+
+ pp->data = data;
+ if (!get_next_task)
+ die("BUG: you need to specify a get_next_task function");
+ pp->get_next_task = get_next_task;
+
+ pp->start_failure = start_failure ? start_failure : default_start_failure;
+ pp->task_finished = task_finished ? task_finished : default_task_finished;
+
+ pp->nr_processes = 0;
+ pp->output_owner = 0;
+ pp->shutdown = 0;
+ pp->children = xcalloc(n, sizeof(*pp->children));
+ pp->pfd = xcalloc(n, sizeof(*pp->pfd));
+ strbuf_init(&pp->buffered_output, 0);
+
+ for (i = 0; i < n; i++) {
+ strbuf_init(&pp->children[i].err, 0);
+ child_process_init(&pp->children[i].process);
+ pp->pfd[i].events = POLLIN | POLLHUP;
+ pp->pfd[i].fd = -1;
+ }
+
+ pp_for_signal = pp;
+ sigchain_push_common(handle_children_on_signal);
+}
+
+static void pp_cleanup(struct parallel_processes *pp)
+{
+ int i;
+
+ trace_printf("run_processes_parallel: done");
+ for (i = 0; i < pp->max_processes; i++) {
+ strbuf_release(&pp->children[i].err);
+ child_process_clear(&pp->children[i].process);
+ }
+
+ free(pp->children);
+ free(pp->pfd);
+
+ /*
+ * When get_next_task added messages to the buffer in its last
+ * iteration, the buffered output is non empty.
+ */
+ fputs(pp->buffered_output.buf, stderr);
+ strbuf_release(&pp->buffered_output);
+
+ sigchain_pop_common();
+}
+
+/* returns
+ * 0 if a new task was started.
+ * 1 if no new jobs was started (get_next_task ran out of work, non critical
+ * problem with starting a new command)
+ * <0 no new job was started, user wishes to shutdown early. Use negative code
+ * to signal the children.
+ */
+static int pp_start_one(struct parallel_processes *pp)
+{
+ int i, code;
+
+ for (i = 0; i < pp->max_processes; i++)
+ if (pp->children[i].state == GIT_CP_FREE)
+ break;
+ if (i == pp->max_processes)
+ die("BUG: bookkeeping is hard");
+
+ code = pp->get_next_task(&pp->children[i].process,
+ &pp->children[i].err,
+ pp->data,
+ &pp->children[i].data);
+ if (!code) {
+ strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
+ strbuf_reset(&pp->children[i].err);
+ return 1;
+ }
+ pp->children[i].process.err = -1;
+ pp->children[i].process.stdout_to_stderr = 1;
+ pp->children[i].process.no_stdin = 1;
+
+ if (start_command(&pp->children[i].process)) {
+ code = pp->start_failure(&pp->children[i].process,
+ &pp->children[i].err,
+ pp->data,
+ &pp->children[i].data);
+ strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
+ strbuf_reset(&pp->children[i].err);
+ if (code)
+ pp->shutdown = 1;
+ return code;
+ }
+
+ pp->nr_processes++;
+ pp->children[i].state = GIT_CP_WORKING;
+ pp->pfd[i].fd = pp->children[i].process.err;
+ return 0;
+}
+
+static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout)
+{
+ int i;
+
+ while ((i = poll(pp->pfd, pp->max_processes, output_timeout)) < 0) {
+ if (errno == EINTR)
+ continue;
+ pp_cleanup(pp);
+ die_errno("poll");
+ }
+
+ /* Buffer output from all pipes. */
+ for (i = 0; i < pp->max_processes; i++) {
+ if (pp->children[i].state == GIT_CP_WORKING &&
+ pp->pfd[i].revents & (POLLIN | POLLHUP)) {
+ int n = strbuf_read_once(&pp->children[i].err,
+ pp->children[i].process.err, 0);
+ if (n == 0) {
+ close(pp->children[i].process.err);
+ pp->children[i].state = GIT_CP_WAIT_CLEANUP;
+ } else if (n < 0)
+ if (errno != EAGAIN)
+ die_errno("read");
+ }
+ }
+}
+
+static void pp_output(struct parallel_processes *pp)
+{
+ int i = pp->output_owner;
+ if (pp->children[i].state == GIT_CP_WORKING &&
+ pp->children[i].err.len) {
+ fputs(pp->children[i].err.buf, stderr);
+ strbuf_reset(&pp->children[i].err);
+ }
+}
+
+static int pp_collect_finished(struct parallel_processes *pp)
+{
+ int i, code;
+ int n = pp->max_processes;
+ int result = 0;
+
+ while (pp->nr_processes > 0) {
+ for (i = 0; i < pp->max_processes; i++)
+ if (pp->children[i].state == GIT_CP_WAIT_CLEANUP)
+ break;
+ if (i == pp->max_processes)
+ break;
+
+ code = finish_command(&pp->children[i].process);
+
+ code = pp->task_finished(code, &pp->children[i].process,
+ &pp->children[i].err, pp->data,
+ &pp->children[i].data);
+
+ if (code)
+ result = code;
+ if (code < 0)
+ break;
+
+ pp->nr_processes--;
+ pp->children[i].state = GIT_CP_FREE;
+ pp->pfd[i].fd = -1;
+ child_process_init(&pp->children[i].process);
+
+ if (i != pp->output_owner) {
+ strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
+ strbuf_reset(&pp->children[i].err);
+ } else {
+ fputs(pp->children[i].err.buf, stderr);
+ strbuf_reset(&pp->children[i].err);
+
+ /* Output all other finished child processes */
+ fputs(pp->buffered_output.buf, stderr);
+ strbuf_reset(&pp->buffered_output);
+
+ /*
+ * Pick next process to output live.
+ * NEEDSWORK:
+ * For now we pick it randomly by doing a round
+ * robin. Later we may want to pick the one with
+ * the most output or the longest or shortest
+ * running process time.
+ */
+ for (i = 0; i < n; i++)
+ if (pp->children[(pp->output_owner + i) % n].state == GIT_CP_WORKING)
+ break;
+ pp->output_owner = (pp->output_owner + i) % n;
+ }
+ }
+ return result;
+}
+
+int run_processes_parallel(int n,
+ get_next_task_fn get_next_task,
+ start_failure_fn start_failure,
+ task_finished_fn task_finished,
+ void *pp_cb)
+{
+ int i, code;
+ int output_timeout = 100;
+ int spawn_cap = 4;
+ struct parallel_processes pp;
+
+ pp_init(&pp, n, get_next_task, start_failure, task_finished, pp_cb);
+ while (1) {
+ for (i = 0;
+ i < spawn_cap && !pp.shutdown &&
+ pp.nr_processes < pp.max_processes;
+ i++) {
+ code = pp_start_one(&pp);
+ if (!code)
+ continue;
+ if (code < 0) {
+ pp.shutdown = 1;
+ kill_children(&pp, -code);
+ }
+ break;
+ }
+ if (!pp.nr_processes)
+ break;
+ pp_buffer_stderr(&pp, output_timeout);
+ pp_output(&pp);
+ code = pp_collect_finished(&pp);
+ if (code) {
+ pp.shutdown = 1;
+ if (code < 0)
+ kill_children(&pp, -code);
+ }
+ }
+
+ pp_cleanup(&pp);
+ return 0;
+}
struct commit_message {
char *parent_label;
- const char *label;
- const char *subject;
+ char *label;
+ char *subject;
const char *message;
};
static int get_message(struct commit *commit, struct commit_message *out)
{
const char *abbrev, *subject;
- int abbrev_len, subject_len;
- char *q;
+ int subject_len;
- if (!git_commit_encoding)
- git_commit_encoding = "UTF-8";
-
- out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
+ out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding());
abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
- abbrev_len = strlen(abbrev);
subject_len = find_commit_subject(out->message, &subject);
- out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
- strlen("... ") + subject_len + 1);
- q = out->parent_label;
- q = mempcpy(q, "parent of ", strlen("parent of "));
- out->label = q;
- q = mempcpy(q, abbrev, abbrev_len);
- q = mempcpy(q, "... ", strlen("... "));
- out->subject = q;
- q = mempcpy(q, subject, subject_len);
- *q = '\0';
+ out->subject = xmemdupz(subject, subject_len);
+ out->label = xstrfmt("%s... %s", abbrev, out->subject);
+ out->parent_label = xstrfmt("parent of %s", out->label);
+
return 0;
}
static void free_message(struct commit *commit, struct commit_message *msg)
{
free(msg->parent_label);
+ free(msg->label);
+ free(msg->subject);
unuse_commit_buffer(commit, msg->message);
}
if (!f)
return error(_("cannot open %s: %s"), git_path_head_file(),
strerror(errno));
- if (strbuf_getline(&buf, f, '\n')) {
+ if (strbuf_getline_lf(&buf, f)) {
error(_("cannot read %s: %s"), git_path_head_file(),
ferror(f) ? strerror(errno) : _("unexpected end of file"));
fclose(f);
const char *orig = path;
char *sanitized;
if (is_absolute_path(orig)) {
- sanitized = xmalloc(strlen(path) + 1);
+ sanitized = xmallocz(strlen(path));
if (remaining_prefix)
*remaining_prefix = 0;
if (normalize_path_copy_len(sanitized, path, remaining_prefix)) {
if (arg[2] == '\0') /* ":/" is root dir, always exists */
return 1;
name = arg + 2;
- } else if (!no_wildcard(arg))
- return 1;
- else if (prefix)
+ } else if (prefix)
name = prefix_filename(prefix, strlen(prefix), arg);
else
name = arg;
{
if (*arg == '-')
die("bad flag '%s' used after filename", arg);
- if (check_filename(prefix, arg))
+ if (check_filename(prefix, arg) || !no_wildcard(arg))
return;
die_verify_filename(prefix, arg, diagnose_misspelt_rev);
}
return ret;
}
-static void update_linked_gitdir(const char *gitfile, const char *gitdir)
-{
- struct strbuf path = STRBUF_INIT;
- struct stat st;
-
- strbuf_addf(&path, "%s/gitdir", gitdir);
- if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
- 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.
error_code = READ_GITFILE_ERR_OPEN_FAILED;
goto cleanup_return;
}
- buf = xmalloc(st.st_size + 1);
+ buf = xmallocz(st.st_size);
len = read_in_full(fd, buf, st.st_size);
close(fd);
if (len != st.st_size) {
error_code = READ_GITFILE_ERR_READ_FAILED;
goto cleanup_return;
}
- buf[len] = '\0';
if (!starts_with(buf, "gitdir: ")) {
error_code = READ_GITFILE_ERR_INVALID_FORMAT;
goto cleanup_return;
error_code = READ_GITFILE_ERR_NOT_A_REPO;
goto cleanup_return;
}
- update_linked_gitdir(path, dir);
path = real_path(dir);
cleanup_return:
{
struct alternate_object_database *ent;
struct alternate_object_database *alt;
- int pfxlen, entlen;
+ size_t pfxlen, entlen;
struct strbuf pathbuf = STRBUF_INIT;
if (!is_absolute_path(entry) && relative_base) {
while (pfxlen && pathbuf.buf[pfxlen-1] == '/')
pfxlen -= 1;
- entlen = pfxlen + 43; /* '/' + 2 hex + '/' + 38 hex + NUL */
- ent = xmalloc(sizeof(*ent) + entlen);
+ entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */
+ ent = xmalloc(st_add(sizeof(*ent), entlen));
memcpy(ent->base, pathbuf.buf, pfxlen);
strbuf_release(&pathbuf);
struct strbuf line = STRBUF_INIT;
int found = 0;
- while (strbuf_getline(&line, in, '\n') != EOF) {
+ while (strbuf_getline(&line, in) != EOF) {
if (!strcmp(reference, line.buf)) {
found = 1;
break;
static struct packed_git *alloc_packed_git(int extra)
{
- struct packed_git *p = xmalloc(sizeof(*p) + extra);
+ struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));
memset(p, 0, sizeof(*p));
p->pack_fd = -1;
return p;
* ".pack" is long enough to hold any suffix we're adding (and
* the use xsnprintf double-checks that)
*/
- alloc = path_len + strlen(".pack") + 1;
+ alloc = st_add3(path_len, strlen(".pack"), 1);
p = alloc_packed_git(alloc);
memcpy(p->pack_name, path, path_len);
struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
{
const char *path = sha1_pack_name(sha1);
- int alloc = strlen(path) + 1;
+ size_t alloc = st_add(strlen(path), 1);
struct packed_git *p = alloc_packed_git(alloc);
memcpy(p->pack_name, path, alloc); /* includes NUL */
{
unsigned i;
for (i = 0; i < p->num_bad_objects; i++)
- if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+ if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
return;
- p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1));
- hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1);
+ p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
+ st_mult(GIT_SHA1_RAWSZ,
+ st_add(p->num_bad_objects, 1)));
+ hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
p->num_bad_objects++;
}
/* Push the object we're going to leave behind */
if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
poi_stack_alloc = alloc_nr(poi_stack_nr);
- poi_stack = xmalloc(sizeof(off_t)*poi_stack_alloc);
+ ALLOC_ARRAY(poi_stack, poi_stack_alloc);
memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
} else {
ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
if (delta_stack_nr >= delta_stack_alloc
&& delta_stack == small_delta_stack) {
delta_stack_alloc = alloc_nr(delta_stack_nr);
- delta_stack = xmalloc(sizeof(*delta_stack)*delta_stack_alloc);
+ ALLOC_ARRAY(delta_stack, delta_stack_alloc);
memcpy(delta_stack, small_delta_stack,
sizeof(*delta_stack)*delta_stack_nr);
} else {
* object databases including our own.
*/
const char *objdir = get_object_directory();
- int objdir_len = strlen(objdir);
- int entlen = objdir_len + 43;
- fakeent = xmalloc(sizeof(*fakeent) + entlen);
+ size_t objdir_len = strlen(objdir);
+ fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43));
memcpy(fakeent->base, objdir, objdir_len);
fakeent->name = fakeent->base + objdir_len + 1;
fakeent->name[-1] = '/';
* through history and returning the first commit whose message starts
* the given regular expression.
*
- * For future extension, ':/!' is reserved. If you want to match a message
- * beginning with a '!', you have to repeat the exclamation mark.
+ * For negative-matching, prefix the pattern-part with '!-', like: ':/!-WIP'.
+ *
+ * For a literal '!' character at the beginning of a pattern, you have to repeat
+ * that, like: ':/!!foo'
+ *
+ * For future extension, all other sequences beginning with ':/!' are reserved.
*/
/* Remember to update object flag allocation in object.h */
{
struct commit_list *backup = NULL, *l;
int found = 0;
+ int negative = 0;
regex_t regex;
if (prefix[0] == '!') {
- if (prefix[1] != '!')
- die ("Invalid search pattern: %s", prefix);
prefix++;
+
+ if (prefix[0] == '-') {
+ prefix++;
+ negative = 1;
+ } else if (prefix[0] != '!') {
+ return -1;
+ }
}
if (regcomp(®ex, prefix, REG_EXTENDED))
- die("Invalid search pattern: %s", prefix);
+ return -1;
for (l = list; l; l = l->next) {
l->item->object.flags |= ONELINE_SEEN;
continue;
buf = get_commit_buffer(commit, NULL);
p = strstr(buf, "\n\n");
- matches = p && !regexec(®ex, p + 2, 0, NULL, 0);
+ matches = negative ^ (p && !regexec(®ex, p + 2, 0, NULL, 0));
unuse_commit_buffer(commit, buf);
if (matches) {
return sb->len - oldlen;
}
+ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint)
+{
+ ssize_t cnt;
+
+ strbuf_grow(sb, hint ? hint : 8192);
+ cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
+ if (cnt > 0)
+ strbuf_setlen(sb, sb->len + cnt);
+ return cnt;
+}
+
#define STRBUF_MAXLINK (2*PATH_MAX)
int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
}
#endif
-int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term)
{
if (strbuf_getwholeline(sb, fp, term))
return EOF;
- if (sb->buf[sb->len-1] == term)
- strbuf_setlen(sb, sb->len-1);
+ if (sb->buf[sb->len - 1] == term)
+ strbuf_setlen(sb, sb->len - 1);
return 0;
}
+int strbuf_getline(struct strbuf *sb, FILE *fp)
+{
+ if (strbuf_getwholeline(sb, fp, '\n'))
+ return EOF;
+ if (sb->buf[sb->len - 1] == '\n') {
+ strbuf_setlen(sb, sb->len - 1);
+ if (sb->len && sb->buf[sb->len - 1] == '\r')
+ strbuf_setlen(sb, sb->len - 1);
+ }
+ return 0;
+}
+
+int strbuf_getline_lf(struct strbuf *sb, FILE *fp)
+{
+ return strbuf_getdelim(sb, fp, '\n');
+}
+
+int strbuf_getline_nul(struct strbuf *sb, FILE *fp)
+{
+ return strbuf_getdelim(sb, fp, '\0');
+}
+
int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term)
{
strbuf_reset(sb);
size_t len, i;
len = strlen(string);
- result = xmalloc(len + 1);
+ result = xmallocz(len);
for (i = 0; i < len; i++)
result[i] = tolower(string[i]);
result[i] = '\0';
#include "sha1-array.h"
#include "argv-array.h"
#include "blob.h"
+#include "thread-utils.h"
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
static struct string_list changed_submodule_paths;
struct strbuf objects_directory = STRBUF_INIT;
struct alternate_object_database *alt_odb;
int ret = 0;
- int alloc;
+ size_t alloc;
strbuf_git_path_submodule(&objects_directory, path, "objects/");
if (!is_directory(objects_directory.buf)) {
objects_directory.len))
goto done;
- alloc = objects_directory.len + 42; /* for "12/345..." sha1 */
- alt_odb = xmalloc(sizeof(*alt_odb) + alloc);
+ alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */
+ alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc));
alt_odb->next = alt_odb_list;
xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
alt_odb->name = alt_odb->base + objects_directory.len;
initialized_fetch_ref_tips = 0;
}
-int fetch_populated_submodules(const struct argv_array *options,
- const char *prefix, int command_line_option,
- int quiet)
+struct submodule_parallel_fetch {
+ int count;
+ struct argv_array args;
+ const char *work_tree;
+ const char *prefix;
+ int command_line_option;
+ int quiet;
+ int result;
+};
+#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0}
+
+static int get_next_submodule(struct child_process *cp,
+ struct strbuf *err, void *data, void **task_cb)
{
- int i, result = 0;
- struct child_process cp = CHILD_PROCESS_INIT;
- struct argv_array argv = ARGV_ARRAY_INIT;
- const char *work_tree = get_git_work_tree();
- if (!work_tree)
- goto out;
-
- if (read_cache() < 0)
- die("index file corrupt");
-
- argv_array_push(&argv, "fetch");
- for (i = 0; i < options->argc; i++)
- argv_array_push(&argv, options->argv[i]);
- argv_array_push(&argv, "--recurse-submodules-default");
- /* default value, "--submodule-prefix" and its value are added later */
-
- cp.env = local_repo_env;
- cp.git_cmd = 1;
- cp.no_stdin = 1;
-
- calculate_changed_submodule_paths();
+ int ret = 0;
+ struct submodule_parallel_fetch *spf = data;
- for (i = 0; i < active_nr; i++) {
+ for (; spf->count < active_nr; spf->count++) {
struct strbuf submodule_path = STRBUF_INIT;
struct strbuf submodule_git_dir = STRBUF_INIT;
struct strbuf submodule_prefix = STRBUF_INIT;
- const struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[spf->count];
const char *git_dir, *default_argv;
const struct submodule *submodule;
submodule = submodule_from_name(null_sha1, ce->name);
default_argv = "yes";
- if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
+ if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
if (submodule &&
submodule->fetch_recurse !=
RECURSE_SUBMODULES_NONE) {
default_argv = "on-demand";
}
}
- } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
+ } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
continue;
default_argv = "on-demand";
}
- strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
+ strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
- strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
+ strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name);
git_dir = read_gitfile(submodule_git_dir.buf);
if (!git_dir)
git_dir = submodule_git_dir.buf;
if (is_directory(git_dir)) {
- if (!quiet)
- printf("Fetching submodule %s%s\n", prefix, ce->name);
- cp.dir = submodule_path.buf;
- argv_array_push(&argv, default_argv);
- argv_array_push(&argv, "--submodule-prefix");
- argv_array_push(&argv, submodule_prefix.buf);
- cp.argv = argv.argv;
- if (run_command(&cp))
- result = 1;
- argv_array_pop(&argv);
- argv_array_pop(&argv);
- argv_array_pop(&argv);
+ child_process_init(cp);
+ cp->dir = strbuf_detach(&submodule_path, NULL);
+ cp->env = local_repo_env;
+ cp->git_cmd = 1;
+ if (!spf->quiet)
+ strbuf_addf(err, "Fetching submodule %s%s\n",
+ spf->prefix, ce->name);
+ argv_array_init(&cp->args);
+ argv_array_pushv(&cp->args, spf->args.argv);
+ argv_array_push(&cp->args, default_argv);
+ argv_array_push(&cp->args, "--submodule-prefix");
+ argv_array_push(&cp->args, submodule_prefix.buf);
+ ret = 1;
}
strbuf_release(&submodule_path);
strbuf_release(&submodule_git_dir);
strbuf_release(&submodule_prefix);
+ if (ret) {
+ spf->count++;
+ return 1;
+ }
}
- argv_array_clear(&argv);
+ return 0;
+}
+
+static int fetch_start_failure(struct child_process *cp,
+ struct strbuf *err,
+ void *cb, void *task_cb)
+{
+ struct submodule_parallel_fetch *spf = cb;
+
+ spf->result = 1;
+
+ return 0;
+}
+
+static int fetch_finish(int retvalue, struct child_process *cp,
+ struct strbuf *err, void *cb, void *task_cb)
+{
+ struct submodule_parallel_fetch *spf = cb;
+
+ if (retvalue)
+ spf->result = 1;
+
+ return 0;
+}
+
+int fetch_populated_submodules(const struct argv_array *options,
+ const char *prefix, int command_line_option,
+ int quiet, int max_parallel_jobs)
+{
+ int i;
+ struct submodule_parallel_fetch spf = SPF_INIT;
+
+ spf.work_tree = get_git_work_tree();
+ spf.command_line_option = command_line_option;
+ spf.quiet = quiet;
+ spf.prefix = prefix;
+
+ if (!spf.work_tree)
+ goto out;
+
+ if (read_cache() < 0)
+ die("index file corrupt");
+
+ argv_array_push(&spf.args, "fetch");
+ for (i = 0; i < options->argc; i++)
+ argv_array_push(&spf.args, options->argv[i]);
+ argv_array_push(&spf.args, "--recurse-submodules-default");
+ /* default value, "--submodule-prefix" and its value are added later */
+
+ calculate_changed_submodule_paths();
+ run_processes_parallel(max_parallel_jobs,
+ get_next_submodule,
+ fetch_start_failure,
+ fetch_finish,
+ &spf);
+
+ argv_array_clear(&spf.args);
out:
string_list_clear(&changed_submodule_paths, 1);
- return result;
+ return spf.result;
}
unsigned is_submodule_modified(const char *path, int ignore_untracked)
*/
static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
{
- const char *ceil = item->string;
- int len = strlen(ceil);
- char buf[PATH_MAX+1];
+ char *ceil = item->string;
- if (len == 0)
+ if (!*ceil)
die("Empty path is not supported");
- if (len > PATH_MAX)
- die("Path \"%s\" is too long", ceil);
if (!is_absolute_path(ceil))
die("Path \"%s\" is not absolute", ceil);
- if (normalize_path_copy(buf, ceil) < 0)
+ if (normalize_path_copy(ceil, ceil) < 0)
die("Path \"%s\" could not be normalized", ceil);
- len = strlen(buf);
- free(item->string);
- item->string = xstrdup(buf);
return 1;
}
if (!data[i].from)
to = func(NULL);
else {
- strcpy(buffer, data[i].from);
+ xsnprintf(buffer, sizeof(buffer), "%s", data[i].from);
to = func(buffer);
}
if (!strcmp(to, data[i].to))
int main(int argc, char **argv)
{
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
- char *buf = xmalloc(PATH_MAX + 1);
+ char *buf = xmallocz(strlen(argv[2]));
int rv = normalize_path_copy(buf, argv[2]);
if (rv)
buf = "++failed++";
#include "sha1-array.h"
#include "sigchain.h"
-/* rsync support */
-
-/*
- * We copy packed-refs and refs/ into a temporary file, then read the
- * loose refs recursively (sorting whenever possible), and then inserting
- * those packed refs that are not yet in the list (not validating, but
- * assuming that the file is sorted).
- *
- * Appears refactoring this from refs.c is too cumbersome.
- */
-
-static int str_cmp(const void *a, const void *b)
-{
- const char *s1 = a;
- const char *s2 = b;
-
- return strcmp(s1, s2);
-}
-
-/* path->buf + name_offset is expected to point to "refs/" */
-
-static int read_loose_refs(struct strbuf *path, int name_offset,
- struct ref **tail)
-{
- DIR *dir = opendir(path->buf);
- struct dirent *de;
- struct {
- char **entries;
- int nr, alloc;
- } list;
- int i, pathlen;
-
- if (!dir)
- return -1;
-
- memset (&list, 0, sizeof(list));
-
- while ((de = readdir(dir))) {
- if (is_dot_or_dotdot(de->d_name))
- continue;
- ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
- list.entries[list.nr++] = xstrdup(de->d_name);
- }
- closedir(dir);
-
- /* sort the list */
-
- qsort(list.entries, list.nr, sizeof(char *), str_cmp);
-
- pathlen = path->len;
- strbuf_addch(path, '/');
-
- for (i = 0; i < list.nr; i++, strbuf_setlen(path, pathlen + 1)) {
- strbuf_addstr(path, list.entries[i]);
- if (read_loose_refs(path, name_offset, tail)) {
- int fd = open(path->buf, O_RDONLY);
- char buffer[40];
- struct ref *next;
-
- if (fd < 0)
- continue;
- next = alloc_ref(path->buf + name_offset);
- if (read_in_full(fd, buffer, 40) != 40 ||
- get_oid_hex(buffer, &next->old_oid)) {
- close(fd);
- free(next);
- continue;
- }
- close(fd);
- (*tail)->next = next;
- *tail = next;
- }
- }
- strbuf_setlen(path, pathlen);
-
- for (i = 0; i < list.nr; i++)
- free(list.entries[i]);
- free(list.entries);
-
- return 0;
-}
-
-/* insert the packed refs for which no loose refs were found */
-
-static void insert_packed_refs(const char *packed_refs, struct ref **list)
-{
- FILE *f = fopen(packed_refs, "r");
- static char buffer[PATH_MAX];
-
- if (!f)
- return;
-
- for (;;) {
- int cmp = 0; /* assigned before used */
- int len;
-
- if (!fgets(buffer, sizeof(buffer), f)) {
- fclose(f);
- return;
- }
-
- if (!isxdigit(buffer[0]))
- continue;
- len = strlen(buffer);
- if (len && buffer[len - 1] == '\n')
- buffer[--len] = '\0';
- if (len < 41)
- continue;
- while ((*list)->next &&
- (cmp = strcmp(buffer + 41,
- (*list)->next->name)) > 0)
- list = &(*list)->next;
- if (!(*list)->next || cmp < 0) {
- struct ref *next = alloc_ref(buffer + 41);
- buffer[40] = '\0';
- if (get_oid_hex(buffer, &next->old_oid)) {
- warning ("invalid SHA-1: %s", buffer);
- free(next);
- continue;
- }
- next->next = (*list)->next;
- (*list)->next = next;
- list = &(*list)->next;
- }
- }
-}
-
static void set_upstreams(struct transport *transport, struct ref *refs,
int pretend)
{
}
}
-static const char *rsync_url(const char *url)
-{
- if (!starts_with(url, "rsync://"))
- skip_prefix(url, "rsync:", &url);
- return url;
-}
-
-static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
-{
- struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
- struct ref dummy = {NULL}, *tail = &dummy;
- struct child_process rsync = CHILD_PROCESS_INIT;
- const char *args[5];
- int temp_dir_len;
-
- if (for_push)
- return NULL;
-
- /* copy the refs to the temporary directory */
-
- strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
- if (!mkdtemp(temp_dir.buf))
- die_errno ("Could not make temporary directory");
- temp_dir_len = temp_dir.len;
-
- strbuf_addstr(&buf, rsync_url(transport->url));
- strbuf_addstr(&buf, "/refs");
-
- rsync.argv = args;
- rsync.stdout_to_stderr = 1;
- args[0] = "rsync";
- args[1] = (transport->verbose > 1) ? "-rv" : "-r";
- args[2] = buf.buf;
- args[3] = temp_dir.buf;
- args[4] = NULL;
-
- if (run_command(&rsync))
- die ("Could not run rsync to get refs");
-
- strbuf_reset(&buf);
- strbuf_addstr(&buf, rsync_url(transport->url));
- strbuf_addstr(&buf, "/packed-refs");
-
- args[2] = buf.buf;
-
- if (run_command(&rsync))
- die ("Could not run rsync to get refs");
-
- /* read the copied refs */
-
- strbuf_addstr(&temp_dir, "/refs");
- read_loose_refs(&temp_dir, temp_dir_len + 1, &tail);
- strbuf_setlen(&temp_dir, temp_dir_len);
-
- tail = &dummy;
- strbuf_addstr(&temp_dir, "/packed-refs");
- insert_packed_refs(temp_dir.buf, &tail);
- strbuf_setlen(&temp_dir, temp_dir_len);
-
- if (remove_dir_recursively(&temp_dir, 0))
- warning ("Error removing temporary directory %s.",
- temp_dir.buf);
-
- strbuf_release(&buf);
- strbuf_release(&temp_dir);
-
- return dummy.next;
-}
-
-static int fetch_objs_via_rsync(struct transport *transport,
- int nr_objs, struct ref **to_fetch)
-{
- struct child_process rsync = CHILD_PROCESS_INIT;
-
- rsync.stdout_to_stderr = 1;
- argv_array_push(&rsync.args, "rsync");
- argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
- argv_array_push(&rsync.args, "--ignore-existing");
- argv_array_push(&rsync.args, "--exclude");
- argv_array_push(&rsync.args, "info");
- argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url));
- argv_array_push(&rsync.args, get_object_directory());
-
- /* NEEDSWORK: handle one level of alternates */
- return run_command(&rsync);
-}
-
-static int write_one_ref(const char *name, const struct object_id *oid,
- int flags, void *data)
-{
- struct strbuf *buf = data;
- int len = buf->len;
-
- /* when called via for_each_ref(), flags is non-zero */
- if (flags && !starts_with(name, "refs/heads/") &&
- !starts_with(name, "refs/tags/"))
- return 0;
-
- strbuf_addstr(buf, name);
- if (safe_create_leading_directories(buf->buf) ||
- write_file_gently(buf->buf, "%s", oid_to_hex(oid)))
- return error("problems writing temporary file %s: %s",
- buf->buf, strerror(errno));
- strbuf_setlen(buf, len);
- return 0;
-}
-
-static int write_refs_to_temp_dir(struct strbuf *temp_dir,
- int refspec_nr, const char **refspec)
-{
- int i;
-
- for (i = 0; i < refspec_nr; i++) {
- struct object_id oid;
- char *ref;
-
- if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1)
- return error("Could not get ref %s", refspec[i]);
-
- if (write_one_ref(ref, &oid, 0, temp_dir)) {
- free(ref);
- return -1;
- }
- free(ref);
- }
- return 0;
-}
-
-static int rsync_transport_push(struct transport *transport,
- int refspec_nr, const char **refspec, int flags)
-{
- struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
- int result = 0, i;
- struct child_process rsync = CHILD_PROCESS_INIT;
- const char *args[10];
-
- if (flags & TRANSPORT_PUSH_MIRROR)
- return error("rsync transport does not support mirror mode");
-
- /* first push the objects */
-
- strbuf_addstr(&buf, rsync_url(transport->url));
- strbuf_addch(&buf, '/');
-
- rsync.argv = args;
- rsync.stdout_to_stderr = 1;
- i = 0;
- args[i++] = "rsync";
- args[i++] = "-a";
- if (flags & TRANSPORT_PUSH_DRY_RUN)
- args[i++] = "--dry-run";
- if (transport->verbose > 1)
- args[i++] = "-v";
- args[i++] = "--ignore-existing";
- args[i++] = "--exclude";
- args[i++] = "info";
- args[i++] = get_object_directory();
- args[i++] = buf.buf;
- args[i++] = NULL;
-
- if (run_command(&rsync))
- return error("Could not push objects to %s",
- rsync_url(transport->url));
-
- /* copy the refs to the temporary directory; they could be packed. */
-
- strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
- if (!mkdtemp(temp_dir.buf))
- die_errno ("Could not make temporary directory");
- strbuf_addch(&temp_dir, '/');
-
- if (flags & TRANSPORT_PUSH_ALL) {
- if (for_each_ref(write_one_ref, &temp_dir))
- return -1;
- } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec))
- return -1;
-
- i = 2;
- if (flags & TRANSPORT_PUSH_DRY_RUN)
- args[i++] = "--dry-run";
- if (!(flags & TRANSPORT_PUSH_FORCE))
- args[i++] = "--ignore-existing";
- args[i++] = temp_dir.buf;
- args[i++] = rsync_url(transport->url);
- args[i++] = NULL;
- if (run_command(&rsync))
- result = error("Could not push to %s",
- rsync_url(transport->url));
-
- if (remove_dir_recursively(&temp_dir, 0))
- warning ("Could not remove temporary directory %s.",
- temp_dir.buf);
-
- strbuf_release(&buf);
- strbuf_release(&temp_dir);
-
- return result;
-}
-
struct bundle_transport_data {
int fd;
struct bundle_header header;
return 1;
}
-static int connect_setup(struct transport *transport, int for_push, int verbose)
+static int connect_setup(struct transport *transport, int for_push)
{
struct git_transport_data *data = transport->data;
+ int flags = transport->verbose > 0 ? CONNECT_VERBOSE : 0;
if (data->conn)
return 0;
+ switch (transport->family) {
+ case TRANSPORT_FAMILY_ALL: break;
+ case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;
+ case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;
+ }
+
data->conn = git_connect(data->fd, transport->url,
for_push ? data->options.receivepack :
data->options.uploadpack,
- verbose ? CONNECT_VERBOSE : 0);
+ flags);
return 0;
}
struct git_transport_data *data = transport->data;
struct ref *refs;
- connect_setup(transport, for_push, 0);
+ connect_setup(transport, for_push);
get_remote_heads(data->fd[0], NULL, 0, &refs,
for_push ? REF_NORMAL : 0,
&data->extra_have,
args.update_shallow = data->options.update_shallow;
if (!data->got_remote_heads) {
- connect_setup(transport, 0, 0);
+ connect_setup(transport, 0);
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
NULL, &data->shallow);
data->got_remote_heads = 1;
if (!data->got_remote_heads) {
struct ref *tmp_refs;
- connect_setup(transport, 1, 0);
+ connect_setup(transport, 1);
get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL,
NULL, &data->shallow);
if (helper) {
transport_helper_init(ret, helper);
} else if (starts_with(url, "rsync:")) {
- transport_check_allowed("rsync");
- ret->get_refs_list = get_refs_via_rsync;
- ret->fetch = fetch_objs_via_rsync;
- ret->push = rsync_transport_push;
- ret->smart_options = NULL;
+ die("git-over-rsync is no longer supported");
} else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
transport_check_allowed("file");
* This condition shouldn't be met in a non-deepening fetch
* (see builtin/fetch.c:quickfetch()).
*/
- heads = xmalloc(nr_refs * sizeof(*heads));
+ ALLOC_ARRAY(heads, nr_refs);
for (rm = refs; rm; rm = rm->next)
heads[nr_heads++] = rm;
}
*/
char *transport_anonymize_url(const char *url)
{
- char *anon_url, *scheme_prefix, *anon_part;
+ char *scheme_prefix, *anon_part;
size_t anon_len, prefix_len = 0;
anon_part = strchr(url, '@');
goto literal_copy;
prefix_len = scheme_prefix - url + 3;
}
- anon_url = xcalloc(1, 1 + prefix_len + anon_len);
- memcpy(anon_url, url, prefix_len);
- memcpy(anon_url + prefix_len, anon_part, anon_len);
- return anon_url;
+ return xstrfmt("%.*s%.*s", (int)prefix_len, url,
+ (int)anon_len, anon_part);
literal_copy:
return xstrdup(url);
}
{
void *ret;
+ if (unsigned_mult_overflows(nmemb, size))
+ die("data too large to fit into virtual memory space");
+
memory_limit_check(size * nmemb, 0);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
len = MAX_IO_SIZE;
while (1) {
nr = read(fd, buf, len);
- if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
- continue;
+ if (nr < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ struct pollfd pfd;
+ pfd.events = POLLIN;
+ pfd.fd = fd;
+ /*
+ * it is OK if this poll() failed; we
+ * want to leave this infinite loop
+ * only when read() returns with
+ * success, or an expected failure,
+ * which would be checked by the next
+ * call to read(2).
+ */
+ poll(&pfd, 1, -1);
+ }
+ }
return nr;
}
}