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;
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)
"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),
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);
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;
}
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");
}
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) {
die("Unable to write new index file");
}
- return 0;
+ return exit_status;
}
}
/*
- * 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
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);
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
* 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))
/*
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);
}
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);
#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)
{
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|<type>] <sha1>");
- 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':
write_or_die(1, buf, size);
return 0;
}
- git_config(git_default_config);
+
+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|<type>] <sha1>",
+ "git-cat-file [--batch|--batch-check] < <list_of_sha1s>",
+ 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("<type> 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, 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);
+}
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()) {
* 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);
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;
--- /dev/null
- git_config(git_default_config);
+/*
+ * Builtin "git clone"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ * 2008 Daniel Barkalow <barkalow@iabervon.org>
+ * 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] [--] <repo> [<dir>]",
+ 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 <branch> 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, 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;
+}
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;
/*
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);
}
}
*/
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))
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;
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;
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"
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);
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[])
{
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;
}
}
- 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)
return 0;
}
- return git_status_config(k, v);
+ return git_status_config(k, v, cb);
}
static const char commit_utf8_warn[] =
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);
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);
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"))
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)
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();
/*
* 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]);
}
}
-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);
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 &&
}
memcpy(path, git_dir, len);
+ if (len && path[len - 1] != '/')
+ path[len++] = '/';
path[len] = 0;
copy_templates_1(path, len,
template_path, template_len,
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;
/*
* 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"));
}
/*
/* 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)) &&
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[] =
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];
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);
}
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);
}
#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;
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)) {
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);
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;
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;
{
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;
{
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;
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)
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;
}
return 0;
}
- return git_log_config(var, value);
+ return git_log_config(var, value, cb);
}
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;
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)) {
*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)) {
}
}
*ot = 0;
- return 0;
+ return (ot - otbegin);
}
/*
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)
{
"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())
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;
break;
filter++;
case 1:
- if (!handle_patch(line))
+ if (!handle_patch(line, linelen))
break;
filter++;
default:
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) {
/* 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:
/* 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;
}
/* 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;
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;
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");
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)
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);
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)
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++)
[--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\
[<ref-list | <object-list]";
struct object_entry {
*/
void *delta_data; /* cached delta (uncompressed) */
unsigned long delta_size; /* delta data size (uncompressed) */
+ unsigned long z_delta_size; /* delta data size (compressed) */
unsigned int hash; /* name hint hash */
enum object_type type;
enum object_type in_pack_type; /* could be delta */
static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
static int non_empty;
-static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
+static int reuse_delta = 1, reuse_object = 1;
+static int keep_unreachable, unpack_unreachable, include_tag;
static int local;
static int incremental;
static int allow_ofs_delta;
static uint32_t reused, reused_delta;
-static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
+static void *get_delta(struct object_entry *entry)
{
- unsigned long othersize, delta_size;
+ unsigned long size, base_size, delta_size;
+ void *buf, *base_buf, *delta_buf;
enum object_type type;
- void *otherbuf = read_sha1_file(entry->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",
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 */
*/
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
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;
}
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 {
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;
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;
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);
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));
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;
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);
* 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)
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;
/*
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;
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);
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)
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;
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)
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;
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)) {
keep_unreachable = 1;
continue;
}
+ if (!strcmp("--unpack-unreachable", arg)) {
+ unpack_unreachable = 1;
+ continue;
+ }
if (!strcmp("--include-tag", arg)) {
include_tag = 1;
continue;
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();
#include "list-objects.h"
#include "builtin.h"
#include "log-tree.h"
+#include "graph.h"
/* bits #0-15 in revision.h */
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));
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);
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;
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)
{
return;
def = NULL;
- if (type != show_type)
- putchar('^');
if (symbolic && name) {
if (symbolic == SHOW_SYMBOLIC_FULL) {
unsigned char discard[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. */
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];
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));
refresh_flags |= REFRESH_QUIET;
continue;
}
+ if (!strcmp(path, "--ignore-submodules")) {
+ refresh_flags |= REFRESH_IGNORE_SUBMODULES;
+ continue;
+ }
if (!strcmp(path, "--add")) {
allow_add = 1;
continue;
#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))
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"
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)
/*
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);
#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 {
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);
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);
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;
/* 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 *);
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);
#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;
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;
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;
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)
}
}
- 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;
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);
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")) {
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;
}
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;
}
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");
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;
}
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;
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)
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;
}
!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;
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]);
}
* 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) {
#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;
* 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);
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);
}
}
- 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)
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"))
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
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;
#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)
int pickaxe_opts;
int rename_score;
int rename_limit;
+ int warn_on_too_large_rename;
int dirstat_percent;
int setup;
int abbrev;
#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);
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);
next_mark = 0;
}
-static void cmd_data(struct strbuf *sb)
+static void parse_data(struct strbuf *sb)
{
strbuf_reset(sb);
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);
}
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)
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));
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);
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;
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);
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;
return list;
}
-static void cmd_new_commit(void)
+static void parse_new_commit(void)
{
static struct strbuf msg = STRBUF_INIT;
struct branch *b;
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();
}
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) {
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;
/* tag payload/message */
read_next_command();
- cmd_data(&msg);
+ parse_data(&msg);
/* build the tag object */
strbuf_reset(&new_data);
t->pack_id = pack_id;
}
-static void cmd_reset_branch(void)
+static void parse_reset_branch(void)
{
struct branch *b;
char *sp;
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();
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);
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);
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[] =
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;
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);
}
*/
#include "cache.h"
#include "blob.h"
+#include "quote.h"
static void hash_object(const char *path, enum object_type type, int write_object)
{
? "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)
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 <type>] [-w] [--stdin] <file>...";
+"git-hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]";
int main(int argc, char **argv)
{
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] == '-') {
}
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;
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;
no_more_flags = 1;
}
}
+
+ if (stdin_paths)
+ hash_stdin_paths(type, write_object);
+
if (hashstdin)
hash_stdin(type, write_object);
return 0;
#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;
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;
current_branch =
make_branch(head_ref + strlen("refs/heads/"), 0);
}
- git_config(handle_config);
+ git_config(handle_config, NULL);
alias_all_urls();
}
}
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);
const char *dst_value = rs->dst;
char *dst_guess;
- if (rs->pattern)
+ if (rs->pattern || rs->matching)
return errs;
matched_src = matched_dst = NULL;
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;
}
/*
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;
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);
}
"\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[] =
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);
}
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);
}
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;
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) {
}
}
- 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;
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);
}
WT_STATUS_UPDATED,
WT_STATUS_CHANGED,
WT_STATUS_UNTRACKED,
+ WT_STATUS_NOBRANCH,
};
struct wt_status {
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);