From: Junio C Hamano Date: Sun, 25 May 2008 21:25:02 +0000 (-0700) Subject: Merge branch 'js/config-cb' X-Git-Tag: v1.5.6-rc0^0 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/9bd81e4249a419f9cde8fd68e033e263533f4914?ds=inline;hp=-c Merge branch 'js/config-cb' * js/config-cb: Provide git_config with a callback-data parameter Conflicts: builtin-add.c builtin-cat-file.c --- 9bd81e4249a419f9cde8fd68e033e263533f4914 diff --combined builtin-add.c index 6e4e645cb7,15def7dada..1da22eec91 --- a/builtin-add.c +++ b/builtin-add.c @@@ -79,18 -79,12 +79,18 @@@ static void fill_directory(struct dir_s prune_directory(dir, pathspec, baselen); } +struct update_callback_data +{ + int flags; + int add_errors; +}; + static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { - int i, verbose; + int i; + struct update_callback_data *data = cbdata; - verbose = *((int *)cbdata); for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; @@@ -100,36 -94,27 +100,36 @@@ case DIFF_STATUS_UNMERGED: case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: - add_file_to_cache(path, verbose); + if (add_file_to_cache(path, data->flags)) { + if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) + die("updating files failed"); + data->add_errors++; + } break; case DIFF_STATUS_DELETED: - remove_file_from_cache(path); - if (verbose) + if (!(data->flags & ADD_CACHE_PRETEND)) + remove_file_from_cache(path); + if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) printf("remove '%s'\n", path); break; } } } -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec) +int add_files_to_cache(const char *prefix, const char **pathspec, int flags) { + struct update_callback_data data; struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.prune_data = pathspec; rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = &verbose; + data.flags = flags; + data.add_errors = 0; + rev.diffopt.format_callback_data = &data; run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); + return !!data.add_errors; } static void refresh(int verbose, const char **pathspec) @@@ -192,7 -177,6 +192,7 @@@ static const char ignore_error[] "The following paths are ignored by one of your .gitignore files:\n"; static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; +static int ignore_add_errors; static struct option builtin_add_options[] = { OPT__DRY_RUN(&show_only), @@@ -203,26 -187,14 +203,26 @@@ OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), + OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), OPT_END(), }; - static int add_config(const char *var, const char *value) ++static int add_config(const char *var, const char *value, void *cb) +{ + if (!strcasecmp(var, "add.ignore-errors")) { + ignore_add_errors = git_config_bool(var, value); + return 0; + } - return git_default_config(var, value); ++ return git_default_config(var, value, cb); +} + int cmd_add(int argc, const char **argv, const char *prefix) { + int exit_status = 0; int i, newfd; const char **pathspec; struct dir_struct dir; + int flags; argc = parse_options(argc, argv, builtin_add_options, builtin_add_usage, 0); @@@ -231,20 -203,16 +231,20 @@@ if (add_interactive) exit(interactive_add(argc, argv, prefix)); - git_config(add_config); - git_config(git_default_config, NULL); ++ git_config(add_config, NULL); newfd = hold_locked_index(&lock_file, 1); + flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | + (show_only ? ADD_CACHE_PRETEND : 0) | + (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0)); + if (take_worktree_changes) { const char **pathspec; if (read_cache() < 0) die("index file corrupt"); pathspec = get_pathspec(prefix, argv); - add_files_to_cache(verbose, prefix, pathspec); + exit_status = add_files_to_cache(prefix, pathspec, flags); goto finish; } @@@ -262,6 -230,17 +262,6 @@@ fill_directory(&dir, pathspec, ignored_too); - if (show_only) { - const char *sep = "", *eof = ""; - for (i = 0; i < dir.nr; i++) { - printf("%s%s", sep, dir.entries[i]->name); - sep = " "; - eof = "\n"; - } - fputs(eof, stdout); - return 0; - } - if (read_cache() < 0) die("index file corrupt"); @@@ -275,11 -254,7 +275,11 @@@ } for (i = 0; i < dir.nr; i++) - add_file_to_cache(dir.entries[i]->name, verbose); + if (add_file_to_cache(dir.entries[i]->name, flags)) { + if (!ignore_add_errors) + die("adding files failed"); + exit_status = 1; + } finish: if (active_cache_changed) { @@@ -288,5 -263,5 +288,5 @@@ die("Unable to write new index file"); } - return 0; + return exit_status; } diff --combined builtin-apply.c index 1540f28ab4,bbdf08a10a..c497889312 --- a/builtin-apply.c +++ b/builtin-apply.c @@@ -418,7 -418,7 +418,7 @@@ static int guess_p_value(const char *na } /* - * Get the name etc info from the --/+++ lines of a traditional patch header + * Get the name etc info from the ---/+++ lines of a traditional patch header * * FIXME! The end-of-filename heuristics are kind of screwy. For existing * files, we can happily check the index for a match, but for creating a @@@ -1143,6 -1143,21 +1143,6 @@@ static int parse_single_patch(char *lin if (patch->is_delete < 0 && (newlines || (patch->fragments && patch->fragments->next))) patch->is_delete = 0; - if (!unidiff_zero || context) { - /* If the user says the patch is not generated with - * --unified=0, or if we have seen context lines, - * then not having oldlines means the patch is creation, - * and not having newlines means the patch is deletion. - */ - if (patch->is_new < 0 && !oldlines) { - patch->is_new = 1; - patch->old_name = NULL; - } - if (patch->is_delete < 0 && !newlines) { - patch->is_delete = 1; - patch->new_name = NULL; - } - } if (0 < patch->is_new && oldlines) die("new file %s depends on old contents", patch->new_name); @@@ -2252,11 -2267,16 +2252,11 @@@ static int verify_index_match(struct ca return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID); } -static int check_patch(struct patch *patch, struct patch *prev_patch) +static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) { - struct stat st; const char *old_name = patch->old_name; - const char *new_name = patch->new_name; - const char *name = old_name ? old_name : new_name; - struct cache_entry *ce = NULL; - int ok_if_exists; - - patch->rejected = 1; /* we will drop this after we succeed */ + int stat_ret = 0; + unsigned st_mode = 0; /* * Make sure that we do not have local modifications from the @@@ -2264,84 -2284,58 +2264,84 @@@ * we have the preimage file to be patched in the work tree, * unless --cached, which tells git to apply only in the index. */ - if (old_name) { - int stat_ret = 0; - unsigned st_mode = 0; - - if (!cached) - stat_ret = lstat(old_name, &st); - if (check_index) { - int pos = cache_name_pos(old_name, strlen(old_name)); - if (pos < 0) - return error("%s: does not exist in index", - old_name); - ce = active_cache[pos]; - if (stat_ret < 0) { - struct checkout costate; - if (errno != ENOENT) - return error("%s: %s", old_name, - strerror(errno)); - /* checkout */ - costate.base_dir = ""; - costate.base_dir_len = 0; - costate.force = 0; - costate.quiet = 0; - costate.not_new = 0; - costate.refresh_cache = 1; - if (checkout_entry(ce, - &costate, - NULL) || - lstat(old_name, &st)) - return -1; - } - if (!cached && verify_index_match(ce, &st)) - return error("%s: does not match index", - old_name); - if (cached) - st_mode = ce->ce_mode; - } else if (stat_ret < 0) - return error("%s: %s", old_name, strerror(errno)); - - if (!cached) - st_mode = ce_mode_from_stat(ce, st.st_mode); + if (!old_name) + return 0; + assert(patch->is_new <= 0); + if (!cached) { + stat_ret = lstat(old_name, st); + if (stat_ret && errno != ENOENT) + return error("%s: %s", old_name, strerror(errno)); + } + if (check_index) { + int pos = cache_name_pos(old_name, strlen(old_name)); + if (pos < 0) { + if (patch->is_new < 0) + goto is_new; + return error("%s: does not exist in index", old_name); + } + *ce = active_cache[pos]; + if (stat_ret < 0) { + struct checkout costate; + /* checkout */ + costate.base_dir = ""; + costate.base_dir_len = 0; + costate.force = 0; + costate.quiet = 0; + costate.not_new = 0; + costate.refresh_cache = 1; + if (checkout_entry(*ce, &costate, NULL) || + lstat(old_name, st)) + return -1; + } + if (!cached && verify_index_match(*ce, st)) + return error("%s: does not match index", old_name); + if (cached) + st_mode = (*ce)->ce_mode; + } else if (stat_ret < 0) { if (patch->is_new < 0) - patch->is_new = 0; - if (!patch->old_mode) - patch->old_mode = st_mode; - if ((st_mode ^ patch->old_mode) & S_IFMT) - return error("%s: wrong type", old_name); - if (st_mode != patch->old_mode) - fprintf(stderr, "warning: %s has type %o, expected %o\n", - old_name, st_mode, patch->old_mode); + goto is_new; + return error("%s: %s", old_name, strerror(errno)); } + if (!cached) + st_mode = ce_mode_from_stat(*ce, st->st_mode); + + if (patch->is_new < 0) + patch->is_new = 0; + if (!patch->old_mode) + patch->old_mode = st_mode; + if ((st_mode ^ patch->old_mode) & S_IFMT) + return error("%s: wrong type", old_name); + if (st_mode != patch->old_mode) + fprintf(stderr, "warning: %s has type %o, expected %o\n", + old_name, st_mode, patch->old_mode); + return 0; + + is_new: + patch->is_new = 1; + patch->is_delete = 0; + patch->old_name = NULL; + return 0; +} + +static int check_patch(struct patch *patch, struct patch *prev_patch) +{ + struct stat st; + const char *old_name = patch->old_name; + const char *new_name = patch->new_name; + const char *name = old_name ? old_name : new_name; + struct cache_entry *ce = NULL; + int ok_if_exists; + int status; + + patch->rejected = 1; /* we will drop this after we succeed */ + + status = check_preimage(patch, &ce, &st); + if (status) + return status; + old_name = patch->old_name; + if (new_name && prev_patch && 0 < prev_patch->is_delete && !strcmp(prev_patch->old_name, new_name)) /* @@@ -2985,11 -2979,11 +2985,11 @@@ static int apply_patch(int fd, const ch return 0; } - static int git_apply_config(const char *var, const char *value) + static int git_apply_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "apply.whitespace")) return git_config_string(&apply_default_whitespace, var, value); - return git_default_config(var, value); + return git_default_config(var, value, cb); } @@@ -3005,7 -2999,7 +3005,7 @@@ int cmd_apply(int argc, const char **ar prefix = setup_git_directory_gently(&is_not_gitdir); prefix_length = prefix ? strlen(prefix) : 0; - git_config(git_apply_config); + git_config(git_apply_config, NULL); if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); diff --combined builtin-cat-file.c index 5ef15a4fa9,b488fad431..200345e7fb --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@@ -8,10 -8,6 +8,10 @@@ #include "tag.h" #include "tree.h" #include "builtin.h" +#include "parse-options.h" + +#define BATCH 1 +#define BATCH_CHECK 2 static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size) { @@@ -80,16 -76,31 +80,16 @@@ write_or_die(1, cp, endp - cp); } -int cmd_cat_file(int argc, const char **argv, const char *prefix) +static int cat_one_file(int opt, const char *exp_type, const char *obj_name) { unsigned char sha1[20]; enum object_type type; void *buf; unsigned long size; - int opt; - const char *exp_type, *obj_name; - - git_config(git_default_config, NULL); - if (argc != 3) - usage("git-cat-file [-t|-s|-e|-p|] "); - exp_type = argv[1]; - obj_name = argv[2]; if (get_sha1(obj_name, sha1)) die("Not a valid object name %s", obj_name); - opt = 0; - if ( exp_type[0] == '-' ) { - opt = exp_type[1]; - if ( !opt || exp_type[2] ) - opt = -1; /* Not a single character option */ - } - buf = NULL; switch (opt) { case 't': @@@ -146,108 -157,3 +146,108 @@@ write_or_die(1, buf, size); return 0; } + +static int batch_one_object(const char *obj_name, int print_contents) +{ + unsigned char sha1[20]; + enum object_type type; + unsigned long size; + void *contents = contents; + + if (!obj_name) + return 1; + + if (get_sha1(obj_name, sha1)) { + printf("%s missing\n", obj_name); + return 0; + } + + if (print_contents == BATCH) + contents = read_sha1_file(sha1, &type, &size); + else + type = sha1_object_info(sha1, &size); + + if (type <= 0) + return 1; + + printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); + fflush(stdout); + + if (print_contents == BATCH) { + write_or_die(1, contents, size); + printf("\n"); + fflush(stdout); + } + + return 0; +} + +static int batch_objects(int print_contents) +{ + struct strbuf buf; + + strbuf_init(&buf, 0); + while (strbuf_getline(&buf, stdin, '\n') != EOF) { + int error = batch_one_object(buf.buf, print_contents); + if (error) + return error; + } + + return 0; +} + +static const char * const cat_file_usage[] = { + "git-cat-file [-t|-s|-e|-p|] ", + "git-cat-file [--batch|--batch-check] < ", + NULL +}; + +int cmd_cat_file(int argc, const char **argv, const char *prefix) +{ + int opt = 0, batch = 0; + const char *exp_type = NULL, *obj_name = NULL; + + const struct option options[] = { + OPT_GROUP(" can be one of: blob, tree, commit, tag"), + OPT_SET_INT('t', NULL, &opt, "show object type", 't'), + OPT_SET_INT('s', NULL, &opt, "show object size", 's'), + OPT_SET_INT('e', NULL, &opt, + "exit with zero when there's no error", 'e'), + OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'), + OPT_SET_INT(0, "batch", &batch, + "show info and content of objects feeded on stdin", BATCH), + OPT_SET_INT(0, "batch-check", &batch, + "show info about objects feeded on stdin", + BATCH_CHECK), + OPT_END() + }; + - git_config(git_default_config); ++ git_config(git_default_config, NULL); + + if (argc != 3 && argc != 2) + usage_with_options(cat_file_usage, options); + + argc = parse_options(argc, argv, options, cat_file_usage, 0); + + if (opt) { + if (argc == 1) + obj_name = argv[0]; + else + usage_with_options(cat_file_usage, options); + } + if (!opt && !batch) { + if (argc == 2) { + exp_type = argv[0]; + obj_name = argv[1]; + } else + usage_with_options(cat_file_usage, options); + } + if (batch && (opt || argc)) { + usage_with_options(cat_file_usage, options); + } + + if (batch) + return batch_objects(batch); + + return cat_one_file(opt, exp_type, obj_name); +} diff --combined builtin-checkout.c index 68fffd28cb,c077134e62..1ea017f5f7 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@@ -236,8 -236,6 +236,8 @@@ static int merge_working_tree(struct ch topts.src_index = &the_index; topts.dst_index = &the_index; + topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches."; + refresh_cache(REFRESH_QUIET); if (unmerged_cache()) { @@@ -284,7 -282,7 +284,7 @@@ * entries in the index. */ - add_files_to_cache(0, NULL, NULL); + add_files_to_cache(NULL, NULL, 0); work = write_tree_from_memory(); ret = reset_to_new(new->commit->tree, opts->quiet); @@@ -516,7 -514,7 +516,7 @@@ int cmd_checkout(int argc, const char * memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); - git_config(git_default_config); + git_config(git_default_config, NULL); opts.track = git_branch_track; diff --combined builtin-clone.c index 2a3f6732f2,0000000000..4740b13067 mode 100644,000000..100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@@ -1,550 -1,0 +1,550 @@@ +/* + * Builtin "git clone" + * + * Copyright (c) 2007 Kristian Høgsberg , + * 2008 Daniel Barkalow + * Based on git-commit.sh by Junio C Hamano and Linus Torvalds + * + * Clone a repository into a different directory that does not yet exist. + */ + +#include "cache.h" +#include "parse-options.h" +#include "fetch-pack.h" +#include "refs.h" +#include "tree.h" +#include "tree-walk.h" +#include "unpack-trees.h" +#include "transport.h" +#include "strbuf.h" +#include "dir.h" + +/* + * Overall FIXMEs: + * - respect DB_ENVIRONMENT for .git/objects. + * + * Implementation notes: + * - dropping use-separate-remote and no-separate-remote compatibility + * + */ +static const char * const builtin_clone_usage[] = { + "git-clone [options] [--] []", + NULL +}; + +static int option_quiet, option_no_checkout, option_bare; +static int option_local, option_no_hardlinks, option_shared; +static char *option_template, *option_reference, *option_depth; +static char *option_origin = NULL; +static char *option_upload_pack = "git-upload-pack"; + +static struct option builtin_clone_options[] = { + OPT__QUIET(&option_quiet), + OPT_BOOLEAN('n', "no-checkout", &option_no_checkout, + "don't create a checkout"), + OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"), + OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"), + OPT_BOOLEAN('l', "local", &option_local, + "to clone from a local repository"), + OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks, + "don't use local hardlinks, always copy"), + OPT_BOOLEAN('s', "shared", &option_shared, + "setup as shared repository"), + OPT_STRING(0, "template", &option_template, "path", + "path the template repository"), + OPT_STRING(0, "reference", &option_reference, "repo", + "reference repository"), + OPT_STRING('o', "origin", &option_origin, "branch", + "use instead or 'origin' to track upstream"), + OPT_STRING('u', "upload-pack", &option_upload_pack, "path", + "path to git-upload-pack on the remote"), + OPT_STRING(0, "depth", &option_depth, "depth", + "create a shallow clone of that depth"), + + OPT_END() +}; + +static char *get_repo_path(const char *repo, int *is_bundle) +{ + static char *suffix[] = { "/.git", ".git", "" }; + static char *bundle_suffix[] = { ".bundle", "" }; + struct stat st; + int i; + + for (i = 0; i < ARRAY_SIZE(suffix); i++) { + const char *path; + path = mkpath("%s%s", repo, suffix[i]); + if (!stat(path, &st) && S_ISDIR(st.st_mode)) { + *is_bundle = 0; + return xstrdup(make_absolute_path(path)); + } + } + + for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) { + const char *path; + path = mkpath("%s%s", repo, bundle_suffix[i]); + if (!stat(path, &st) && S_ISREG(st.st_mode)) { + *is_bundle = 1; + return xstrdup(make_absolute_path(path)); + } + } + + return NULL; +} + +static char *guess_dir_name(const char *repo, int is_bundle) +{ + const char *p, *start, *end, *limit; + int after_slash_or_colon; + + /* Guess dir name from repository: strip trailing '/', + * strip trailing '[:/]*.{git,bundle}', strip leading '.*[/:]'. */ + + after_slash_or_colon = 1; + limit = repo + strlen(repo); + start = repo; + end = limit; + for (p = repo; p < limit; p++) { + const char *prefix = is_bundle ? ".bundle" : ".git"; + if (!prefixcmp(p, prefix)) { + if (!after_slash_or_colon) + end = p; + p += strlen(prefix) - 1; + } else if (!prefixcmp(p, ".bundle")) { + if (!after_slash_or_colon) + end = p; + p += 7; + } else if (*p == '/' || *p == ':') { + if (end == limit) + end = p; + after_slash_or_colon = 1; + } else if (after_slash_or_colon) { + start = p; + end = limit; + after_slash_or_colon = 0; + } + } + + return xstrndup(start, end - start); +} + +static int is_directory(const char *path) +{ + struct stat buf; + + return !stat(path, &buf) && S_ISDIR(buf.st_mode); +} + +static void setup_reference(const char *repo) +{ + const char *ref_git; + char *ref_git_copy; + + struct remote *remote; + struct transport *transport; + const struct ref *extra; + + ref_git = make_absolute_path(option_reference); + + if (is_directory(mkpath("%s/.git/objects", ref_git))) + ref_git = mkpath("%s/.git", ref_git); + else if (!is_directory(mkpath("%s/objects", ref_git))) + die("reference repository '%s' is not a local directory.", + option_reference); + + ref_git_copy = xstrdup(ref_git); + + add_to_alternates_file(ref_git_copy); + + remote = remote_get(ref_git_copy); + transport = transport_get(remote, ref_git_copy); + for (extra = transport_get_remote_refs(transport); extra; + extra = extra->next) + add_extra_ref(extra->name, extra->old_sha1, 0); + + transport_disconnect(transport); + + free(ref_git_copy); +} + +static void copy_or_link_directory(char *src, char *dest) +{ + struct dirent *de; + struct stat buf; + int src_len, dest_len; + DIR *dir; + + dir = opendir(src); + if (!dir) + die("failed to open %s\n", src); + + if (mkdir(dest, 0777)) { + if (errno != EEXIST) + die("failed to create directory %s\n", dest); + else if (stat(dest, &buf)) + die("failed to stat %s\n", dest); + else if (!S_ISDIR(buf.st_mode)) + die("%s exists and is not a directory\n", dest); + } + + src_len = strlen(src); + src[src_len] = '/'; + dest_len = strlen(dest); + dest[dest_len] = '/'; + + while ((de = readdir(dir)) != NULL) { + strcpy(src + src_len + 1, de->d_name); + strcpy(dest + dest_len + 1, de->d_name); + if (stat(src, &buf)) { + warning ("failed to stat %s\n", src); + continue; + } + if (S_ISDIR(buf.st_mode)) { + if (de->d_name[0] != '.') + copy_or_link_directory(src, dest); + continue; + } + + if (unlink(dest) && errno != ENOENT) + die("failed to unlink %s\n", dest); + if (!option_no_hardlinks) { + if (!link(src, dest)) + continue; + if (option_local) + die("failed to create link %s\n", dest); + option_no_hardlinks = 1; + } + if (copy_file(dest, src, 0666)) + die("failed to copy file to %s\n", dest); + } + closedir(dir); +} + +static const struct ref *clone_local(const char *src_repo, + const char *dest_repo) +{ + const struct ref *ret; + char src[PATH_MAX]; + char dest[PATH_MAX]; + struct remote *remote; + struct transport *transport; + + if (option_shared) + add_to_alternates_file(src_repo); + else { + snprintf(src, PATH_MAX, "%s/objects", src_repo); + snprintf(dest, PATH_MAX, "%s/objects", dest_repo); + copy_or_link_directory(src, dest); + } + + remote = remote_get(src_repo); + transport = transport_get(remote, src_repo); + ret = transport_get_remote_refs(transport); + transport_disconnect(transport); + return ret; +} + +static const char *junk_work_tree; +static const char *junk_git_dir; +pid_t junk_pid; + +static void remove_junk(void) +{ + struct strbuf sb; + if (getpid() != junk_pid) + return; + strbuf_init(&sb, 0); + if (junk_git_dir) { + strbuf_addstr(&sb, junk_git_dir); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } + if (junk_work_tree) { + strbuf_addstr(&sb, junk_work_tree); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } +} + +static void remove_junk_on_signal(int signo) +{ + remove_junk(); + signal(SIGINT, SIG_DFL); + raise(signo); +} + +static const struct ref *locate_head(const struct ref *refs, + const struct ref *mapped_refs, + const struct ref **remote_head_p) +{ + const struct ref *remote_head = NULL; + const struct ref *remote_master = NULL; + const struct ref *r; + for (r = refs; r; r = r->next) + if (!strcmp(r->name, "HEAD")) + remote_head = r; + + for (r = mapped_refs; r; r = r->next) + if (!strcmp(r->name, "refs/heads/master")) + remote_master = r; + + if (remote_head_p) + *remote_head_p = remote_head; + + /* If there's no HEAD value at all, never mind. */ + if (!remote_head) + return NULL; + + /* If refs/heads/master could be right, it is. */ + if (remote_master && !hashcmp(remote_master->old_sha1, + remote_head->old_sha1)) + return remote_master; + + /* Look for another ref that points there */ + for (r = mapped_refs; r; r = r->next) + if (r != remote_head && + !hashcmp(r->old_sha1, remote_head->old_sha1)) + return r; + + /* Nothing is the same */ + return NULL; +} + +static struct ref *write_remote_refs(const struct ref *refs, + struct refspec *refspec, const char *reflog) +{ + struct ref *local_refs = NULL; + struct ref **tail = &local_refs; + struct ref *r; + + get_fetch_map(refs, refspec, &tail, 0); + get_fetch_map(refs, tag_refspec, &tail, 0); + + for (r = local_refs; r; r = r->next) + update_ref(reflog, + r->peer_ref->name, r->old_sha1, NULL, 0, DIE_ON_ERR); + return local_refs; +} + +int cmd_clone(int argc, const char **argv, const char *prefix) +{ + int use_local_hardlinks = 1; + int use_separate_remote = 1; + int is_bundle = 0; + struct stat buf; + const char *repo_name, *repo, *work_tree, *git_dir; + char *path, *dir; + const struct ref *refs, *head_points_at, *remote_head, *mapped_refs; + char branch_top[256], key[256], value[256]; + struct strbuf reflog_msg; + + struct refspec refspec; + + junk_pid = getpid(); + + argc = parse_options(argc, argv, builtin_clone_options, + builtin_clone_usage, 0); + + if (argc == 0) + die("You must specify a repository to clone."); + + if (option_no_hardlinks) + use_local_hardlinks = 0; + + if (option_bare) { + if (option_origin) + die("--bare and --origin %s options are incompatible.", + option_origin); + option_no_checkout = 1; + use_separate_remote = 0; + } + + if (!option_origin) + option_origin = "origin"; + + repo_name = argv[0]; + + path = get_repo_path(repo_name, &is_bundle); + if (path) + repo = path; + else if (!strchr(repo_name, ':')) + repo = xstrdup(make_absolute_path(repo_name)); + else + repo = repo_name; + + if (argc == 2) + dir = xstrdup(argv[1]); + else + dir = guess_dir_name(repo_name, is_bundle); + + if (!stat(dir, &buf)) + die("destination directory '%s' already exists.", dir); + + strbuf_init(&reflog_msg, 0); + strbuf_addf(&reflog_msg, "clone: from %s", repo); + + if (option_bare) + work_tree = NULL; + else { + work_tree = getenv("GIT_WORK_TREE"); + if (work_tree && !stat(work_tree, &buf)) + die("working tree '%s' already exists.", work_tree); + } + + if (option_bare || work_tree) + git_dir = xstrdup(dir); + else { + work_tree = dir; + git_dir = xstrdup(mkpath("%s/.git", dir)); + } + + if (!option_bare) { + junk_work_tree = work_tree; + if (mkdir(work_tree, 0755)) + die("could not create work tree dir '%s'.", work_tree); + set_git_work_tree(work_tree); + } + junk_git_dir = git_dir; + atexit(remove_junk); + signal(SIGINT, remove_junk_on_signal); + + setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); + + set_git_dir(make_absolute_path(git_dir)); + + fprintf(stderr, "Initialize %s\n", git_dir); + init_db(option_template, option_quiet ? INIT_DB_QUIET : 0); + + if (option_reference) + setup_reference(git_dir); + - git_config(git_default_config); ++ git_config(git_default_config, NULL); + + if (option_bare) { + strcpy(branch_top, "refs/heads/"); + + git_config_set("core.bare", "true"); + } else { + snprintf(branch_top, sizeof(branch_top), + "refs/remotes/%s/", option_origin); + + /* Configure the remote */ + snprintf(key, sizeof(key), "remote.%s.url", option_origin); + git_config_set(key, repo); + + snprintf(key, sizeof(key), "remote.%s.fetch", option_origin); + snprintf(value, sizeof(value), + "+refs/heads/*:%s*", branch_top); + git_config_set_multivar(key, value, "^$", 0); + } + + refspec.force = 0; + refspec.pattern = 1; + refspec.src = "refs/heads/"; + refspec.dst = branch_top; + + if (path && !is_bundle) + refs = clone_local(path, git_dir); + else { + struct remote *remote = remote_get(argv[0]); + struct transport *transport = transport_get(remote, argv[0]); + + transport_set_option(transport, TRANS_OPT_KEEP, "yes"); + + if (option_depth) + transport_set_option(transport, TRANS_OPT_DEPTH, + option_depth); + + if (option_quiet) + transport->verbose = -1; + + refs = transport_get_remote_refs(transport); + transport_fetch_refs(transport, refs); + } + + clear_extra_refs(); + + mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); + + head_points_at = locate_head(refs, mapped_refs, &remote_head); + + if (head_points_at) { + /* Local default branch link */ + create_symref("HEAD", head_points_at->name, NULL); + + if (!option_bare) { + struct strbuf head_ref; + const char *head = head_points_at->name; + + if (!prefixcmp(head, "refs/heads/")) + head += 11; + + /* Set up the initial local branch */ + + /* Local branch initial value */ + update_ref(reflog_msg.buf, "HEAD", + head_points_at->old_sha1, + NULL, 0, DIE_ON_ERR); + + strbuf_init(&head_ref, 0); + strbuf_addstr(&head_ref, branch_top); + strbuf_addstr(&head_ref, "HEAD"); + + /* Remote branch link */ + create_symref(head_ref.buf, + head_points_at->peer_ref->name, + reflog_msg.buf); + + snprintf(key, sizeof(key), "branch.%s.remote", head); + git_config_set(key, option_origin); + snprintf(key, sizeof(key), "branch.%s.merge", head); + git_config_set(key, head_points_at->name); + } + } else if (remote_head) { + /* Source had detached HEAD pointing somewhere. */ + if (!option_bare) + update_ref(reflog_msg.buf, "HEAD", + remote_head->old_sha1, + NULL, REF_NODEREF, DIE_ON_ERR); + } else { + /* Nothing to checkout out */ + if (!option_no_checkout) + warning("remote HEAD refers to nonexistent ref, " + "unable to checkout.\n"); + option_no_checkout = 1; + } + + if (!option_no_checkout) { + struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); + struct unpack_trees_options opts; + struct tree *tree; + struct tree_desc t; + int fd; + + /* We need to be in the new work tree for the checkout */ + setup_work_tree(); + + fd = hold_locked_index(lock_file, 1); + + memset(&opts, 0, sizeof opts); + opts.update = 1; + opts.merge = 1; + opts.fn = oneway_merge; + opts.verbose_update = !option_quiet; + opts.src_index = &the_index; + opts.dst_index = &the_index; + + tree = parse_tree_indirect(remote_head->old_sha1); + parse_tree(tree); + init_tree_desc(&t, tree->buffer, tree->size); + unpack_trees(1, &t, &opts); + + if (write_cache(fd, active_cache, active_nr) || + commit_locked_index(lock_file)) + die("unable to write new index file"); + } + + strbuf_release(&reflog_msg); + junk_pid = 0; + return 0; +} diff --combined builtin-commit.c index d75224381b,9f0026ed00..07872c8ea7 --- a/builtin-commit.c +++ b/builtin-commit.c @@@ -47,7 -47,6 +47,7 @@@ static enum static char *logfile, *force_author, *template_file; static char *edit_message, *use_message; +static char *author_name, *author_email, *author_date; static int all, edit_flag, also, interactive, only, amend, signoff; static int quiet, verbose, untracked_files, no_verify, allow_empty; /* @@@ -179,10 -178,9 +179,10 @@@ static void add_remove_files(struct pat struct stat st; struct path_list_item *p = &(list->items[i]); - if (!lstat(p->path, &st)) - add_to_cache(p->path, &st, 0); - else + if (!lstat(p->path, &st)) { + if (add_to_cache(p->path, &st, 0)) + die("updating files failed"); + } else remove_file_from_cache(p->path); } } @@@ -247,7 -245,7 +247,7 @@@ static char *prepare_index(int argc, co */ if (all || (also && pathspec && *pathspec)) { int fd = hold_locked_index(&index_lock, 1); - add_files_to_cache(0, also ? prefix : NULL, pathspec); + add_files_to_cache(also ? prefix : NULL, pathspec, 0); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) @@@ -399,47 -397,6 +399,47 @@@ static int is_a_merge(const unsigned ch static const char sign_off_header[] = "Signed-off-by: "; +static void determine_author_info(void) +{ + char *name, *email, *date; + + name = getenv("GIT_AUTHOR_NAME"); + email = getenv("GIT_AUTHOR_EMAIL"); + date = getenv("GIT_AUTHOR_DATE"); + + if (use_message) { + const char *a, *lb, *rb, *eol; + + a = strstr(use_message_buffer, "\nauthor "); + if (!a) + die("invalid commit: %s", use_message); + + lb = strstr(a + 8, " <"); + rb = strstr(a + 8, "> "); + eol = strchr(a + 8, '\n'); + if (!lb || !rb || !eol) + die("invalid commit: %s", use_message); + + name = xstrndup(a + 8, lb - (a + 8)); + email = xstrndup(lb + 2, rb - (lb + 2)); + date = xstrndup(rb + 2, eol - (rb + 2)); + } + + if (force_author) { + const char *lb = strstr(force_author, " <"); + const char *rb = strchr(force_author, '>'); + + if (!lb || !rb) + die("malformed --author parameter"); + name = xstrndup(force_author, lb - force_author); + email = xstrndup(lb + 2, rb - (lb + 2)); + } + + author_name = name; + author_email = email; + author_date = date; +} + static int prepare_to_commit(const char *index_file, const char *prefix) { struct stat statbuf; @@@ -449,7 -406,6 +449,7 @@@ FILE *fp; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; + int ident_shown = 0; if (!no_verify && run_hook(index_file, "pre-commit", NULL)) return 0; @@@ -529,14 -485,7 +529,14 @@@ strbuf_release(&sb); + determine_author_info(); + + /* This checks if committer ident is explicitly given */ + git_committer_info(0); if (use_editor) { + char *author_ident; + const char *committer_ident; + if (in_merge) fprintf(fp, "#\n" @@@ -559,27 -508,6 +559,27 @@@ if (only_include_assumed) fprintf(fp, "# %s\n", only_include_assumed); + author_ident = xstrdup(fmt_name(author_name, author_email)); + committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"), + getenv("GIT_COMMITTER_EMAIL")); + if (strcmp(author_ident, committer_ident)) + fprintf(fp, + "%s" + "# Author: %s\n", + ident_shown++ ? "" : "#\n", + author_ident); + free(author_ident); + + if (!user_ident_explicitly_given) + fprintf(fp, + "%s" + "# Committer: %s\n", + ident_shown++ ? "" : "#\n", + committer_ident); + + if (ident_shown) + fprintf(fp, "#\n"); + saved_color_setting = wt_status_use_color; wt_status_use_color = 0; commitable = run_status(fp, index_file, prefix, 1); @@@ -696,6 -624,45 +696,6 @@@ static int message_is_empty(struct strb return 1; } -static void determine_author_info(struct strbuf *sb) -{ - char *name, *email, *date; - - name = getenv("GIT_AUTHOR_NAME"); - email = getenv("GIT_AUTHOR_EMAIL"); - date = getenv("GIT_AUTHOR_DATE"); - - if (use_message) { - const char *a, *lb, *rb, *eol; - - a = strstr(use_message_buffer, "\nauthor "); - if (!a) - die("invalid commit: %s", use_message); - - lb = strstr(a + 8, " <"); - rb = strstr(a + 8, "> "); - eol = strchr(a + 8, '\n'); - if (!lb || !rb || !eol) - die("invalid commit: %s", use_message); - - name = xstrndup(a + 8, lb - (a + 8)); - email = xstrndup(lb + 2, rb - (lb + 2)); - date = xstrndup(rb + 2, eol - (rb + 2)); - } - - if (force_author) { - const char *lb = strstr(force_author, " <"); - const char *rb = strchr(force_author, '>'); - - if (!lb || !rb) - die("malformed --author parameter"); - name = xstrndup(force_author, lb - force_author); - email = xstrndup(lb + 2, rb - (lb + 2)); - } - - strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME)); -} - static int parse_and_validate_options(int argc, const char *argv[], const char * const usage[]) { @@@ -806,7 -773,7 +806,7 @@@ int cmd_status(int argc, const char **a const char *index_file; int commitable; - git_config(git_status_config); + git_config(git_status_config, NULL); if (wt_status_use_color == -1) wt_status_use_color = git_use_color_default; @@@ -860,7 -827,7 +860,7 @@@ static void print_summary(const char *p } } - int git_commit_config(const char *k, const char *v) + int git_commit_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "commit.template")) { if (!v) @@@ -869,7 -836,7 +869,7 @@@ return 0; } - return git_status_config(k, v); + return git_status_config(k, v, cb); } static const char commit_utf8_warn[] = @@@ -897,7 -864,7 +897,7 @@@ int cmd_commit(int argc, const char **a unsigned char commit_sha1[20]; struct ref_lock *ref_lock; - git_config(git_commit_config); + git_config(git_commit_config, NULL); argc = parse_and_validate_options(argc, argv, builtin_commit_usage); @@@ -955,8 -922,7 +955,8 @@@ strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1)); } - determine_author_info(&sb); + strbuf_addf(&sb, "author %s\n", + fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME)); strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (!is_encoding_utf8(git_commit_encoding)) strbuf_addf(&sb, "encoding %s\n", git_commit_encoding); diff --combined builtin-gc.c index 48f7d95f97,e1a329917a..f5625bb9fb --- a/builtin-gc.c +++ b/builtin-gc.c @@@ -35,7 -35,7 +35,7 @@@ static const char *argv_repack[MAX_ADD static const char *argv_prune[] = {"prune", "--expire", NULL, NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; - static int gc_config(const char *var, const char *value) + static int gc_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "gc.packrefs")) { if (value && !strcmp(value, "notbare")) @@@ -67,7 -67,7 +67,7 @@@ prune_expire = xstrdup(value); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static void append_option(const char **cmd, const char *opt, int max_length) @@@ -219,14 -219,14 +219,14 @@@ int cmd_gc(int argc, const char **argv char buf[80]; struct option builtin_gc_options[] = { - OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"), + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"), OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"), OPT_END() }; - git_config(gc_config); + git_config(gc_config, NULL); if (pack_refs < 0) pack_refs = !is_bare_repository(); @@@ -249,14 -249,24 +249,14 @@@ /* * Auto-gc should be least intrusive as possible. */ - prune = 0; if (!need_to_gc()) return 0; fprintf(stderr, "Auto packing your repository for optimum " "performance. You may also\n" "run \"git gc\" manually. See " "\"git help gc\" for more information.\n"); - } else { - /* - * Use safer (for shared repos) "-A" option to - * repack when not pruning. Auto-gc makes its - * own decision. - */ - if (prune) - append_option(argv_repack, "-a", MAX_ADD); - else - append_option(argv_repack, "-A", MAX_ADD); - } + } else + append_option(argv_repack, "-A", MAX_ADD); if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) return error(FAILED_RUN, argv_pack_refs[0]); diff --combined builtin-init-db.c index 3968c9911f,f6aa353529..d8bdf928b1 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@@ -104,14 -104,12 +104,14 @@@ static void copy_templates_1(char *path } } -static void copy_templates(const char *git_dir, int len, const char *template_dir) +static void copy_templates(const char *template_dir) { char path[PATH_MAX]; char template_path[PATH_MAX]; int template_len; DIR *dir; + const char *git_dir = get_git_dir(); + int len = strlen(git_dir); if (!template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); @@@ -144,7 -142,7 +144,7 @@@ strcpy(template_path + template_len, "config"); repository_format_version = 0; git_config_from_file(check_repository_format_version, - template_path); + template_path, NULL); template_path[template_len] = 0; if (repository_format_version && @@@ -158,8 -156,6 +158,8 @@@ } memcpy(path, git_dir, len); + if (len && path[len - 1] != '/') + path[len++] = '/'; path[len] = 0; copy_templates_1(path, len, template_path, template_len, @@@ -167,9 -163,8 +167,9 @@@ closedir(dir); } -static int create_default_files(const char *git_dir, const char *template_path) +static int create_default_files(const char *template_path) { + const char *git_dir = get_git_dir(); unsigned len = strlen(git_dir); static char path[PATH_MAX]; struct stat st1; @@@ -188,27 -183,35 +188,27 @@@ /* * Create .git/refs/{heads,tags} */ - strcpy(path + len, "refs"); - safe_create_dir(path, 1); - strcpy(path + len, "refs/heads"); - safe_create_dir(path, 1); - strcpy(path + len, "refs/tags"); - safe_create_dir(path, 1); + safe_create_dir(git_path("refs"), 1); + safe_create_dir(git_path("refs/heads"), 1); + safe_create_dir(git_path("refs/tags"), 1); /* First copy the templates -- we might have the default * config file there, in which case we would want to read * from it after installing. */ - path[len] = 0; - copy_templates(path, len, template_path); + copy_templates(template_path); - git_config(git_default_config); + git_config(git_default_config, NULL); /* * We would have created the above under user's umask -- under * shared-repository settings, we would need to fix them up. */ if (shared_repository) { - path[len] = 0; - adjust_shared_perm(path); - strcpy(path + len, "refs"); - adjust_shared_perm(path); - strcpy(path + len, "refs/heads"); - adjust_shared_perm(path); - strcpy(path + len, "refs/tags"); - adjust_shared_perm(path); + adjust_shared_perm(get_git_dir()); + adjust_shared_perm(git_path("refs")); + adjust_shared_perm(git_path("refs/heads")); + adjust_shared_perm(git_path("refs/tags")); } /* @@@ -248,14 -251,12 +248,14 @@@ /* allow template config file to override the default */ if (log_all_ref_updates == -1) git_config_set("core.logallrefupdates", "true"); - if (work_tree != git_work_tree_cfg) + if (prefixcmp(git_dir, work_tree) || + strcmp(git_dir + strlen(work_tree), "/.git")) { git_config_set("core.worktree", work_tree); + } } - /* Check if symlink is supported in the work tree */ if (!reinit) { + /* Check if symlink is supported in the work tree */ path[len] = 0; strcpy(path + len, "tXXXXXX"); if (!close(xmkstemp(path)) && @@@ -266,101 -267,47 +266,101 @@@ unlink(path); /* good */ else git_config_set("core.symlinks", "false"); + + /* Check if the filesystem is case-insensitive */ + path[len] = 0; + strcpy(path + len, "CoNfIg"); + if (!access(path, F_OK)) + git_config_set("core.ignorecase", "true"); } return reinit; } -static void guess_repository_type(const char *git_dir) +int init_db(const char *template_dir, unsigned int flags) +{ + const char *sha1_dir; + char *path; + int len, reinit; + + safe_create_dir(get_git_dir(), 0); + + /* Check to see if the repository version is right. + * Note that a newly created repository does not have + * config file, so this will not fail. What we are catching + * is an attempt to reinitialize new repository with an old tool. + */ + check_repository_format(); + + reinit = create_default_files(template_dir); + + sha1_dir = get_object_directory(); + len = strlen(sha1_dir); + path = xmalloc(len + 40); + memcpy(path, sha1_dir, len); + + safe_create_dir(sha1_dir, 1); + strcpy(path+len, "/pack"); + safe_create_dir(path, 1); + strcpy(path+len, "/info"); + safe_create_dir(path, 1); + + if (shared_repository) { + char buf[10]; + /* We do not spell "group" and such, so that + * the configuration can be read by older version + * of git. Note, we use octal numbers for new share modes, + * and compatibility values for PERM_GROUP and + * PERM_EVERYBODY. + */ + if (shared_repository == PERM_GROUP) + sprintf(buf, "%d", OLD_PERM_GROUP); + else if (shared_repository == PERM_EVERYBODY) + sprintf(buf, "%d", OLD_PERM_EVERYBODY); + else + sprintf(buf, "0%o", shared_repository); + git_config_set("core.sharedrepository", buf); + git_config_set("receive.denyNonFastforwards", "true"); + } + + if (!(flags & INIT_DB_QUIET)) + printf("%s%s Git repository in %s/\n", + reinit ? "Reinitialized existing" : "Initialized empty", + shared_repository ? " shared" : "", + get_git_dir()); + + return 0; +} + +static int guess_repository_type(const char *git_dir) { char cwd[PATH_MAX]; const char *slash; - if (0 <= is_bare_repository_cfg) - return; - if (!git_dir) - return; - /* * "GIT_DIR=. git init" is always bare. * "GIT_DIR=`pwd` git init" too. */ if (!strcmp(".", git_dir)) - goto force_bare; + return 1; if (!getcwd(cwd, sizeof(cwd))) die("cannot tell cwd"); if (!strcmp(git_dir, cwd)) - goto force_bare; + return 1; /* * "GIT_DIR=.git or GIT_DIR=something/.git is usually not. */ if (!strcmp(git_dir, ".git")) - return; + return 0; slash = strrchr(git_dir, '/'); if (slash && !strcmp(slash, "/.git")) - return; + return 0; /* * Otherwise it is often bare. At this point * we are just guessing. */ - force_bare: - is_bare_repository_cfg = 1; - return; + return 1; } static const char init_db_usage[] = @@@ -375,9 -322,11 +375,9 @@@ int cmd_init_db(int argc, const char **argv, const char *prefix) { const char *git_dir; - const char *sha1_dir; const char *template_dir = NULL; - char *path; - int len, i, reinit; - int quiet = 0; + unsigned int flags = 0; + int i; for (i = 1; i < argc; i++, argv++) { const char *arg = argv[1]; @@@ -388,7 -337,7 +388,7 @@@ else if (!prefixcmp(arg, "--shared=")) shared_repository = git_config_perm("arg", arg+9); else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) - quiet = 1; + flags |= INIT_DB_QUIET; else usage(init_db_usage); } @@@ -405,35 -354,71 +405,35 @@@ GIT_WORK_TREE_ENVIRONMENT, GIT_DIR_ENVIRONMENT); - guess_repository_type(git_dir); - - if (is_bare_repository_cfg <= 0) { - git_work_tree_cfg = xcalloc(PATH_MAX, 1); - if (!getcwd(git_work_tree_cfg, PATH_MAX)) - die ("Cannot access current working directory."); - if (access(get_git_work_tree(), X_OK)) - die ("Cannot access work tree '%s'", - get_git_work_tree()); - } - /* * Set up the default .git directory contents */ - git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; - safe_create_dir(git_dir, 0); - - /* Check to see if the repository version is right. - * Note that a newly created repository does not have - * config file, so this will not fail. What we are catching - * is an attempt to reinitialize new repository with an old tool. - */ - check_repository_format(); - - reinit = create_default_files(git_dir, template_dir); - /* - * And set up the object store. - */ - sha1_dir = get_object_directory(); - len = strlen(sha1_dir); - path = xmalloc(len + 40); - memcpy(path, sha1_dir, len); - - safe_create_dir(sha1_dir, 1); - strcpy(path+len, "/pack"); - safe_create_dir(path, 1); - strcpy(path+len, "/info"); - safe_create_dir(path, 1); - - if (shared_repository) { - char buf[10]; - /* We do not spell "group" and such, so that - * the configuration can be read by older version - * of git. Note, we use octal numbers for new share modes, - * and compatibility values for PERM_GROUP and - * PERM_EVERYBODY. - */ - if (shared_repository == PERM_GROUP) - sprintf(buf, "%d", OLD_PERM_GROUP); - else if (shared_repository == PERM_EVERYBODY) - sprintf(buf, "%d", OLD_PERM_EVERYBODY); - else - sprintf(buf, "0%o", shared_repository); - git_config_set("core.sharedrepository", buf); - git_config_set("receive.denyNonFastforwards", "true"); + if (is_bare_repository_cfg < 0) + is_bare_repository_cfg = guess_repository_type(git_dir); + + if (!is_bare_repository_cfg) { + if (git_dir) { + const char *git_dir_parent = strrchr(git_dir, '/'); + if (git_dir_parent) { + char *rel = xstrndup(git_dir, git_dir_parent - git_dir); + git_work_tree_cfg = xstrdup(make_absolute_path(rel)); + free(rel); + } + } + if (!git_work_tree_cfg) { + git_work_tree_cfg = xcalloc(PATH_MAX, 1); + if (!getcwd(git_work_tree_cfg, PATH_MAX)) + die ("Cannot access current working directory."); + } + if (access(get_git_work_tree(), X_OK)) + die ("Cannot access work tree '%s'", + get_git_work_tree()); } - if (!quiet) - printf("%s%s Git repository in %s/\n", - reinit ? "Reinitialized existing" : "Initialized empty", - shared_repository ? " shared" : "", - git_dir); + set_git_dir(make_absolute_path(git_dir)); - return 0; + return init_db(template_dir, flags); } diff --combined builtin-log.c index 543855b7ad,addc7098cf..9817d6fbeb --- a/builtin-log.c +++ b/builtin-log.c @@@ -18,9 -18,6 +18,9 @@@ #include "run-command.h" #include "shortlog.h" +/* Set a default date-time format for git log ("log.date" config variable) */ +static const char *default_date_mode = NULL; + static int default_show_root = 1; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; @@@ -64,12 -61,7 +64,12 @@@ static void cmd_log_init(int argc, cons DIFF_OPT_SET(&rev->diffopt, RECURSIVE); rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; + + if (default_date_mode) + rev->date_mode = parse_date_format(default_date_mode); + argc = setup_revisions(argc, argv, rev, "HEAD"); + if (rev->diffopt.pickaxe || rev->diffopt.filter) rev->always_show_header = 0; if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { @@@ -230,7 -222,7 +230,7 @@@ static int cmd_log_walk(struct rev_inf return 0; } - static int git_log_config(const char *var, const char *value) + static int git_log_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "format.pretty")) return git_config_string(&fmt_pretty, var, value); @@@ -240,20 -232,18 +240,20 @@@ fmt_patch_subject_prefix = xstrdup(value); return 0; } + if (!strcmp(var, "log.date")) + return git_config_string(&default_date_mode, var, value); if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); return 0; } - return git_diff_ui_config(var, value); + return git_diff_ui_config(var, value, cb); } int cmd_whatchanged(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@@ -329,7 -319,7 +329,7 @@@ int cmd_show(int argc, const char **arg struct object_array_entry *objects; int i, count, ret = 0; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@@ -393,7 -383,7 +393,7 @@@ int cmd_log_reflog(int argc, const cha { struct rev_info rev; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@@ -426,7 -416,7 +426,7 @@@ int cmd_log(int argc, const char **argv { struct rev_info rev; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@@ -481,7 -471,7 +481,7 @@@ static void add_header(const char *valu extra_hdr[extra_hdr_nr++] = xstrndup(value, len); } - static int git_format_config(const char *var, const char *value) + static int git_format_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "format.headers")) { if (!value) @@@ -495,13 -485,6 +495,13 @@@ fmt_patch_suffix = xstrdup(value); return 0; } + if (!strcmp(var, "format.cc")) { + if (!value) + return config_error_nonbool(var); + ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); + extra_cc[extra_cc_nr++] = xstrdup(value); + return 0; + } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { return 0; } @@@ -514,7 -497,7 +514,7 @@@ return 0; } - return git_log_config(var, value); + return git_log_config(var, value, cb); } @@@ -781,7 -764,7 +781,7 @@@ int cmd_format_patch(int argc, const ch char *add_signoff = NULL; struct strbuf buf; - git_config(git_format_config); + git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; diff --combined builtin-mailinfo.c index e1e094f29e,6e23ffd8af..97c1ff9744 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@@ -434,7 -434,6 +434,7 @@@ static int read_one_header_line(char *l static int decode_q_segment(char *in, char *ot, unsigned otsize, char *ep, int rfc2047) { + char *otbegin = ot; char *otend = ot + otsize; int c; while ((c = *in++) != 0 && (in <= ep)) { @@@ -454,14 -453,13 +454,14 @@@ *ot++ = c; } *ot = 0; - return 0; + return (ot - otbegin); } static int decode_b_segment(char *in, char *ot, unsigned otsize, char *ep) { /* Decode in..ep, possibly in-place to ot */ int c, pos = 0, acc = 0; + char *otbegin = ot; char *otend = ot + otsize; while ((c = *in++) != 0 && (in <= ep)) { @@@ -507,7 -505,7 +507,7 @@@ } } *ot = 0; - return 0; + return (ot - otbegin); } /* @@@ -625,24 -623,25 +625,24 @@@ static void decode_header(char *it, uns convert_to_utf8(it, itsize, ""); } -static void decode_transfer_encoding(char *line, unsigned linesize) +static int decode_transfer_encoding(char *line, unsigned linesize, int inputlen) { char *ep; switch (transfer_encoding) { case TE_QP: - ep = line + strlen(line); - decode_q_segment(line, line, linesize, ep, 0); - break; + ep = line + inputlen; + return decode_q_segment(line, line, linesize, ep, 0); case TE_BASE64: - ep = line + strlen(line); - decode_b_segment(line, line, linesize, ep); - break; + ep = line + inputlen; + return decode_b_segment(line, line, linesize, ep); case TE_DONTCARE: - break; + default: + return inputlen; } } -static int handle_filter(char *line, unsigned linesize); +static int handle_filter(char *line, unsigned linesize, int linelen); static int find_boundary(void) { @@@ -670,7 -669,7 +670,7 @@@ again "can't recover\n"); exit(1); } - handle_filter(newline, sizeof(newline)); + handle_filter(newline, sizeof(newline), strlen(newline)); /* skip to the next boundary */ if (!find_boundary()) @@@ -760,14 -759,14 +760,14 @@@ static int handle_commit_msg(char *line return 0; } -static int handle_patch(char *line) +static int handle_patch(char *line, int len) { - fputs(line, patchfile); + fwrite(line, 1, len, patchfile); patch_lines++; return 0; } -static int handle_filter(char *line, unsigned linesize) +static int handle_filter(char *line, unsigned linesize, int linelen) { static int filter = 0; @@@ -780,7 -779,7 +780,7 @@@ break; filter++; case 1: - if (!handle_patch(line)) + if (!handle_patch(line, linelen)) break; filter++; default: @@@ -795,7 -794,6 +795,7 @@@ static void handle_body(void int rc = 0; static char newline[2000]; static char *np = newline; + int len = strlen(line); /* Skip up to the first boundary */ if (content_top->boundary) { @@@ -807,19 -805,16 +807,19 @@@ /* process any boundary lines */ if (content_top->boundary && is_multipart_boundary(line)) { /* flush any leftover */ - if ((transfer_encoding == TE_BASE64) && - (np != newline)) { - handle_filter(newline, sizeof(newline)); - } + if (np != newline) + handle_filter(newline, sizeof(newline), + np - newline); if (!handle_boundary()) return; } /* Unwrap transfer encoding */ - decode_transfer_encoding(line, sizeof(line)); + len = decode_transfer_encoding(line, sizeof(line), len); + if (len < 0) { + error("Malformed input line"); + return; + } switch (transfer_encoding) { case TE_BASE64: @@@ -829,40 -824,39 +829,40 @@@ /* binary data most likely doesn't have newlines */ if (message_type != TYPE_TEXT) { - rc = handle_filter(line, sizeof(newline)); + rc = handle_filter(line, sizeof(line), len); break; } - /* this is a decoded line that may contain + /* + * This is a decoded line that may contain * multiple new lines. Pass only one chunk * at a time to handle_filter() */ - do { - while (*op != '\n' && *op != 0) + while (op < line + len && *op != '\n') *np++ = *op++; *np = *op; if (*np != 0) { /* should be sitting on a new line */ *(++np) = 0; op++; - rc = handle_filter(newline, sizeof(newline)); + rc = handle_filter(newline, sizeof(newline), np - newline); np = newline; } - } while (*op != 0); - /* the partial chunk is saved in newline and - * will be appended by the next iteration of fgets + } while (op < line + len); + /* + * The partial chunk is saved in newline and will be + * appended by the next iteration of read_line_with_nul(). */ break; } default: - rc = handle_filter(line, sizeof(newline)); + rc = handle_filter(line, sizeof(line), len); } if (rc) /* nothing left to filter */ break; - } while (fgets(line, sizeof(line), fin)); + } while ((len = read_line_with_nul(line, sizeof(line), fin))); return; } @@@ -968,7 -962,7 +968,7 @@@ int cmd_mailinfo(int argc, const char * /* NEEDSWORK: might want to do the optional .git/ directory * discovery */ - git_config(git_default_config); + git_config(git_default_config, NULL); def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8"); metainfo_charset = def_charset; diff --combined builtin-merge-recursive.c index 46e636fdcf,e16f5e2a4e..362c290028 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@@ -92,8 -92,7 +92,8 @@@ static struct path_list current_directo static int call_depth = 0; static int verbosity = 2; -static int rename_limit = -1; +static int diff_rename_limit = -1; +static int merge_rename_limit = -1; static int buffer_output = 1; static struct strbuf obuf = STRBUF_INIT; @@@ -362,10 -361,7 +362,10 @@@ static struct path_list *get_renames(st diff_setup(&opts); DIFF_OPT_SET(&opts, RECURSIVE); opts.detect_rename = DIFF_DETECT_RENAME; - opts.rename_limit = rename_limit; + opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit : + diff_rename_limit >= 0 ? diff_rename_limit : + 500; + opts.warn_on_too_large_rename = 1; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); @@@ -1340,21 -1336,17 +1340,21 @@@ static struct commit *get_ref(const cha return (struct commit *)object; } - static int merge_config(const char *var, const char *value) + static int merge_config(const char *var, const char *value, void *cb) { if (!strcasecmp(var, "merge.verbosity")) { verbosity = git_config_int(var, value); return 0; } if (!strcasecmp(var, "diff.renamelimit")) { - rename_limit = git_config_int(var, value); + diff_rename_limit = git_config_int(var, value); + return 0; + } + if (!strcasecmp(var, "merge.renamelimit")) { + merge_rename_limit = git_config_int(var, value); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } int cmd_merge_recursive(int argc, const char **argv, const char *prefix) @@@ -1375,7 -1367,7 +1375,7 @@@ subtree_merge = 1; } - git_config(merge_config); + git_config(merge_config, NULL); if (getenv("GIT_MERGE_VERBOSITY")) verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); diff --combined builtin-mv.c index fb906b3fc7,3edebef45f..5530e11b89 --- a/builtin-mv.c +++ b/builtin-mv.c @@@ -81,7 -81,7 +81,7 @@@ int cmd_mv(int argc, const char **argv struct path_list deleted = {NULL, 0, 0, 0}; struct path_list changed = {NULL, 0, 0, 0}; - git_config(git_default_config); + git_config(git_default_config, NULL); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) @@@ -256,8 -256,7 +256,8 @@@ for (i = 0; i < added.nr; i++) { const char *path = added.items[i].path; - add_file_to_cache(path, verbose); + if (add_file_to_cache(path, verbose ? ADD_CACHE_VERBOSE : 0)) + die("updating index entries failed"); } for (i = 0; i < deleted.nr; i++) diff --combined builtin-pack-objects.c index f43eb67016,a8dc225a72..70d2f5d416 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@@ -28,8 -28,7 +28,8 @@@ git-pack-objects [{ -q | --progress | - [--window=N] [--window-memory=N] [--depth=N] \n\ [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\ [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\ - [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\ + [--stdout | base-name] [--include-tag] \n\ + [--keep-unreachable | --unpack-unreachable] \n\ [delta->idx.sha1, &type, &othersize); - void *delta_buf; - if (!otherbuf) + buf = read_sha1_file(entry->idx.sha1, &type, &size); + if (!buf) + die("unable to read %s", sha1_to_hex(entry->idx.sha1)); + base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size); + if (!base_buf) die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1)); - delta_buf = diff_delta(otherbuf, othersize, + delta_buf = diff_delta(base_buf, base_size, buf, size, &delta_size, 0); - if (!delta_buf || delta_size != entry->delta_size) + if (!delta_buf || delta_size != entry->delta_size) die("delta size changed"); - free(buf); - free(otherbuf); + free(buf); + free(base_buf); return delta_buf; } +static unsigned long do_compress(void **pptr, unsigned long size) +{ + z_stream stream; + void *in, *out; + unsigned long maxsize; + + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, pack_compression_level); + maxsize = deflateBound(&stream, size); + + in = *pptr; + out = xmalloc(maxsize); + *pptr = out; + + stream.next_in = in; + stream.avail_in = size; + stream.next_out = out; + stream.avail_out = maxsize; + while (deflate(&stream, Z_FINISH) == Z_OK) + ; /* nothing */ + deflateEnd(&stream); + + free(in); + return stream.total_out; +} + /* * The per-object header is a pretty dense thing, which is * - first byte: low four bits are "size", then three bits of "type", @@@ -254,42 -222,42 +254,42 @@@ static unsigned long write_object(struc struct object_entry *entry, off_t write_offset) { - unsigned long size; - enum object_type type; + unsigned long size, limit, datalen; void *buf; - unsigned char header[10]; - unsigned char dheader[10]; + unsigned char header[10], dheader[10]; unsigned hdrlen; - off_t datalen; - enum object_type obj_type; - int to_reuse = 0; - /* write limit if limited packsize and not first object */ - unsigned long limit = pack_size_limit && nr_written ? - pack_size_limit - write_offset : 0; - /* no if no delta */ - int usable_delta = !entry->delta ? 0 : - /* yes if unlimited packfile */ - !pack_size_limit ? 1 : - /* no if base written to previous pack */ - entry->delta->idx.offset == (off_t)-1 ? 0 : - /* otherwise double-check written to this - * pack, like we do below - */ - entry->delta->idx.offset ? 1 : 0; + enum object_type type; + int usable_delta, to_reuse; if (!pack_to_stdout) crc32_begin(f); - obj_type = entry->type; - if (no_reuse_object) + type = entry->type; + + /* write limit if limited packsize and not first object */ + limit = pack_size_limit && nr_written ? + pack_size_limit - write_offset : 0; + + if (!entry->delta) + usable_delta = 0; /* no delta */ + else if (!pack_size_limit) + usable_delta = 1; /* unlimited packfile */ + else if (entry->delta->idx.offset == (off_t)-1) + usable_delta = 0; /* base was written to another pack */ + else if (entry->delta->idx.offset) + usable_delta = 1; /* base already exists in this pack */ + else + usable_delta = 0; /* base could end up in another pack */ + + if (!reuse_object) to_reuse = 0; /* explicit */ else if (!entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ - else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA) + else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) /* check_object() decided it for us ... */ to_reuse = usable_delta; /* ... but pack split may override that */ - else if (obj_type != entry->in_pack_type) + else if (type != entry->in_pack_type) to_reuse = 0; /* pack has delta which is unusable */ else if (entry->delta) to_reuse = 0; /* we want to pack afresh */ @@@ -299,42 -267,50 +299,42 @@@ */ if (!to_reuse) { - z_stream stream; - unsigned long maxsize; - void *out; if (!usable_delta) { - buf = read_sha1_file(entry->idx.sha1, &obj_type, &size); + buf = read_sha1_file(entry->idx.sha1, &type, &size); if (!buf) die("unable to read %s", sha1_to_hex(entry->idx.sha1)); + /* + * make sure no cached delta data remains from a + * previous attempt before a pack split occured. + */ + free(entry->delta_data); + entry->delta_data = NULL; + entry->z_delta_size = 0; } else if (entry->delta_data) { size = entry->delta_size; buf = entry->delta_data; entry->delta_data = NULL; - obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } else { - buf = read_sha1_file(entry->idx.sha1, &type, &size); - if (!buf) - die("unable to read %s", sha1_to_hex(entry->idx.sha1)); - buf = delta_against(buf, size, entry); + buf = get_delta(entry); size = entry->delta_size; - obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } - /* compress the data to store and put compressed length in datalen */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, pack_compression_level); - maxsize = deflateBound(&stream, size); - out = xmalloc(maxsize); - /* Compress it */ - stream.next_in = buf; - stream.avail_in = size; - stream.next_out = out; - stream.avail_out = maxsize; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - datalen = stream.total_out; + + if (entry->z_delta_size) + datalen = entry->z_delta_size; + else + datalen = do_compress(&buf, size); /* * The object header is a byte of 'type' followed by zero or * more bytes of length. */ - hdrlen = encode_header(obj_type, size, header); + hdrlen = encode_header(type, size, header); - if (obj_type == OBJ_OFS_DELTA) { + if (type == OBJ_OFS_DELTA) { /* * Deltas with relative base contain an additional * encoding of the relative offset for the delta @@@ -346,18 -322,20 +346,18 @@@ while (ofs >>= 7) dheader[--pos] = 128 | (--ofs & 127); if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { - free(out); free(buf); return 0; } sha1write(f, header, hdrlen); sha1write(f, dheader + pos, sizeof(dheader) - pos); hdrlen += sizeof(dheader) - pos; - } else if (obj_type == OBJ_REF_DELTA) { + } else if (type == OBJ_REF_DELTA) { /* * Deltas with a base reference contain * an additional 20 bytes for the base sha1. */ if (limit && hdrlen + 20 + datalen + 20 >= limit) { - free(out); free(buf); return 0; } @@@ -366,12 -344,14 +366,12 @@@ hdrlen += 20; } else { if (limit && hdrlen + datalen + 20 >= limit) { - free(out); free(buf); return 0; } sha1write(f, header, hdrlen); } - sha1write(f, out, datalen); - free(out); + sha1write(f, buf, datalen); free(buf); } else { @@@ -381,11 -361,11 +381,11 @@@ off_t offset; if (entry->delta) { - obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; reused_delta++; } - hdrlen = encode_header(obj_type, entry->size, header); + hdrlen = encode_header(type, entry->size, header); offset = entry->in_pack_offset; revidx = find_pack_revindex(p, offset); datalen = revidx[1].offset - offset; @@@ -394,7 -374,7 +394,7 @@@ die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1)); offset += entry->in_pack_header_size; datalen -= entry->in_pack_header_size; - if (obj_type == OBJ_OFS_DELTA) { + if (type == OBJ_OFS_DELTA) { off_t ofs = entry->idx.offset - entry->delta->idx.offset; unsigned pos = sizeof(dheader) - 1; dheader[pos] = ofs & 127; @@@ -405,7 -385,7 +405,7 @@@ sha1write(f, header, hdrlen); sha1write(f, dheader + pos, sizeof(dheader) - pos); hdrlen += sizeof(dheader) - pos; - } else if (obj_type == OBJ_REF_DELTA) { + } else if (type == OBJ_REF_DELTA) { if (limit && hdrlen + 20 + datalen + 20 >= limit) return 0; sha1write(f, header, hdrlen); @@@ -472,10 -452,11 +472,10 @@@ static void write_pack_file(void struct sha1file *f; off_t offset, offset_one, last_obj_offset = 0; struct pack_header hdr; - int do_progress = progress >> pack_to_stdout; uint32_t nr_remaining = nr_result; time_t last_mtime = 0; - if (do_progress) + if (progress > pack_to_stdout) progress_state = start_progress("Writing objects", nr_result); written_list = xmalloc(nr_objects * sizeof(*written_list)); @@@ -1041,7 -1022,7 +1041,7 @@@ static void check_object(struct object_ unuse_pack(&w_curs); return; case OBJ_REF_DELTA: - if (!no_reuse_delta && !entry->preferred_base) + if (reuse_delta && !entry->preferred_base) base_ref = use_pack(p, &w_curs, entry->in_pack_offset + used, NULL); entry->in_pack_header_size = used + 20; @@@ -1064,7 -1045,7 +1064,7 @@@ die("delta base offset out of bound for %s", sha1_to_hex(entry->idx.sha1)); ofs = entry->in_pack_offset - ofs; - if (!no_reuse_delta && !entry->preferred_base) { + if (reuse_delta && !entry->preferred_base) { struct revindex_entry *revidx; revidx = find_pack_revindex(p, ofs); base_ref = nth_packed_object_sha1(p, revidx->nr); @@@ -1252,7 -1233,7 +1252,7 @@@ static int try_delta(struct unpacked *t * We do not bother to try a delta that we discarded * on an earlier try, but only when reusing delta data. */ - if (!no_reuse_delta && trg_entry->in_pack && + if (reuse_delta && trg_entry->in_pack && trg_entry->in_pack == src_entry->in_pack && trg_entry->in_pack_type != OBJ_REF_DELTA && trg_entry->in_pack_type != OBJ_OFS_DELTA) @@@ -1460,34 -1441,11 +1460,34 @@@ static void find_deltas(struct object_e best_base = other_idx; } + /* + * If we decided to cache the delta data, then it is best + * to compress it right away. First because we have to do + * it anyway, and doing it here while we're threaded will + * save a lot of time in the non threaded write phase, + * as well as allow for caching more deltas within + * the same cache size limit. + * ... + * But only if not writing to stdout, since in that case + * the network is most likely throttling writes anyway, + * and therefore it is best to go to the write phase ASAP + * instead, as we can afford spending more time compressing + * between writes at that moment. + */ + if (entry->delta_data && !pack_to_stdout) { + entry->z_delta_size = do_compress(&entry->delta_data, + entry->delta_size); + cache_lock(); + delta_cache_size -= entry->delta_size; + delta_cache_size += entry->z_delta_size; + cache_unlock(); + } + /* if we made n a delta, and if n is already at max * depth, leaving it in the window is pointless. we * should evict it first. */ - if (entry->delta && depth <= n->depth) + if (entry->delta && max_depth <= n->depth) continue; /* @@@ -1730,7 -1688,7 +1730,7 @@@ static void prepare_pack(int window, in if (entry->delta) /* This happens if we decided to reuse existing - * delta from a pack. "!no_reuse_delta &&" is implied. + * delta from a pack. "reuse_delta &&" is implied. */ continue; @@@ -1760,7 -1718,7 +1760,7 @@@ free(delta_list); } - static int git_pack_config(const char *k, const char *v) + static int git_pack_config(const char *k, const char *v, void *cb) { if(!strcmp(k, "pack.window")) { window = git_config_int(k, v); @@@ -1813,7 -1771,7 +1813,7 @@@ pack_size_limit_cfg = git_config_ulong(k, v); return 0; } - return git_default_config(k, v); + return git_default_config(k, v, cb); } static void read_object_list_from_stdin(void) @@@ -1947,32 -1905,6 +1947,32 @@@ static void add_objects_in_unpacked_pac free(in_pack.array); } +static void loosen_unused_packed_objects(struct rev_info *revs) +{ + struct packed_git *p; + uint32_t i; + const unsigned char *sha1; + + for (p = packed_git; p; p = p->next) { + for (i = 0; i < revs->num_ignore_packed; i++) { + if (matches_pack_name(p, revs->ignore_packed[i])) + break; + } + if (revs->num_ignore_packed <= i) + continue; + + if (open_pack_index(p)) + die("cannot open pack index"); + + for (i = 0; i < p->num_objects; i++) { + sha1 = nth_packed_object_sha1(p, i); + if (!locate_object_entry(sha1)) + if (force_object_loose(sha1, p->mtime)) + die("unable to force loose object"); + } + } +} + static void get_object_list(int ac, const char **av) { struct rev_info revs; @@@ -2007,8 -1939,6 +2007,8 @@@ if (keep_unreachable) add_objects_in_unpacked_packs(&revs); + if (unpack_unreachable) + loosen_unused_packed_objects(&revs); } static int adjust_perm(const char *path, mode_t mode) @@@ -2033,7 -1963,7 +2033,7 @@@ int cmd_pack_objects(int argc, const ch rp_av[1] = "--objects"; /* --thin will make it --objects-edge */ rp_ac = 2; - git_config(git_pack_config); + git_config(git_pack_config, NULL); if (!pack_compression_seen && core_compression_seen) pack_compression_level = core_compression_level; @@@ -2120,11 -2050,11 +2120,11 @@@ continue; } if (!strcmp("--no-reuse-delta", arg)) { - no_reuse_delta = 1; + reuse_delta = 0; continue; } if (!strcmp("--no-reuse-object", arg)) { - no_reuse_object = no_reuse_delta = 1; + reuse_object = reuse_delta = 0; continue; } if (!strcmp("--delta-base-offset", arg)) { @@@ -2143,10 -2073,6 +2143,10 @@@ keep_unreachable = 1; continue; } + if (!strcmp("--unpack-unreachable", arg)) { + unpack_unreachable = 1; + continue; + } if (!strcmp("--include-tag", arg)) { include_tag = 1; continue; @@@ -2212,9 -2138,6 +2212,9 @@@ if (!pack_to_stdout && thin) die("--thin cannot be used to build an indexable pack."); + if (keep_unreachable && unpack_unreachable) + die("--keep-unreachable and --unpack-unreachable are incompatible."); + #ifdef THREADED_DELTA_SEARCH if (!delta_search_threads) /* --threads=0 means autodetect */ delta_search_threads = online_cpus(); diff --combined builtin-rev-list.c index b474527402,274299044e..83a7b1349e --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@@ -10,7 -10,6 +10,7 @@@ #include "list-objects.h" #include "builtin.h" #include "log-tree.h" +#include "graph.h" /* bits #0-15 in revision.h */ @@@ -59,31 -58,26 +59,31 @@@ static const char *header_prefix static void finish_commit(struct commit *commit); static void show_commit(struct commit *commit) { + graph_show_commit(revs.graph); + if (show_timestamp) printf("%lu ", commit->date); if (header_prefix) fputs(header_prefix, stdout); - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & UNINTERESTING) - putchar('^'); - else if (revs.left_right) { - if (commit->object.flags & SYMMETRIC_LEFT) - putchar('<'); - else - putchar('>'); + + if (!revs.graph) { + if (commit->object.flags & BOUNDARY) + putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); + else if (revs.left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) + putchar('<'); + else + putchar('>'); + } } if (revs.abbrev_commit && revs.abbrev) fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev), stdout); else fputs(sha1_to_hex(commit->object.sha1), stdout); - if (revs.parents) { + if (revs.print_parents) { struct commit_list *parents = commit->parents; while (parents) { printf(" %s", sha1_to_hex(parents->item->object.sha1)); @@@ -102,48 -96,9 +102,48 @@@ pretty_print_commit(revs.commit_format, commit, &buf, revs.abbrev, NULL, NULL, revs.date_mode, 0); - if (buf.len) - printf("%s%c", buf.buf, hdr_termination); + if (revs.graph) { + if (buf.len) { + if (revs.commit_format != CMIT_FMT_ONELINE) + graph_show_oneline(revs.graph); + + graph_show_commit_msg(revs.graph, &buf); + + /* + * Add a newline after the commit message. + * + * Usually, this newline produces a blank + * padding line between entries, in which case + * we need to add graph padding on this line. + * + * However, the commit message may not end in a + * newline. In this case the newline simply + * ends the last line of the commit message, + * and we don't need any graph output. (This + * always happens with CMIT_FMT_ONELINE, and it + * happens with CMIT_FMT_USERFORMAT when the + * format doesn't explicitly end in a newline.) + */ + if (buf.len && buf.buf[buf.len - 1] == '\n') + graph_show_padding(revs.graph); + putchar('\n'); + } else { + /* + * If the message buffer is empty, just show + * the rest of the graph output for this + * commit. + */ + if (graph_show_remainder(revs.graph)) + putchar('\n'); + } + } else { + if (buf.len) + printf("%s%c", buf.buf, hdr_termination); + } strbuf_release(&buf); + } else { + if (graph_show_remainder(revs.graph)) + putchar('\n'); } maybe_flush_or_die(stdout, "stdout"); finish_commit(commit); @@@ -591,7 -546,7 +591,7 @@@ int cmd_rev_list(int argc, const char * int bisect_find_all = 0; int quiet = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = 0; revs.commit_format = CMIT_FMT_UNSPECIFIED; diff --combined builtin-rev-parse.c index ab3e85054e,1d019b3c16..a7860ed75a --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@@ -94,14 -94,6 +94,14 @@@ static void show(const char *arg puts(arg); } +/* Like show(), but with a negation prefix according to type */ +static void show_with_type(int type, const char *arg) +{ + if (type != show_type) + putchar('^'); + show(arg); +} + /* Output a revision, only if filter allows it */ static void show_rev(int type, const unsigned char *sha1, const char *name) { @@@ -109,6 -101,8 +109,6 @@@ return; def = NULL; - if (type != show_type) - putchar('^'); if (symbolic && name) { if (symbolic == SHOW_SYMBOLIC_FULL) { unsigned char discard[20]; @@@ -125,20 -119,20 +125,20 @@@ */ break; case 1: /* happy */ - show(full); + show_with_type(type, full); break; default: /* ambiguous */ error("refname '%s' is ambiguous", name); break; } } else { - show(name); + show_with_type(type, name); } } else if (abbrev) - show(find_unique_abbrev(sha1, abbrev)); + show_with_type(type, find_unique_abbrev(sha1, abbrev)); else - show(sha1_to_hex(sha1)); + show_with_type(type, sha1_to_hex(sha1)); } /* Output a flag, only if filter allows it. */ @@@ -387,7 -381,7 +387,7 @@@ int cmd_rev_parse(int argc, const char return cmd_parseopt(argc - 1, argv + 1, prefix); prefix = setup_git_directory(); - git_config(git_default_config); + git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --combined builtin-update-index.c index d4c85c0cbc,e1ca8deac2..9e0d7ab11e --- a/builtin-update-index.c +++ b/builtin-update-index.c @@@ -567,7 -567,7 +567,7 @@@ int cmd_update_index(int argc, const ch int lock_error = 0; struct lock_file *lock_file; - git_config(git_default_config); + git_config(git_default_config, NULL); /* We can't free this memory, it becomes part of a linked list parsed atexit() */ lock_file = xcalloc(1, sizeof(struct lock_file)); @@@ -593,10 -593,6 +593,10 @@@ refresh_flags |= REFRESH_QUIET; continue; } + if (!strcmp(path, "--ignore-submodules")) { + refresh_flags |= REFRESH_IGNORE_SUBMODULES; + continue; + } if (!strcmp(path, "--add")) { allow_add = 1; continue; diff --combined cache.h index 943bee9e7d,ebe4031b65..eab1a172fe --- a/cache.h +++ b/cache.h @@@ -261,8 -261,8 +261,8 @@@ static inline void remove_name_hash(str #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option)) #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) -#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose)) -#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags)) +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags)) #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) @@@ -317,7 -317,6 +317,7 @@@ extern char *get_graft_file(void) extern int set_git_dir(const char *path); extern const char *get_git_work_tree(void); extern const char *read_gitfile_gently(const char *path); +extern void set_git_work_tree(const char *tree); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" @@@ -330,10 -329,6 +330,10 @@@ extern const char *prefix_filename(cons extern void verify_filename(const char *prefix, const char *name); extern void verify_non_filename(const char *prefix, const char *name); +#define INIT_DB_QUIET 0x0001 + +extern int init_db(const char *template_dir, unsigned int flags); + #define alloc_nr(x) (((x)+16)*3/2) /* @@@ -371,11 -366,8 +371,11 @@@ extern int add_index_entry(struct index extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern int remove_index_entry_at(struct index_state *, int pos); extern int remove_file_from_index(struct index_state *, const char *path); -extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose); -extern int add_file_to_index(struct index_state *, const char *path, int verbose); +#define ADD_CACHE_VERBOSE 1 +#define ADD_CACHE_PRETEND 2 +#define ADD_CACHE_IGNORE_ERRORS 4 +extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); +extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); @@@ -396,7 -388,6 +396,7 @@@ extern void fill_stat_cache_info(struc #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ +#define REFRESH_IGNORE_SUBMODULES 0x0008 /* ignore submodules */ extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen); struct lock_file { @@@ -407,7 -398,6 +407,7 @@@ char filename[PATH_MAX]; }; extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); +extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); extern int hold_locked_index(struct lock_file *, int); @@@ -531,7 -521,6 +531,7 @@@ extern void * read_sha1_file(const unsi extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1); extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1); extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); +extern int force_object_loose(const unsigned char *sha1, time_t mtime); extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type); @@@ -625,7 -614,6 +625,7 @@@ extern struct alternate_object_databas char base[FLEX_ARRAY]; /* more */ } *alt_odb_list; extern void prepare_alt_odb(void); +extern void add_to_alternates_file(const char *reference); struct pack_window { struct pack_window *next; @@@ -723,10 -711,10 +723,10 @@@ extern int matches_pack_name(struct pac /* Dumb servers support */ extern int update_server_info(int); - typedef int (*config_fn_t)(const char *, const char *); - extern int git_default_config(const char *, const char *); - extern int git_config_from_file(config_fn_t fn, const char *); - extern int git_config(config_fn_t fn); + typedef int (*config_fn_t)(const char *, const char *, void *); + extern int git_default_config(const char *, const char *, void *); + extern int git_config_from_file(config_fn_t fn, const char *, void *); + extern int git_config(config_fn_t fn, void *); extern int git_parse_long(const char *, long *); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); @@@ -738,7 -726,7 +738,7 @@@ extern int git_config_set(const char * extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); extern const char *git_etc_gitconfig(void); - extern int check_repository_format_version(const char *var, const char *value); + extern int check_repository_format_version(const char *var, const char *value, void *cb); extern int git_env_bool(const char *, int); extern int git_config_system(void); extern int git_config_global(void); @@@ -747,7 -735,6 +747,7 @@@ extern int config_error_nonbool(const c #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; extern char git_default_name[MAX_GITNAME]; +extern int user_ident_explicitly_given; extern const char *git_commit_encoding; extern const char *git_log_output_encoding; @@@ -794,11 -781,7 +794,11 @@@ extern int convert_to_git(const char *p extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* add */ -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec); +/* + * return 0 if success, 1 - if addition of a file failed and + * ADD_FILES_IGNORE_ERRORS was specified in flags + */ +int add_files_to_cache(const char *prefix, const char **pathspec, int flags); /* diff.c */ extern int diff_auto_refresh_index; diff --combined config.c index 5195de10c9,5431e2dc01..c2f2bbb000 --- a/config.c +++ b/config.c @@@ -111,7 -111,7 +111,7 @@@ static inline int iskeychar(int c return isalnum(c) || c == '-'; } - static int get_value(config_fn_t fn, char *name, unsigned int len) + static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) { int c; char *value; @@@ -139,7 -139,7 +139,7 @@@ if (!value) return -1; } - return fn(name, value); + return fn(name, value, data); } static int get_extended_base_var(char *name, int baselen, int c) @@@ -197,7 -197,7 +197,7 @@@ static int get_base_var(char *name } } - static int git_parse_file(config_fn_t fn) + static int git_parse_file(config_fn_t fn, void *data) { int comment = 0; int baselen = 0; @@@ -228,7 -228,7 +228,7 @@@ if (!isalpha(c)) break; var[baselen] = tolower(c); - if (get_value(fn, var, baselen+1) < 0) + if (get_value(fn, data, var, baselen+1) < 0) break; } die("bad config file line %d in %s", config_linenr, config_file_name); @@@ -332,7 -332,7 +332,7 @@@ int git_config_string(const char **dest return 0; } - int git_default_config(const char *var, const char *value) + int git_default_config(const char *var, const char *value, void *dummy) { /* This needs a better name */ if (!strcmp(var, "core.filemode")) { @@@ -448,8 -448,6 +448,8 @@@ if (!value) return config_error_nonbool(var); strlcpy(git_default_name, value, sizeof(git_default_name)); + if (git_default_email[0]) + user_ident_explicitly_given = 1; return 0; } @@@ -457,8 -455,6 +457,8 @@@ if (!value) return config_error_nonbool(var); strlcpy(git_default_email, value, sizeof(git_default_email)); + if (git_default_name[0]) + user_ident_explicitly_given = 1; return 0; } @@@ -516,7 -512,7 +516,7 @@@ return 0; } - int git_config_from_file(config_fn_t fn, const char *filename) + int git_config_from_file(config_fn_t fn, const char *filename, void *data) { int ret; FILE *f = fopen(filename, "r"); @@@ -527,7 -523,7 +527,7 @@@ config_file_name = filename; config_linenr = 1; config_file_eof = 0; - ret = git_parse_file(fn); + ret = git_parse_file(fn, data); fclose(f); config_file_name = NULL; } @@@ -565,7 -561,7 +565,7 @@@ int git_config_global(void return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); } - int git_config(config_fn_t fn) + int git_config(config_fn_t fn, void *data) { int ret = 0; char *repo_config = NULL; @@@ -578,7 -574,8 +578,8 @@@ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) - ret += git_config_from_file(fn, git_etc_gitconfig()); + ret += git_config_from_file(fn, git_etc_gitconfig(), + data); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!filename) @@@ -588,11 -585,11 +589,11 @@@ if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) - ret = git_config_from_file(fn, user_config); + ret = git_config_from_file(fn, user_config, data); free(user_config); } - ret += git_config_from_file(fn, filename); + ret += git_config_from_file(fn, filename, data); free(repo_config); return ret; } @@@ -622,7 -619,7 +623,7 @@@ static int matches(const char* key, con !regexec(store.value_regex, value, 0, NULL, 0))); } - static int store_aux(const char* key, const char* value) + static int store_aux(const char* key, const char* value, void *cb) { const char *ep; size_t section_len; @@@ -702,7 -699,7 +703,7 @@@ static int store_write_section(int fd, if (dot) { strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key); for (i = dot - key + 1; i < store.baselen; i++) { - if (key[i] == '"') + if (key[i] == '"' || key[i] == '\\') strbuf_addch(&sb, '\\'); strbuf_addch(&sb, key[i]); } @@@ -951,7 -948,7 +952,7 @@@ int git_config_set_multivar(const char * As a side effect, we make sure to transform only a valid * existing config file. */ - if (git_config_from_file(store_aux, config_filename)) { + if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { diff --combined diff.c index d57bc299ee,1f46ff0f08..62fdc5492b --- a/diff.c +++ b/diff.c @@@ -19,7 -19,7 +19,7 @@@ #endif static int diff_detect_rename_default; -static int diff_rename_limit_default = 100; +static int diff_rename_limit_default = 200; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@@ -129,7 -129,7 +129,7 @@@ static int parse_funcname_pattern(cons * never be affected by the setting of diff.renames * the user happens to have in the configuration file. */ - int git_diff_ui_config(const char *var, const char *value) + int git_diff_ui_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.renamelimit")) { diff_rename_limit_default = git_config_int(var, value); @@@ -166,10 -166,10 +166,10 @@@ return parse_lldiff_command(var, ep, value); } - return git_diff_basic_config(var, value); + return git_diff_basic_config(var, value, cb); } - int git_diff_basic_config(const char *var, const char *value) + int git_diff_basic_config(const char *var, const char *value, void *cb) { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); @@@ -190,7 -190,7 +190,7 @@@ } } - return git_color_default_config(var, value); + return git_color_default_config(var, value, cb); } static char *quote_two(const char *one, const char *two) @@@ -2496,8 -2496,6 +2496,8 @@@ int diff_opt_parse(struct diff_options DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + else if (!strcmp(arg, "--ignore-submodules")) + DIFF_OPT_SET(options, IGNORE_SUBMODULES); /* misc options */ else if (!strcmp(arg, "-z")) @@@ -3357,9 -3355,6 +3357,9 @@@ void diff_addremove(struct diff_option char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode)) + return; + /* This may look odd, but it is a preparation for * feeding "there are unchanged files which should * not produce diffs, but when you are doing copy @@@ -3404,10 -3399,6 +3404,10 @@@ void diff_change(struct diff_options *o char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode) + && S_ISGITLINK(new_mode)) + return; + if (DIFF_OPT_TST(options, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_c; diff --combined diff.h index 1dfe1f98b1,1cf1eff485..b0b700a5f6 --- a/diff.h +++ b/diff.h @@@ -63,7 -63,6 +63,7 @@@ typedef void (*diff_format_fn_t)(struc #define DIFF_OPT_REVERSE_DIFF (1 << 15) #define DIFF_OPT_CHECK_FAILED (1 << 16) #define DIFF_OPT_RELATIVE_NAME (1 << 17) +#define DIFF_OPT_IGNORE_SUBMODULES (1 << 18) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) @@@ -84,7 -83,6 +84,7 @@@ struct diff_options int pickaxe_opts; int rename_score; int rename_limit; + int warn_on_too_large_rename; int dirstat_percent; int setup; int abbrev; @@@ -182,8 -180,8 +182,8 @@@ extern void diff_unmerge(struct diff_op #define DIFF_SETUP_USE_CACHE 2 #define DIFF_SETUP_USE_SIZE_CACHE 4 - extern int git_diff_basic_config(const char *var, const char *value); - extern int git_diff_ui_config(const char *var, const char *value); + extern int git_diff_basic_config(const char *var, const char *value, void *cb); + extern int git_diff_ui_config(const char *var, const char *value, void *cb); extern int diff_use_color_default; extern void diff_setup(struct diff_options *); extern int diff_opt_parse(struct diff_options *, const char **, int); diff --combined fast-import.c index caea684338,36ec5b87f8..93119bbd94 --- a/fast-import.c +++ b/fast-import.c @@@ -1690,7 -1690,7 +1690,7 @@@ static void skip_optional_lf(void ungetc(term_char, stdin); } -static void cmd_mark(void) +static void parse_mark(void) { if (!prefixcmp(command_buf.buf, "mark :")) { next_mark = strtoumax(command_buf.buf + 6, NULL, 10); @@@ -1700,7 -1700,7 +1700,7 @@@ next_mark = 0; } -static void cmd_data(struct strbuf *sb) +static void parse_data(struct strbuf *sb) { strbuf_reset(sb); @@@ -1798,13 -1798,13 +1798,13 @@@ static char *parse_ident(const char *bu return ident; } -static void cmd_new_blob(void) +static void parse_new_blob(void) { static struct strbuf buf = STRBUF_INIT; read_next_command(); - cmd_mark(); - cmd_data(&buf); + parse_mark(); + parse_data(&buf); store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark); } @@@ -1908,7 -1908,7 +1908,7 @@@ static void file_change_m(struct branc p = uq.buf; } read_next_command(); - cmd_data(&buf); + parse_data(&buf); store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0); } else if (oe) { if (oe->type != OBJ_BLOB) @@@ -1995,7 -1995,7 +1995,7 @@@ static void file_change_deleteall(struc load_tree(&b->branch_tree); } -static void cmd_from_commit(struct branch *b, char *buf, unsigned long size) +static void parse_from_commit(struct branch *b, char *buf, unsigned long size) { if (!buf || size < 46) die("Not a valid commit: %s", sha1_to_hex(b->sha1)); @@@ -2006,7 -2006,7 +2006,7 @@@ b->branch_tree.versions[1].sha1); } -static void cmd_from_existing(struct branch *b) +static void parse_from_existing(struct branch *b) { if (is_null_sha1(b->sha1)) { hashclr(b->branch_tree.versions[0].sha1); @@@ -2017,12 -2017,12 +2017,12 @@@ buf = read_object_with_reference(b->sha1, commit_type, &size, b->sha1); - cmd_from_commit(b, buf, size); + parse_from_commit(b, buf, size); free(buf); } } -static int cmd_from(struct branch *b) +static int parse_from(struct branch *b) { const char *from; struct branch *s; @@@ -2053,12 -2053,12 +2053,12 @@@ if (oe->pack_id != MAX_PACK_ID) { unsigned long size; char *buf = gfi_unpack_entry(oe, &size); - cmd_from_commit(b, buf, size); + parse_from_commit(b, buf, size); free(buf); } else - cmd_from_existing(b); + parse_from_existing(b); } else if (!get_sha1(from, b->sha1)) - cmd_from_existing(b); + parse_from_existing(b); else die("Invalid ref name or SHA1 expression: %s", from); @@@ -2066,7 -2066,7 +2066,7 @@@ return 1; } -static struct hash_list *cmd_merge(unsigned int *count) +static struct hash_list *parse_merge(unsigned int *count) { struct hash_list *list = NULL, *n, *e = e; const char *from; @@@ -2107,7 -2107,7 +2107,7 @@@ return list; } -static void cmd_new_commit(void) +static void parse_new_commit(void) { static struct strbuf msg = STRBUF_INIT; struct branch *b; @@@ -2124,7 -2124,7 +2124,7 @@@ b = new_branch(sp); read_next_command(); - cmd_mark(); + parse_mark(); if (!prefixcmp(command_buf.buf, "author ")) { author = parse_ident(command_buf.buf + 7); read_next_command(); @@@ -2135,10 -2135,10 +2135,10 @@@ } if (!committer) die("Expected committer but didn't get one"); - cmd_data(&msg); + parse_data(&msg); read_next_command(); - cmd_from(b); - merge_list = cmd_merge(&merge_count); + parse_from(b); + merge_list = parse_merge(&merge_count); /* ensure the branch is active/loaded */ if (!b->branch_tree.tree || !max_active_branches) { @@@ -2196,7 -2196,7 +2196,7 @@@ b->last_commit = object_count_by_type[OBJ_COMMIT]; } -static void cmd_new_tag(void) +static void parse_new_tag(void) { static struct strbuf msg = STRBUF_INIT; char *sp; @@@ -2253,7 -2253,7 +2253,7 @@@ /* tag payload/message */ read_next_command(); - cmd_data(&msg); + parse_data(&msg); /* build the tag object */ strbuf_reset(&new_data); @@@ -2273,7 -2273,7 +2273,7 @@@ t->pack_id = pack_id; } -static void cmd_reset_branch(void) +static void parse_reset_branch(void) { struct branch *b; char *sp; @@@ -2293,12 -2293,12 +2293,12 @@@ else b = new_branch(sp); read_next_command(); - cmd_from(b); + parse_from(b); if (command_buf.len > 0) unread_command_buf = 1; } -static void cmd_checkpoint(void) +static void parse_checkpoint(void) { if (object_count) { cycle_packfile(); @@@ -2309,7 -2309,7 +2309,7 @@@ skip_optional_lf(); } -static void cmd_progress(void) +static void parse_progress(void) { fwrite(command_buf.buf, 1, command_buf.len, stdout); fputc('\n', stdout); @@@ -2352,7 -2352,7 +2352,7 @@@ static void import_marks(const char *in fclose(f); } - static int git_pack_config(const char *k, const char *v) + static int git_pack_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "pack.depth")) { max_depth = git_config_int(k, v); @@@ -2370,7 -2370,7 +2370,7 @@@ pack_compression_seen = 1; return 0; } - return git_default_config(k, v); + return git_default_config(k, v, cb); } static const char fast_import_usage[] = @@@ -2381,7 -2381,7 +2381,7 @@@ int main(int argc, const char **argv unsigned int i, show_stats = 1; setup_git_directory(); - git_config(git_pack_config); + git_config(git_pack_config, NULL); if (!pack_compression_seen && core_compression_seen) pack_compression_level = core_compression_level; @@@ -2449,17 -2449,17 +2449,17 @@@ set_die_routine(die_nicely); while (read_next_command() != EOF) { if (!strcmp("blob", command_buf.buf)) - cmd_new_blob(); + parse_new_blob(); else if (!prefixcmp(command_buf.buf, "commit ")) - cmd_new_commit(); + parse_new_commit(); else if (!prefixcmp(command_buf.buf, "tag ")) - cmd_new_tag(); + parse_new_tag(); else if (!prefixcmp(command_buf.buf, "reset ")) - cmd_reset_branch(); + parse_reset_branch(); else if (!strcmp("checkpoint", command_buf.buf)) - cmd_checkpoint(); + parse_checkpoint(); else if (!prefixcmp(command_buf.buf, "progress ")) - cmd_progress(); + parse_progress(); else die("Unsupported command: %s", command_buf.buf); } diff --combined hash-object.c index 0a7ac2fe2a,3d773900ff..48d5223684 --- a/hash-object.c +++ b/hash-object.c @@@ -6,7 -6,6 +6,7 @@@ */ #include "cache.h" #include "blob.h" +#include "quote.h" static void hash_object(const char *path, enum object_type type, int write_object) { @@@ -21,7 -20,6 +21,7 @@@ ? "Unable to add %s to database" : "Unable to hash %s", path); printf("%s\n", sha1_to_hex(sha1)); + maybe_flush_or_die(stdout, "hash to stdout"); } static void hash_stdin(const char *type, int write_object) @@@ -32,27 -30,8 +32,27 @@@ printf("%s\n", sha1_to_hex(sha1)); } +static void hash_stdin_paths(const char *type, int write_objects) +{ + struct strbuf buf, nbuf; + + strbuf_init(&buf, 0); + strbuf_init(&nbuf, 0); + while (strbuf_getline(&buf, stdin, '\n') != EOF) { + if (buf.buf[0] == '"') { + strbuf_reset(&nbuf); + if (unquote_c_style(&nbuf, buf.buf, NULL)) + die("line is badly quoted"); + strbuf_swap(&buf, &nbuf); + } + hash_object(buf.buf, type_from_string(type), write_objects); + } + strbuf_release(&buf); + strbuf_release(&nbuf); +} + static const char hash_object_usage[] = -"git-hash-object [-t ] [-w] [--stdin] ..."; +"git-hash-object [ [-t ] [-w] [--stdin] ... | --stdin-paths < ]"; int main(int argc, char **argv) { @@@ -63,9 -42,8 +63,9 @@@ int prefix_length = -1; int no_more_flags = 0; int hashstdin = 0; + int stdin_paths = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); for (i = 1 ; i < argc; i++) { if (!no_more_flags && argv[i][0] == '-') { @@@ -87,19 -65,7 +87,19 @@@ } else if (!strcmp(argv[i], "--help")) usage(hash_object_usage); + else if (!strcmp(argv[i], "--stdin-paths")) { + if (hashstdin) { + error("Can't use --stdin-paths with --stdin"); + usage(hash_object_usage); + } + stdin_paths = 1; + + } else if (!strcmp(argv[i], "--stdin")) { + if (stdin_paths) { + error("Can't use %s with --stdin-paths", argv[i]); + usage(hash_object_usage); + } if (hashstdin) die("Multiple --stdin arguments are not supported"); hashstdin = 1; @@@ -110,11 -76,6 +110,11 @@@ else { const char *arg = argv[i]; + if (stdin_paths) { + error("Can't specify files (such as \"%s\") with --stdin-paths", arg); + usage(hash_object_usage); + } + if (hashstdin) { hash_stdin(type, write_object); hashstdin = 0; @@@ -126,10 -87,6 +126,10 @@@ no_more_flags = 1; } } + + if (stdin_paths) + hash_stdin_paths(type, write_object); + if (hashstdin) hash_stdin(type, write_object); return 0; diff --combined remote.c index 75a12c0762,dff6c5f686..91e3b112bb --- a/remote.c +++ b/remote.c @@@ -2,16 -2,6 +2,16 @@@ #include "remote.h" #include "refs.h" +static struct refspec s_tag_refspec = { + 0, + 1, + 0, + "refs/tags/", + "refs/tags/" +}; + +const struct refspec *tag_refspec = &s_tag_refspec; + struct counted_string { size_t len; const char *s; @@@ -298,7 -288,7 +298,7 @@@ static void read_branches_file(struct r remote->fetch_tags = 1; /* always auto-follow */ } - static int handle_config(const char *key, const char *value) + static int handle_config(const char *key, const char *value, void *cb) { const char *name; const char *subkey; @@@ -420,7 -410,7 +420,7 @@@ static void read_config(void current_branch = make_branch(head_ref + strlen("refs/heads/"), 0); } - git_config(handle_config); + git_config(handle_config, NULL); alias_all_urls(); } @@@ -444,16 -434,6 +444,16 @@@ static struct refspec *parse_refspec_in } rhs = strrchr(lhs, ':'); + + /* + * Before going on, special case ":" (or "+:") as a refspec + * for matching refs. + */ + if (!fetch && rhs == lhs && rhs[1] == '\0') { + rs[i].matching = 1; + continue; + } + if (rhs) { rhs++; rlen = strlen(rhs); @@@ -875,7 -855,7 +875,7 @@@ static int match_explicit(struct ref *s const char *dst_value = rs->dst; char *dst_guess; - if (rs->pattern) + if (rs->pattern || rs->matching) return errs; matched_src = matched_dst = NULL; @@@ -965,23 -945,13 +965,23 @@@ static const struct refspec *check_patt const struct ref *src) { int i; + int matching_refs = -1; for (i = 0; i < rs_nr; i++) { + if (rs[i].matching && + (matching_refs == -1 || rs[i].force)) { + matching_refs = i; + continue; + } + if (rs[i].pattern && !prefixcmp(src->name, rs[i].src) && src->name[strlen(rs[i].src)] == '/') return rs + i; } - return NULL; + if (matching_refs != -1) + return rs + matching_refs; + else + return NULL; } /* @@@ -992,16 -962,11 +992,16 @@@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, const char **refspec, int flags) { - struct refspec *rs = - parse_push_refspec(nr_refspec, (const char **) refspec); + struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + static const char *default_refspec[] = { ":", 0 }; + if (!nr_refspec) { + nr_refspec = 1; + refspec = default_refspec; + } + rs = parse_push_refspec(nr_refspec, (const char **) refspec); if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) return -1; @@@ -1012,50 -977,48 +1012,50 @@@ char *dst_name; if (src->peer_ref) continue; - if (nr_refspec) { - pat = check_pattern_match(rs, nr_refspec, src); - if (!pat) - continue; - } - else if (!send_mirror && prefixcmp(src->name, "refs/heads/")) + + pat = check_pattern_match(rs, nr_refspec, src); + if (!pat) + continue; + + if (pat->matching) { /* * "matching refs"; traditionally we pushed everything * including refs outside refs/heads/ hierarchy, but * that does not make much sense these days. */ - continue; + if (!send_mirror && prefixcmp(src->name, "refs/heads/")) + continue; + dst_name = xstrdup(src->name); - if (pat) { + } else { const char *dst_side = pat->dst ? pat->dst : pat->src; dst_name = xmalloc(strlen(dst_side) + strlen(src->name) - strlen(pat->src) + 2); strcpy(dst_name, dst_side); strcat(dst_name, src->name + strlen(pat->src)); - } else - dst_name = xstrdup(src->name); + } dst_peer = find_ref_by_name(dst, dst_name); - if (dst_peer && dst_peer->peer_ref) - /* We're already sending something to this ref. */ - goto free_name; + if (dst_peer) { + if (dst_peer->peer_ref) + /* We're already sending something to this ref. */ + goto free_name; + + } else { + if (pat->matching && !(send_all || send_mirror)) + /* + * Remote doesn't have it, and we have no + * explicit pattern, and we don't have + * --all nor --mirror. + */ + goto free_name; - if (!dst_peer && !nr_refspec && !(send_all || send_mirror)) - /* - * Remote doesn't have it, and we have no - * explicit pattern, and we don't have - * --all nor --mirror. - */ - goto free_name; - if (!dst_peer) { /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } dst_peer->peer_ref = src; - if (pat) - dst_peer->force = pat->force; + dst_peer->force = pat->force; free_name: free(dst_name); } diff --combined wt-status.c index 6e6516cd87,c932b39d20..5b4d74c1f3 --- a/wt-status.c +++ b/wt-status.c @@@ -18,7 -18,6 +18,7 @@@ static char wt_status_colors[][COLOR_MA "\033[32m", /* WT_STATUS_UPDATED: green */ "\033[31m", /* WT_STATUS_CHANGED: red */ "\033[31m", /* WT_STATUS_UNTRACKED: red */ + "\033[31m", /* WT_STATUS_NOBRANCH: red */ }; static const char use_add_msg[] = @@@ -39,8 -38,6 +39,8 @@@ static int parse_status_slot(const cha return WT_STATUS_CHANGED; if (!strcasecmp(var+offset, "untracked")) return WT_STATUS_UNTRACKED; + if (!strcasecmp(var+offset, "nobranch")) + return WT_STATUS_NOBRANCH; die("bad config variable '%s'", var); } @@@ -209,7 -206,7 +209,7 @@@ static void wt_status_print_updated(str rev.diffopt.format_callback = wt_status_print_updated_cb; rev.diffopt.format_callback_data = s; rev.diffopt.detect_rename = 1; - rev.diffopt.rename_limit = 100; + rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; run_diff_index(&rev, 1); } @@@ -317,9 -314,8 +317,9 @@@ static void wt_status_print_verbose(str void wt_status_print(struct wt_status *s) { unsigned char sha1[20]; - s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; + const char *branch_color = color(WT_STATUS_HEADER); + s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; if (s->branch) { const char *on_what = "On branch "; const char *branch_name = s->branch; @@@ -327,11 -323,10 +327,11 @@@ branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_name = ""; + branch_color = color(WT_STATUS_NOBRANCH); on_what = "Not currently on any branch."; } - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), - "# %s%s", on_what, branch_name); + color_fprintf(s->fp, color(WT_STATUS_HEADER), "# "); + color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name); } if (s->is_initial) { @@@ -367,7 -362,7 +367,7 @@@ } } - int git_status_config(const char *k, const char *v) + int git_status_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "status.submodulesummary")) { int is_bool; @@@ -391,5 -386,5 +391,5 @@@ wt_status_relative_paths = git_config_bool(k, v); return 0; } - return git_color_default_config(k, v); + return git_color_default_config(k, v, cb); } diff --combined wt-status.h index f0675fdff3,f2c71302cb..597c7ea988 --- a/wt-status.h +++ b/wt-status.h @@@ -8,7 -8,6 +8,7 @@@ enum color_wt_status WT_STATUS_UPDATED, WT_STATUS_CHANGED, WT_STATUS_UNTRACKED, + WT_STATUS_NOBRANCH, }; struct wt_status { @@@ -28,7 -27,7 +28,7 @@@ const char *prefix; }; - int git_status_config(const char *var, const char *value); + int git_status_config(const char *var, const char *value, void *cb); extern int wt_status_use_color; extern int wt_status_relative_paths; void wt_status_prepare(struct wt_status *s);