summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c33
-rw-r--r--builtin/am.c11
-rw-r--r--builtin/apply.c6
-rw-r--r--builtin/blame.c6
-rw-r--r--builtin/branch.c36
-rw-r--r--builtin/cat-file.c21
-rw-r--r--builtin/checkout.c7
-rw-r--r--builtin/clone.c25
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c13
-rw-r--r--builtin/diff.c1
-rw-r--r--builtin/fetch.c12
-rw-r--r--builtin/fsck.c3
-rw-r--r--builtin/grep.c6
-rw-r--r--builtin/help.c8
-rw-r--r--builtin/log.c185
-rw-r--r--builtin/mailsplit.c10
-rw-r--r--builtin/merge-file.c10
-rw-r--r--builtin/merge-tree.c18
-rw-r--r--builtin/merge.c25
-rw-r--r--builtin/notes.c8
-rw-r--r--builtin/pack-objects.c10
-rw-r--r--builtin/patch-id.c23
-rw-r--r--builtin/pull.c36
-rw-r--r--builtin/receive-pack.c55
-rw-r--r--builtin/reflog.c4
-rw-r--r--builtin/rev-list.c6
-rw-r--r--builtin/rm.c2
-rw-r--r--builtin/submodule--helper.c623
-rw-r--r--builtin/tag.c28
-rw-r--r--builtin/update-index.c2
-rw-r--r--builtin/upload-archive.c3
-rw-r--r--builtin/verify-tag.c61
-rw-r--r--builtin/worktree.c35
34 files changed, 1090 insertions, 246 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 145f06ef97..b1dddb4ac6 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -26,7 +26,7 @@ static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
struct update_callback_data {
- int flags;
+ int flags, force_mode;
int add_errors;
};
@@ -65,7 +65,8 @@ static void update_callback(struct diff_queue_struct *q,
die(_("unexpected diff status %c"), p->status);
case DIFF_STATUS_MODIFIED:
case DIFF_STATUS_TYPE_CHANGED:
- if (add_file_to_index(&the_index, path, data->flags)) {
+ if (add_file_to_index(&the_index, path,
+ data->flags, data->force_mode)) {
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
die(_("updating files failed"));
data->add_errors++;
@@ -83,14 +84,15 @@ static void update_callback(struct diff_queue_struct *q,
}
}
-int add_files_to_cache(const char *prefix,
- const struct pathspec *pathspec, int flags)
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
+ int flags, int force_mode)
{
struct update_callback_data data;
struct rev_info rev;
memset(&data, 0, sizeof(data));
data.flags = flags;
+ data.force_mode = force_mode;
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
@@ -238,6 +240,8 @@ static int ignore_add_errors, intent_to_add, ignore_missing;
static int addremove = ADDREMOVE_DEFAULT;
static int addremove_explicit = -1; /* unspecified */
+static char *chmod_arg;
+
static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
{
/* if we are told to ignore, we are not adding removals */
@@ -263,6 +267,7 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
+ OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
OPT_END(),
};
@@ -276,7 +281,7 @@ static int add_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-static int add_files(struct dir_struct *dir, int flags)
+static int add_files(struct dir_struct *dir, int flags, int force_mode)
{
int i, exit_status = 0;
@@ -289,7 +294,8 @@ static int add_files(struct dir_struct *dir, int flags)
}
for (i = 0; i < dir->nr; i++)
- if (add_file_to_cache(dir->entries[i]->name, flags)) {
+ if (add_file_to_index(&the_index, dir->entries[i]->name,
+ flags, force_mode)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
@@ -302,7 +308,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int exit_status = 0;
struct pathspec pathspec;
struct dir_struct dir;
- int flags;
+ int flags, force_mode;
int add_new_files;
int require_pathspec;
char *seen = NULL;
@@ -336,6 +342,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (!show_only && ignore_missing)
die(_("Option --ignore-missing can only be used together with --dry-run"));
+ if (!chmod_arg)
+ force_mode = 0;
+ else if (!strcmp(chmod_arg, "-x"))
+ force_mode = 0666;
+ else if (!strcmp(chmod_arg, "+x"))
+ force_mode = 0777;
+ else
+ die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
+
add_new_files = !take_worktree_changes && !refresh_only;
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
@@ -426,10 +441,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
plug_bulk_checkin();
- exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+ exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
if (add_new_files)
- exit_status |= add_files(&dir, flags);
+ exit_status |= add_files(&dir, flags, force_mode);
unplug_bulk_checkin();
diff --git a/builtin/am.c b/builtin/am.c
index d003939bc5..3dfe70b7a0 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -769,15 +769,15 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
in = fopen(*paths, "r");
if (!in)
- return error(_("could not open '%s' for reading: %s"),
- *paths, strerror(errno));
+ return error_errno(_("could not open '%s' for reading"),
+ *paths);
mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
out = fopen(mail, "w");
if (!out)
- return error(_("could not open '%s' for writing: %s"),
- mail, strerror(errno));
+ return error_errno(_("could not open '%s' for writing"),
+ mail);
ret = fn(out, in, keep_cr);
@@ -857,8 +857,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
fp = fopen(*paths, "r");
if (!fp)
- return error(_("could not open '%s' for reading: %s"), *paths,
- strerror(errno));
+ return error_errno(_("could not open '%s' for reading"), *paths);
while (!strbuf_getline_lf(&sb, fp)) {
if (*sb.buf == '#')
diff --git a/builtin/apply.c b/builtin/apply.c
index ce3b77853c..c770d7d3d9 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -442,7 +442,7 @@ static int is_dev_null(const char *str)
#define TERM_SPACE 1
#define TERM_TAB 2
-static int name_terminate(const char *name, int namelen, int c, int terminate)
+static int name_terminate(int c, int terminate)
{
if (c == ' ' && !(terminate & TERM_SPACE))
return 0;
@@ -671,7 +671,7 @@ static char *find_name_common(const char *line, const char *def,
if (!end && isspace(c)) {
if (c == '\n')
break;
- if (name_terminate(start, line-start, c, terminate))
+ if (name_terminate(c, terminate))
break;
}
line++;
@@ -4383,6 +4383,8 @@ static int apply_patch(int fd, const char *filename, int options)
listp = &patch->next;
}
else {
+ if (apply_verbosely)
+ say_patch_name(stderr, _("Skipped patch '%s'."), patch);
free_patch(patch);
skipped_patch++;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 21f42b0b62..7417edf6ef 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2425,8 +2425,7 @@ static struct commit *find_single_final(struct rev_info *revs,
struct object *obj = revs->pending.objects[i].item;
if (obj->flags & UNINTERESTING)
continue;
- while (obj->type == OBJ_TAG)
- obj = deref_tag(obj, NULL, 0);
+ obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (found)
@@ -2461,8 +2460,7 @@ static char *prepare_initial(struct scoreboard *sb)
struct object *obj = revs->pending.objects[i].item;
if (!(obj->flags & UNINTERESTING))
continue;
- while (obj->type == OBJ_TAG)
- obj = deref_tag(obj, NULL, 0);
+ obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (sb->final)
diff --git a/builtin/branch.c b/builtin/branch.c
index 37af77161e..2ecde53bf8 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
name = mkpathdup(fmt, bname.buf);
if (kinds == FILTER_REFS_BRANCHES) {
- char *worktree = find_shared_symref("HEAD", name);
- if (worktree) {
+ const struct worktree *wt =
+ find_shared_symref("HEAD", name);
+ if (wt) {
error(_("Cannot delete branch '%s' "
"checked out at '%s'"),
- bname.buf, worktree);
- free(worktree);
+ bname.buf, wt->path);
ret = 1;
continue;
}
@@ -526,6 +526,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
ref_array_clear(&array);
}
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+ struct worktree **worktrees = get_worktrees();
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+
+ if (!wt->is_detached)
+ continue;
+
+ if (is_worktree_being_rebased(wt, target))
+ die(_("Branch %s is being rebased at %s"),
+ target, wt->path);
+
+ if (is_worktree_being_bisected(wt, target))
+ die(_("Branch %s is being bisected at %s"),
+ target, wt->path);
+ }
+
+ free_worktrees(worktrees);
+}
+
static void rename_branch(const char *oldname, const char *newname, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -555,6 +578,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
validate_new_branchname(newname, &newref, force, clobber_head_ok);
+ reject_rebase_or_bisect_branch(oldref.buf);
+
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
@@ -595,8 +620,7 @@ static int edit_branch_description(const char *branch_name)
branch_name, comment_line_char);
if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
strbuf_release(&buf);
- return error(_("could not write branch description template: %s"),
- strerror(errno));
+ return error_errno(_("could not write branch description template"));
}
strbuf_reset(&buf);
if (launch_editor(git_path(edit_description), &buf, NULL)) {
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 54db1184a0..618103fdee 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -154,6 +154,13 @@ struct expand_data {
* elements above, so you can retrieve the response from there.
*/
struct object_info info;
+
+ /*
+ * This flag will be true if the requested batch format and options
+ * don't require us to call sha1_object_info, which can then be
+ * optimized out.
+ */
+ unsigned skip_object_info : 1;
};
static int is_atom(const char *atom, const char *s, int slen)
@@ -258,7 +265,8 @@ static void batch_object_write(const char *obj_name, struct batch_options *opt,
{
struct strbuf buf = STRBUF_INIT;
- if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
+ if (!data->skip_object_info &&
+ sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
printf("%s missing\n", obj_name ? obj_name : sha1_to_hex(data->sha1));
fflush(stdout);
return;
@@ -369,6 +377,13 @@ static int batch_objects(struct batch_options *opt)
strbuf_expand(&buf, opt->format, expand_format, &data);
data.mark_query = 0;
+ if (opt->all_objects) {
+ struct object_info empty;
+ memset(&empty, 0, sizeof(empty));
+ if (!memcmp(&data.info, &empty, sizeof(empty)))
+ data.skip_object_info = 1;
+ }
+
/*
* If we are printing out the object, then always fill in the type,
* since we will want to decide whether or not to stream.
@@ -489,6 +504,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
git_config(git_cat_file_config, NULL);
+ batch.buffer_output = -1;
argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
if (opt) {
@@ -512,6 +528,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
usage_with_options(cat_file_usage, options);
}
+ if (batch.buffer_output < 0)
+ batch.buffer_output = batch.all_objects;
+
if (batch.enabled)
return batch_objects(&batch);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index efcbd8f6b5..c3486bdec3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -242,7 +242,6 @@ static int checkout_paths(const struct checkout_opts *opts,
struct checkout state;
static char *ps_matched;
unsigned char rev[20];
- int flag;
struct commit *head;
int errs = 0;
struct lock_file *lock_file;
@@ -375,7 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- read_ref_full("HEAD", 0, rev, &flag);
+ read_ref_full("HEAD", 0, rev, NULL);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -549,7 +548,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
* entries in the index.
*/
- add_files_to_cache(NULL, NULL, 0);
+ add_files_to_cache(NULL, NULL, 0, 0);
/*
* NEEDSWORK: carrying over local changes
* when branches have different end-of-line
@@ -1111,7 +1110,7 @@ static int checkout_branch(struct checkout_opts *opts,
char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
if (head_ref &&
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
- die_if_checked_out(new->path);
+ die_if_checked_out(new->path, 1);
free(head_ref);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index 661639255c..8f7db98af6 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = {
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
+static int option_shallow_submodules;
static char *option_template, *option_depth;
static char *option_origin = NULL;
static char *option_branch = NULL;
@@ -51,6 +52,7 @@ static enum transport_family family;
static struct string_list option_config;
static struct string_list option_reference;
static int option_dissociate;
+static int max_jobs = -1;
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
@@ -73,6 +75,8 @@ static struct option builtin_clone_options[] = {
N_("initialize submodules in the clone")),
OPT_BOOL(0, "recurse-submodules", &option_recursive,
N_("initialize submodules in the clone")),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
N_("directory from which templates will be used")),
OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
@@ -89,6 +93,8 @@ static struct option builtin_clone_options[] = {
N_("create a shallow clone of that depth")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
+ N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
@@ -100,10 +106,6 @@ static struct option builtin_clone_options[] = {
OPT_END()
};
-static const char *argv_submodule[] = {
- "submodule", "update", "--init", "--recursive", NULL
-};
-
static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
@@ -732,8 +734,19 @@ static int checkout(void)
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(sha1), "1", NULL);
- if (!err && option_recursive)
- err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+ if (!err && option_recursive) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+ argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+ if (option_shallow_submodules == 1)
+ argv_array_push(&args, "--depth=1");
+
+ if (max_jobs != -1)
+ argv_array_pushf(&args, "--jobs=%d", max_jobs);
+
+ err = run_command_v_opt(args.argv, RUN_GIT_CMD);
+ argv_array_clear(&args);
+ }
return err;
}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 3feeffeab1..8a674bc9e7 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -33,10 +33,6 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, NULL);
if (status)
return status;
- if (!strcmp(var, "commit.gpgsign")) {
- sign_commit = git_config_bool(var, value) ? "" : NULL;
- return 0;
- }
return git_default_config(var, value, cb);
}
diff --git a/builtin/commit.c b/builtin/commit.c
index e13303787a..163dbcabf3 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -114,6 +114,7 @@ static char *fixup_message, *squash_message;
static int all, also, interactive, patch_interactive, only, amend, signoff;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
static char *sign_commit;
@@ -186,6 +187,7 @@ static void status_init_config(struct wt_status *s, config_fn_t fn)
gitmodules_config();
git_config(fn, s);
determine_whence(s);
+ init_diff_ui_defaults();
s->hints = advice_status_hints; /* must come after git_config() */
}
@@ -384,7 +386,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
*/
if (all || (also && pathspec.nr)) {
hold_locked_index(&index_lock, 1);
- add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+ add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
refresh_cache_or_die(refresh_flags);
update_main_cache_tree(WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
@@ -1514,6 +1516,11 @@ static int git_commit_config(const char *k, const char *v, void *cb)
sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
}
+ if (!strcmp(k, "commit.verbose")) {
+ int is_bool;
+ config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+ return 0;
+ }
status = git_gpg_config(k, v, NULL);
if (status)
@@ -1660,9 +1667,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
+ verbose = -1; /* unspecified */
argc = parse_and_validate_options(argc, argv, builtin_commit_options,
builtin_commit_usage,
prefix, current_head, &s);
+ if (verbose == -1)
+ verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
+
if (dry_run)
return dry_run_commit(argc, argv, prefix, current_head, &s);
index_file = prepare_index(argc, argv, prefix, current_head, 0);
diff --git a/builtin/diff.c b/builtin/diff.c
index d6b8f9834d..b7a9405d9f 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -318,6 +318,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!no_index)
gitmodules_config();
+ init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
precompose_argv(argc, argv);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index e4639d8eb1..f896aa1f88 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -37,7 +37,7 @@ static int prune = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int tags = TAGS_DEFAULT, unshallow, update_shallow;
-static int max_children = 1;
+static int max_children = -1;
static enum transport_family family;
static const char *depth;
static const char *upload_pack;
@@ -607,7 +607,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
fp = fopen(filename, "a");
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
if (raw_url)
url = transport_anonymize_url(raw_url);
@@ -848,7 +848,7 @@ static int truncate_fetch_head(void)
FILE *fp = fopen_for_writing(filename);
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
fclose(fp);
return 0;
}
@@ -1005,7 +1005,7 @@ static int get_remote_group(const char *key, const char *value, void *priv)
size_t wordlen = strcspn(value, " \t\n");
if (wordlen >= 1)
- string_list_append(g->list,
+ string_list_append_nodup(g->list,
xstrndup(value, wordlen));
value += wordlen + (value[wordlen] != '\0');
}
@@ -1143,7 +1143,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
- struct string_list list = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote;
int result = 0;
struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
@@ -1226,8 +1226,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
argv_array_clear(&options);
}
- /* All names were strdup()ed or strndup()ed */
- list.strdup_strings = 1;
string_list_clear(&list, 0);
close_all_packs();
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 55eac756f7..3f27456883 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -493,13 +493,12 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void)
{
- int flag;
int null_is_error = 0;
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
if (!head_points_at) {
errors_found |= ERROR_REFS;
return error("Invalid HEAD");
diff --git a/builtin/grep.c b/builtin/grep.c
index 111b6f6cf1..462e607901 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -438,7 +438,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
strbuf_add(base, entry.path, te_len);
if (S_ISREG(entry.mode)) {
- hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len,
+ hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
check_attr ? base->buf + tn_len : NULL);
}
else if (S_ISDIR(entry.mode)) {
@@ -447,10 +447,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
void *data;
unsigned long size;
- data = lock_and_read_sha1_file(entry.sha1, &type, &size);
+ data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
if (!data)
die(_("unable to read tree (%s)"),
- sha1_to_hex(entry.sha1));
+ oid_to_hex(entry.oid));
strbuf_addch(base, '/');
init_tree_desc(&sub, data, size);
diff --git a/builtin/help.c b/builtin/help.c
index 3c55ce4563..88480131cf 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -127,7 +127,7 @@ static void exec_woman_emacs(const char *path, const char *page)
path = "emacsclient";
strbuf_addf(&man_page, "(woman \"%s\")", page);
execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
}
@@ -148,7 +148,7 @@ static void exec_man_konqueror(const char *path, const char *page)
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
}
@@ -157,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
if (!path)
path = "man";
execlp(path, "man", page, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
static void exec_man_cmd(const char *cmd, const char *page)
@@ -165,7 +165,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
struct strbuf shell_cmd = STRBUF_INIT;
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
+ warning(_("failed to exec '%s'"), cmd);
}
static void add_man_viewer(const char *name)
diff --git a/builtin/log.c b/builtin/log.c
index 0d738d6ddc..099f4f7be9 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -100,6 +100,12 @@ static int log_line_range_callback(const struct option *option, const char *arg,
return 0;
}
+static void init_log_defaults(void)
+{
+ init_grep_defaults();
+ init_diff_ui_defaults();
+}
+
static void cmd_log_init_defaults(struct rev_info *rev)
{
if (fmt_pretty)
@@ -416,7 +422,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -527,7 +533,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
struct pathspec match_all;
int i, count, ret = 0;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
memset(&match_all, 0, sizeof(match_all));
@@ -608,7 +614,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -647,7 +653,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -696,6 +702,7 @@ static void add_header(const char *value)
#define THREAD_DEEP 2
static int thread;
static int do_signoff;
+static int base_auto;
static const char *signature = git_version_string;
static const char *signature_file;
static int config_cover_letter;
@@ -780,6 +787,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "format.outputdirectory"))
return git_config_string(&config_output_directory, var, value);
+ if (!strcmp(var, "format.useautobase")) {
+ base_auto = git_config_bool(var, value);
+ return 0;
+ }
return git_log_config(var, value, cb);
}
@@ -1185,6 +1196,155 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+struct base_tree_info {
+ struct object_id base_commit;
+ int nr_patch_id, alloc_patch_id;
+ struct object_id *patch_id;
+};
+
+static struct commit *get_base_commit(const char *base_commit,
+ struct commit **list,
+ int total)
+{
+ struct commit *base = NULL;
+ struct commit **rev;
+ int i = 0, rev_nr = 0;
+
+ if (base_commit && strcmp(base_commit, "auto")) {
+ base = lookup_commit_reference_by_name(base_commit);
+ if (!base)
+ die(_("Unknown commit %s"), base_commit);
+ } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+ struct branch *curr_branch = branch_get(NULL);
+ const char *upstream = branch_get_upstream(curr_branch, NULL);
+ if (upstream) {
+ struct commit_list *base_list;
+ struct commit *commit;
+ unsigned char sha1[20];
+
+ if (get_sha1(upstream, sha1))
+ die(_("Failed to resolve '%s' as a valid ref."), upstream);
+ commit = lookup_commit_or_die(sha1, "upstream base");
+ base_list = get_merge_bases_many(commit, total, list);
+ /* There should be one and only one merge base. */
+ if (!base_list || base_list->next)
+ die(_("Could not find exact merge base."));
+ base = base_list->item;
+ free_commit_list(base_list);
+ } else {
+ die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+ "please use git branch --set-upstream-to to track a remote branch.\n"
+ "Or you could specify base commit by --base=<base-commit-id> manually."));
+ }
+ }
+
+ ALLOC_ARRAY(rev, total);
+ for (i = 0; i < total; i++)
+ rev[i] = list[i];
+
+ rev_nr = total;
+ /*
+ * Get merge base through pair-wise computations
+ * and store it in rev[0].
+ */
+ while (rev_nr > 1) {
+ for (i = 0; i < rev_nr / 2; i++) {
+ struct commit_list *merge_base;
+ merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+ if (!merge_base || merge_base->next)
+ die(_("Failed to find exact merge base"));
+
+ rev[i] = merge_base->item;
+ }
+
+ if (rev_nr % 2)
+ rev[i] = rev[2 * i];
+ rev_nr = (rev_nr + 1) / 2;
+ }
+
+ if (!in_merge_bases(base, rev[0]))
+ die(_("base commit should be the ancestor of revision list"));
+
+ for (i = 0; i < total; i++) {
+ if (base == list[i])
+ die(_("base commit shouldn't be in revision list"));
+ }
+
+ free(rev);
+ return base;
+}
+
+static void prepare_bases(struct base_tree_info *bases,
+ struct commit *base,
+ struct commit **list,
+ int total)
+{
+ struct commit *commit;
+ struct rev_info revs;
+ struct diff_options diffopt;
+ int i;
+
+ if (!base)
+ return;
+
+ diff_setup(&diffopt);
+ DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diff_setup_done(&diffopt);
+
+ oidcpy(&bases->base_commit, &base->object.oid);
+
+ init_revisions(&revs, NULL);
+ revs.max_parents = 1;
+ revs.topo_order = 1;
+ for (i = 0; i < total; i++) {
+ list[i]->object.flags &= ~UNINTERESTING;
+ add_pending_object(&revs, &list[i]->object, "rev_list");
+ list[i]->util = (void *)1;
+ }
+ base->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &base->object, "base");
+
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+ /*
+ * Traverse the commits list, get prerequisite patch ids
+ * and stuff them in bases structure.
+ */
+ while ((commit = get_revision(&revs)) != NULL) {
+ unsigned char sha1[20];
+ struct object_id *patch_id;
+ if (commit->util)
+ continue;
+ if (commit_patch_id(commit, &diffopt, sha1))
+ die(_("cannot get patch id"));
+ ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
+ patch_id = bases->patch_id + bases->nr_patch_id;
+ hashcpy(patch_id->hash, sha1);
+ bases->nr_patch_id++;
+ }
+}
+
+static void print_bases(struct base_tree_info *bases)
+{
+ int i;
+
+ /* Only do this once, either for the cover or for the first one */
+ if (is_null_oid(&bases->base_commit))
+ return;
+
+ /* Show the base commit */
+ printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
+
+ /* Show the prerequisite patches */
+ for (i = bases->nr_patch_id - 1; i >= 0; i--)
+ printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+
+ free(bases->patch_id);
+ bases->nr_patch_id = 0;
+ bases->alloc_patch_id = 0;
+ oidclr(&bases->base_commit);
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1209,6 +1369,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int reroll_count = -1;
char *branch_name = NULL;
char *from = NULL;
+ char *base_commit = NULL;
+ struct base_tree_info bases;
+
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
N_("use [PATCH n/m] even with a single patch"),
@@ -1271,6 +1434,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, thread_callback },
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
+ OPT_STRING(0, "base", &base_commit, N_("base-commit"),
+ N_("add prerequisite tree info to the patch series")),
OPT_FILENAME(0, "signature-file", &signature_file,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
@@ -1280,10 +1445,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_hdr.strdup_strings = 1;
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_format_config, NULL);
init_revisions(&rev, prefix);
rev.commit_format = CMIT_FMT_EMAIL;
+ rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
rev.diff = 1;
rev.max_parents = 1;
@@ -1507,6 +1673,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
signature = strbuf_detach(&buf, NULL);
}
+ memset(&bases, 0, sizeof(bases));
+ if (base_commit || base_auto) {
+ struct commit *base = get_base_commit(base_commit, list, nr);
+ reset_revision_walk();
+ prepare_bases(&bases, base, list, nr);
+ }
+
if (in_reply_to || thread || cover_letter)
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
if (in_reply_to) {
@@ -1520,6 +1693,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout,
origin, nr, list, branch_name, quiet);
+ print_bases(&bases);
total++;
start_number--;
}
@@ -1585,6 +1759,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.mime_boundary);
else
print_signature();
+ print_bases(&bases);
}
if (!use_stdout)
fclose(stdout);
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 104277acc4..4859ede38a 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -109,7 +109,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
if ((dir = opendir(name)) == NULL) {
if (errno == ENOENT)
continue;
- error("cannot opendir %s (%s)", name, strerror(errno));
+ error_errno("cannot opendir %s", name);
goto out;
}
@@ -174,12 +174,12 @@ static int split_maildir(const char *maildir, const char *dir,
f = fopen(file, "r");
if (!f) {
- error("cannot open mail %s (%s)", file, strerror(errno));
+ error_errno("cannot open mail %s", file);
goto out;
}
if (strbuf_getwholeline(&buf, f, '\n')) {
- error("cannot read mail %s (%s)", file, strerror(errno));
+ error_errno("cannot read mail %s", file);
goto out;
}
@@ -210,7 +210,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
int file_done = 0;
if (!f) {
- error("cannot open mbox %s", file);
+ error_errno("cannot open mbox %s", file);
goto out;
}
@@ -318,7 +318,7 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
}
if (stat(arg, &argstat) == -1) {
- error("cannot stat %s (%s)", arg, strerror(errno));
+ error_errno("cannot stat %s", arg);
return 1;
}
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 55447053f2..13e22a2f0b 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -62,8 +62,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
usage_with_options(merge_file_usage, options);
if (quiet) {
if (!freopen("/dev/null", "w", stderr))
- return error("failed to redirect stderr to /dev/null: "
- "%s", strerror(errno));
+ return error_errno("failed to redirect stderr to /dev/null");
}
if (prefix)
@@ -95,12 +94,13 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
if (!f)
- ret = error("Could not open %s for writing", filename);
+ ret = error_errno("Could not open %s for writing",
+ filename);
else if (result.size &&
fwrite(result.ptr, result.size, 1, f) != 1)
- ret = error("Could not write to %s", filename);
+ ret = error_errno("Could not write to %s", filename);
else if (fclose(f))
- ret = error("Could not close %s", filename);
+ ret = error_errno("Could not close %s", filename);
free(result.ptr);
}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index ca570041df..5b7ab9b967 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -150,15 +150,15 @@ static void show_result(void)
/* An empty entry never compares same, not even to another empty entry */
static int same_entry(struct name_entry *a, struct name_entry *b)
{
- return a->sha1 &&
- b->sha1 &&
- !hashcmp(a->sha1, b->sha1) &&
+ return a->oid &&
+ b->oid &&
+ !oidcmp(a->oid, b->oid) &&
a->mode == b->mode;
}
static int both_empty(struct name_entry *a, struct name_entry *b)
{
- return !(a->sha1 || b->sha1);
+ return !(a->oid || b->oid);
}
static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
@@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
return;
path = traverse_path(info, result);
- orig = create_entry(2, ours->mode, ours->sha1, path);
- final = create_entry(0, result->mode, result->sha1, path);
+ orig = create_entry(2, ours->mode, ours->oid->hash, path);
+ final = create_entry(0, result->mode, result->oid->hash, path);
final->link = orig;
@@ -213,7 +213,7 @@ static void unresolved_directory(const struct traverse_info *info,
newbase = traverse_path(info, p);
-#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->sha1 : NULL)
+#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL)
buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
@@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
path = entry->path;
else
path = traverse_path(info, n);
- link = create_entry(stage, n->mode, n->sha1, path);
+ link = create_entry(stage, n->mode, n->oid->hash, path);
link->link = entry;
return link;
}
@@ -314,7 +314,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
}
if (same_entry(entry+0, entry+1)) {
- if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
+ if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
/* We did not touch, they modified -- take theirs */
resolve(info, entry+1, entry+2);
return mask;
diff --git a/builtin/merge.c b/builtin/merge.c
index bf2f2614fb..b555a1bf9c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -64,6 +64,7 @@ static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
static int abort_current_merge;
+static int allow_unrelated_histories;
static int show_progress = -1;
static int default_to_upstream = 1;
static const char *sign_commit;
@@ -221,6 +222,8 @@ static struct option builtin_merge_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
+ OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
+ N_("allow merging unrelated histories")),
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
@@ -819,6 +822,14 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
unsigned char result_tree[20], result_commit[20];
struct commit_list *parents, **pptr = &parents;
+ static struct lock_file lock;
+
+ hold_locked_index(&lock, 1);
+ refresh_cache(REFRESH_QUIET);
+ if (active_cache_changed &&
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ return error(_("Unable to write index."));
+ rollback_lock_file(&lock);
write_tree_trivial(result_tree);
printf(_("Wonderful.\n"));
@@ -1165,7 +1176,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, i, ret = 0, head_subsumed;
+ int i, ret = 0, head_subsumed;
int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
@@ -1179,7 +1190,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1187,6 +1198,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+ init_diff_ui_defaults();
git_config(git_merge_config, NULL);
if (branch_mergeoptions)
@@ -1397,9 +1409,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
- if (remoteheads && !common)
- ; /* No common ancestors found. We need a real merge. */
- else if (!remoteheads ||
+ if (remoteheads && !common) {
+ /* No common ancestors found. */
+ if (!allow_unrelated_histories)
+ die(_("refusing to merge unrelated histories"));
+ /* otherwise, we need a real merge. */
+ } else if (!remoteheads ||
(!remoteheads->next && !common->next &&
common->item == remoteheads->item)) {
/*
diff --git a/builtin/notes.c b/builtin/notes.c
index 6fd058de92..c65b59ad9a 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -847,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
- char *existing;
+ const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
- existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
- if (existing)
+ wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+ if (wt)
die(_("A notes merge into %s is already in-progress at %s"),
- default_notes_ref(), existing);
+ default_notes_ref(), wt->path);
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
die("Failed to store link to current notes ref (%s)",
default_notes_ref());
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index b6664ce4d8..8f5e358e22 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -835,8 +835,7 @@ static void write_pack_file(void)
* to preserve this property.
*/
if (stat(pack_tmp_name, &st) < 0) {
- warning("failed to stat %s: %s",
- pack_tmp_name, strerror(errno));
+ warning_errno("failed to stat %s", pack_tmp_name);
} else if (!last_mtime) {
last_mtime = st.st_mtime;
} else {
@@ -844,8 +843,7 @@ static void write_pack_file(void)
utb.actime = st.st_atime;
utb.modtime = --last_mtime;
if (utime(pack_tmp_name, &utb) < 0)
- warning("failed utime() on %s: %s",
- pack_tmp_name, strerror(errno));
+ warning_errno("failed utime() on %s", pack_tmp_name);
}
strbuf_addf(&tmpname, "%s-", base_name);
@@ -1193,7 +1191,7 @@ static void add_pbase_object(struct tree_desc *tree,
if (cmp < 0)
return;
if (name[cmplen] != '/') {
- add_object_entry(entry.sha1,
+ add_object_entry(entry.oid->hash,
object_type(entry.mode),
fullname, 1);
return;
@@ -1204,7 +1202,7 @@ static void add_pbase_object(struct tree_desc *tree,
const char *down = name+cmplen+1;
int downlen = name_cmp_len(down);
- tree = pbase_tree_get(entry.sha1);
+ tree = pbase_tree_get(entry.oid->hash);
if (!tree)
return;
init_tree_desc(&sub, tree->tree_data, tree->tree_size);
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 366ce5a5d4..a84d0003a3 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -81,16 +81,13 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
char *line = line_buf->buf;
- char *p = line;
+ const char *p = line;
int len;
- if (!memcmp(line, "diff-tree ", 10))
- p += 10;
- else if (!memcmp(line, "commit ", 7))
- p += 7;
- else if (!memcmp(line, "From ", 5))
- p += 5;
- else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line))
+ if (!skip_prefix(line, "diff-tree ", &p) &&
+ !skip_prefix(line, "commit ", &p) &&
+ !skip_prefix(line, "From ", &p) &&
+ starts_with(line, "\\ ") && 12 < strlen(line))
continue;
if (!get_oid_hex(p, next_oid)) {
@@ -99,14 +96,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
}
/* Ignore commit comments */
- if (!patchlen && memcmp(line, "diff ", 5))
+ if (!patchlen && !starts_with(line, "diff "))
continue;
/* Parsing diff header? */
if (before == -1) {
- if (!memcmp(line, "index ", 6))
+ if (starts_with(line, "index "))
continue;
- else if (!memcmp(line, "--- ", 4))
+ else if (starts_with(line, "--- "))
before = after = 1;
else if (!isalpha(line[0]))
break;
@@ -114,14 +111,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
/* Looking for a valid hunk header? */
if (before == 0 && after == 0) {
- if (!memcmp(line, "@@ -", 4)) {
+ if (starts_with(line, "@@ -")) {
/* Parse next hunk, but ignore line numbers. */
scan_hunk_header(line, &before, &after);
continue;
}
/* Split at the end of the patch. */
- if (memcmp(line, "diff ", 5))
+ if (!starts_with(line, "diff "))
break;
/* Else we're parsing another header. */
diff --git a/builtin/pull.c b/builtin/pull.c
index 6214af9b10..1d7333c8a1 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -86,9 +86,12 @@ static char *opt_commit;
static char *opt_edit;
static char *opt_ff;
static char *opt_verify_signatures;
+static int opt_autostash = -1;
+static int config_autostash;
static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
static char *opt_gpg_sign;
+static int opt_allow_unrelated_histories;
/* Options passed to git-fetch */
static char *opt_all;
@@ -149,6 +152,8 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
+ OPT_BOOL(0, "autostash", &opt_autostash,
+ N_("automatically stash/stash pop before and after rebase")),
OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
N_("merge strategy to use"),
0),
@@ -159,6 +164,9 @@ static struct option pull_options[] = {
OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
N_("GPG sign commit"),
PARSE_OPT_OPTARG),
+ OPT_SET_INT(0, "allow-unrelated-histories",
+ &opt_allow_unrelated_histories,
+ N_("allow merging unrelated histories"), 1),
/* Options passed to git-fetch */
OPT_GROUP(N_("Options related to fetching")),
@@ -306,6 +314,18 @@ static enum rebase_type config_get_rebase(void)
}
/**
+ * Read config variables.
+ */
+static int git_pull_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "rebase.autostash")) {
+ config_autostash = git_config_bool(var, value);
+ return 0;
+ }
+ return git_default_config(var, value, cb);
+}
+
+/**
* Returns 1 if there are unstaged changes, 0 otherwise.
*/
static int has_unstaged_changes(const char *prefix)
@@ -612,6 +632,8 @@ static int run_merge(void)
argv_array_pushv(&args, opt_strategy_opts.argv);
if (opt_gpg_sign)
argv_array_push(&args, opt_gpg_sign);
+ if (opt_allow_unrelated_histories > 0)
+ argv_array_push(&args, "--allow-unrelated-histories");
argv_array_push(&args, "FETCH_HEAD");
ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
@@ -789,6 +811,10 @@ static int run_rebase(const unsigned char *curr_head,
argv_array_pushv(&args, opt_strategy_opts.argv);
if (opt_gpg_sign)
argv_array_push(&args, opt_gpg_sign);
+ if (opt_autostash == 0)
+ argv_array_push(&args, "--no-autostash");
+ else if (opt_autostash == 1)
+ argv_array_push(&args, "--autostash");
argv_array_push(&args, "--onto");
argv_array_push(&args, sha1_to_hex(merge_head));
@@ -823,7 +849,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (opt_rebase < 0)
opt_rebase = config_get_rebase();
- git_config(git_default_config, NULL);
+ git_config(git_pull_config, NULL);
if (read_cache_unmerged())
die_resolve_conflict("Pull");
@@ -834,13 +860,17 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (get_sha1("HEAD", orig_head))
hashclr(orig_head);
+ if (!opt_rebase && opt_autostash != -1)
+ die(_("--[no-]autostash option is only valid with --rebase."));
+
if (opt_rebase) {
- int autostash = 0;
+ int autostash = config_autostash;
+ if (opt_autostash != -1)
+ autostash = opt_autostash;
if (is_null_sha1(orig_head) && !is_cache_unborn())
die(_("Updating an unborn branch with changes added to the index."));
- git_config_get_bool("rebase.autostash", &autostash);
if (!autostash)
die_on_unclean_work_tree(prefix);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c8e32b297c..a744437b58 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -21,7 +21,10 @@
#include "sigchain.h"
#include "fsck.h"
-static const char receive_pack_usage[] = "git receive-pack <git-dir>";
+static const char * const receive_pack_usage[] = {
+ N_("git receive-pack <git-dir>"),
+ NULL
+};
enum deny_action {
DENY_UNCONFIGURED,
@@ -49,7 +52,7 @@ static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
-static int fix_thin = 1;
+static int reject_thin;
static int stateless_rpc;
static const char *service_dir;
static const char *head_name;
@@ -1081,13 +1084,13 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
if (!(flag & REF_ISSYMREF))
return;
- dst_name = strip_namespace(dst_name);
if (!dst_name) {
rp_error("refusing update to broken symref '%s'", cmd->ref_name);
cmd->skip_update = 1;
cmd->error_string = "broken symref";
return;
}
+ dst_name = strip_namespace(dst_name);
if ((item = string_list_lookup(list, dst_name)) == NULL)
return;
@@ -1548,7 +1551,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
if (fsck_objects)
argv_array_pushf(&child.args, "--strict%s",
fsck_msg_types.buf);
- if (fix_thin)
+ if (!reject_thin)
argv_array_push(&child.args, "--fix-thin");
child.out = -1;
child.err = err_fd;
@@ -1707,45 +1710,29 @@ static int delete_only(struct command *commands)
int cmd_receive_pack(int argc, const char **argv, const char *prefix)
{
int advertise_refs = 0;
- int i;
struct command *commands;
struct sha1_array shallow = SHA1_ARRAY_INIT;
struct sha1_array ref = SHA1_ARRAY_INIT;
struct shallow_info si;
- packet_trace_identity("receive-pack");
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("quiet")),
+ OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL),
+ OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL),
+ OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL),
+ OPT_END()
+ };
- argv++;
- for (i = 1; i < argc; i++) {
- const char *arg = *argv++;
+ packet_trace_identity("receive-pack");
- if (*arg == '-') {
- if (!strcmp(arg, "--quiet")) {
- quiet = 1;
- continue;
- }
+ argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
- if (!strcmp(arg, "--advertise-refs")) {
- advertise_refs = 1;
- continue;
- }
- if (!strcmp(arg, "--stateless-rpc")) {
- stateless_rpc = 1;
- continue;
- }
- if (!strcmp(arg, "--reject-thin-pack-for-testing")) {
- fix_thin = 0;
- continue;
- }
+ if (argc > 1)
+ usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
+ if (argc == 0)
+ usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
- usage(receive_pack_usage);
- }
- if (service_dir)
- usage(receive_pack_usage);
- service_dir = arg;
- }
- if (!service_dir)
- usage(receive_pack_usage);
+ service_dir = argv[0];
setup_path();
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 2d46b6482a..7a7136e53e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -84,8 +84,8 @@ static int tree_is_complete(const unsigned char *sha1)
init_tree_desc(&desc, tree->buffer, tree->size);
complete = 1;
while (tree_entry(&desc, &entry)) {
- if (!has_sha1_file(entry.sha1) ||
- (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
+ if (!has_sha1_file(entry.oid->hash) ||
+ (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) {
tree->object.flags |= INCOMPLETE;
complete = 0;
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 275da0d647..b82bcc3436 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -358,12 +358,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (use_bitmap_index && !revs.prune) {
if (revs.count && !revs.left_right && !revs.cherry_mark) {
uint32_t commit_count;
+ int max_count = revs.max_count;
if (!prepare_bitmap_walk(&revs)) {
count_bitmap_commit_list(&commit_count, NULL, NULL, NULL);
+ if (max_count >= 0 && max_count < commit_count)
+ commit_count = max_count;
printf("%d\n", commit_count);
return 0;
}
- } else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) {
+ } else if (revs.max_count < 0 &&
+ revs.tag_objects && revs.tree_objects && revs.blob_objects) {
if (!prepare_bitmap_walk(&revs)) {
traverse_bitmap_commit_list(&show_object_fast);
return 0;
diff --git a/builtin/rm.c b/builtin/rm.c
index be83c4347a..8abb0207fa 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -152,7 +152,7 @@ static int check_local_mod(unsigned char *head, int index_only)
if (lstat(ce->name, &st) < 0) {
if (errno != ENOENT && errno != ENOTDIR)
- warning("'%s': %s", ce->name, strerror(errno));
+ warning_errno(_("failed to stat '%s'"), ce->name);
/* It already vanished from the working tree */
continue;
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3bea3aaa50..926d205162 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -9,6 +9,211 @@
#include "submodule-config.h"
#include "string-list.h"
#include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+#include "connect.h"
+
+static char *get_default_remote(void)
+{
+ char *dest = NULL, *ret;
+ unsigned char sha1[20];
+ struct strbuf sb = STRBUF_INIT;
+ const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+
+ if (!refname)
+ die(_("No such ref: %s"), "HEAD");
+
+ /* detached HEAD */
+ if (!strcmp(refname, "HEAD"))
+ return xstrdup("origin");
+
+ if (!skip_prefix(refname, "refs/heads/", &refname))
+ die(_("Expecting a full ref name, got %s"), refname);
+
+ strbuf_addf(&sb, "branch.%s.remote", refname);
+ if (git_config_get_string(sb.buf, &dest))
+ ret = xstrdup("origin");
+ else
+ ret = dest;
+
+ strbuf_release(&sb);
+ return ret;
+}
+
+static int starts_with_dot_slash(const char *str)
+{
+ return str[0] == '.' && is_dir_sep(str[1]);
+}
+
+static int starts_with_dot_dot_slash(const char *str)
+{
+ return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+}
+
+/*
+ * Returns 1 if it was the last chop before ':'.
+ */
+static int chop_last_dir(char **remoteurl, int is_relative)
+{
+ char *rfind = find_last_dir_sep(*remoteurl);
+ if (rfind) {
+ *rfind = '\0';
+ return 0;
+ }
+
+ rfind = strrchr(*remoteurl, ':');
+ if (rfind) {
+ *rfind = '\0';
+ return 1;
+ }
+
+ if (is_relative || !strcmp(".", *remoteurl))
+ die(_("cannot strip one component off url '%s'"),
+ *remoteurl);
+
+ free(*remoteurl);
+ *remoteurl = xstrdup(".");
+ return 0;
+}
+
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ *
+ * NEEDSWORK: This works incorrectly on the domain and protocol part.
+ * remote_url url outcome expectation
+ * http://a.com/b ../c http://a.com/c as is
+ * http://a.com/b ../../c http://c error out
+ * http://a.com/b ../../../c http:/c error out
+ * http://a.com/b ../../../../c http:c error out
+ * http://a.com/b ../../../../../c .:c error out
+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken
+ * when a local part has a colon in its path component, too.
+ */
+static char *relative_url(const char *remote_url,
+ const char *url,
+ const char *up_path)
+{
+ int is_relative = 0;
+ int colonsep = 0;
+ char *out;
+ char *remoteurl = xstrdup(remote_url);
+ struct strbuf sb = STRBUF_INIT;
+ size_t len = strlen(remoteurl);
+
+ if (is_dir_sep(remoteurl[len]))
+ remoteurl[len] = '\0';
+
+ if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+ is_relative = 0;
+ else {
+ is_relative = 1;
+ /*
+ * Prepend a './' to ensure all relative
+ * remoteurls start with './' or '../'
+ */
+ if (!starts_with_dot_slash(remoteurl) &&
+ !starts_with_dot_dot_slash(remoteurl)) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "./%s", remoteurl);
+ free(remoteurl);
+ remoteurl = strbuf_detach(&sb, NULL);
+ }
+ }
+ /*
+ * When the url starts with '../', remove that and the
+ * last directory in remoteurl.
+ */
+ while (url) {
+ if (starts_with_dot_dot_slash(url)) {
+ url += 3;
+ colonsep |= chop_last_dir(&remoteurl, is_relative);
+ } else if (starts_with_dot_slash(url))
+ url += 2;
+ else
+ break;
+ }
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+ free(remoteurl);
+
+ if (starts_with_dot_slash(sb.buf))
+ out = xstrdup(sb.buf + 2);
+ else
+ out = xstrdup(sb.buf);
+ strbuf_reset(&sb);
+
+ if (!up_path || !is_relative)
+ return out;
+
+ strbuf_addf(&sb, "%s%s", up_path, out);
+ free(out);
+ return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+ char *remoteurl = NULL;
+ char *remote = get_default_remote();
+ const char *up_path = NULL;
+ char *res;
+ const char *url;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (argc != 2 && argc != 3)
+ die("resolve-relative-url only accepts one or two arguments");
+
+ url = argv[1];
+ strbuf_addf(&sb, "remote.%s.url", remote);
+ free(remote);
+
+ if (git_config_get_string(sb.buf, &remoteurl))
+ /* the repository is its own authoritative upstream */
+ remoteurl = xgetcwd();
+
+ if (argc == 3)
+ up_path = argv[2];
+
+ res = relative_url(remoteurl, url, up_path);
+ puts(res);
+ free(res);
+ free(remoteurl);
+ return 0;
+}
+
+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
+{
+ char *remoteurl, *res;
+ const char *up_path, *url;
+
+ if (argc != 4)
+ die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>");
+
+ up_path = argv[1];
+ remoteurl = xstrdup(argv[2]);
+ url = argv[3];
+
+ if (!strcmp(up_path, "(null)"))
+ up_path = NULL;
+
+ res = relative_url(remoteurl, url, up_path);
+ puts(res);
+ free(res);
+ free(remoteurl);
+ return 0;
+}
struct module_list {
const struct cache_entry **entries;
@@ -82,10 +287,8 @@ static int module_list(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, module_list_options,
git_submodule_helper_usage, 0);
- if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
- printf("#unmatched\n");
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
return 1;
- }
for (i = 0; i < list.nr; i++) {
const struct cache_entry *ce = list.entries[i];
@@ -100,6 +303,125 @@ static int module_list(int argc, const char **argv, const char *prefix)
return 0;
}
+static void init_submodule(const char *path, const char *prefix, int quiet)
+{
+ const struct submodule *sub;
+ struct strbuf sb = STRBUF_INIT;
+ char *upd = NULL, *url = NULL, *displaypath;
+
+ /* Only loads from .gitmodules, no overlay with .git/config */
+ gitmodules_config();
+
+ if (prefix) {
+ strbuf_addf(&sb, "%s%s", prefix, path);
+ displaypath = strbuf_detach(&sb, NULL);
+ } else
+ displaypath = xstrdup(path);
+
+ sub = submodule_from_path(null_sha1, path);
+
+ if (!sub)
+ die(_("No url found for submodule path '%s' in .gitmodules"),
+ displaypath);
+
+ /*
+ * Copy url setting when it is not set yet.
+ * To look up the url in .git/config, we must not fall back to
+ * .gitmodules, so look it up directly.
+ */
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ if (git_config_get_string(sb.buf, &url)) {
+ url = xstrdup(sub->url);
+
+ if (!url)
+ die(_("No url found for submodule path '%s' in .gitmodules"),
+ displaypath);
+
+ /* Possibly a url relative to parent */
+ if (starts_with_dot_dot_slash(url) ||
+ starts_with_dot_slash(url)) {
+ char *remoteurl, *relurl;
+ char *remote = get_default_remote();
+ struct strbuf remotesb = STRBUF_INIT;
+ strbuf_addf(&remotesb, "remote.%s.url", remote);
+ free(remote);
+
+ if (git_config_get_string(remotesb.buf, &remoteurl))
+ /*
+ * The repository is its own
+ * authoritative upstream
+ */
+ remoteurl = xgetcwd();
+ relurl = relative_url(remoteurl, url, NULL);
+ strbuf_release(&remotesb);
+ free(remoteurl);
+ free(url);
+ url = relurl;
+ }
+
+ if (git_config_set_gently(sb.buf, url))
+ die(_("Failed to register url for submodule path '%s'"),
+ displaypath);
+ if (!quiet)
+ fprintf(stderr,
+ _("Submodule '%s' (%s) registered for path '%s'\n"),
+ sub->name, url, displaypath);
+ }
+
+ /* Copy "update" setting when it is not set yet */
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.update", sub->name);
+ if (git_config_get_string(sb.buf, &upd) &&
+ sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+ if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
+ fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
+ sub->name);
+ upd = xstrdup("none");
+ } else
+ upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+
+ if (git_config_set_gently(sb.buf, upd))
+ die(_("Failed to register update mode for submodule path '%s'"), displaypath);
+ }
+ strbuf_release(&sb);
+ free(displaypath);
+ free(url);
+ free(upd);
+}
+
+static int module_init(int argc, const char **argv, const char *prefix)
+{
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int i;
+
+ struct option module_init_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("alternative anchor for relative paths")),
+ OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper init [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_init_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ for (i = 0; i < list.nr; i++)
+ init_submodule(list.entries[i]->name, prefix, quiet);
+
+ return 0;
+}
+
static int module_name(int argc, const char **argv, const char *prefix)
{
const struct submodule *sub;
@@ -118,6 +440,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
return 0;
}
+
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, const char *reference, int quiet)
{
@@ -139,7 +462,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
argv_array_push(&cp.args, path);
cp.git_cmd = 1;
- cp.env = local_repo_env;
+ prepare_submodule_repo_env(&cp.env_array);
cp.no_stdin = 1;
return run_command(&cp);
@@ -180,16 +503,17 @@ static int module_clone(int argc, const char **argv, const char *prefix)
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
- "[--reference <repository>] [--name <name>] [--url <url>]"
- "[--depth <depth>] [--] [<path>...]"),
+ "[--reference <repository>] [--name <name>] [--depth <depth>] "
+ "--url <url> --path <path>"),
NULL
};
argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0);
- if (!path || !*path)
- die(_("submodule--helper: unspecified or empty --path"));
+ if (argc || !url || !path || !*path)
+ usage_with_options(git_submodule_helper_usage,
+ module_clone_options);
strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
sm_gitdir = xstrdup(absolute_path(sb.buf));
@@ -244,6 +568,280 @@ static int module_clone(int argc, const char **argv, const char *prefix)
return 0;
}
+struct submodule_update_clone {
+ /* index into 'list', the list of submodules to look into for cloning */
+ int current;
+ struct module_list list;
+ unsigned warn_if_uninitialized : 1;
+
+ /* update parameter passed via commandline */
+ struct submodule_update_strategy update;
+
+ /* configuration parameters which are passed on to the children */
+ int quiet;
+ const char *reference;
+ const char *depth;
+ const char *recursive_prefix;
+ const char *prefix;
+
+ /* Machine-readable status lines to be consumed by git-submodule.sh */
+ struct string_list projectlines;
+
+ /* If we want to stop as fast as possible and return an error */
+ unsigned quickstop : 1;
+};
+#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
+ SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \
+ STRING_LIST_INIT_DUP, 0}
+
+
+static void next_submodule_warn_missing(struct submodule_update_clone *suc,
+ struct strbuf *out, const char *displaypath)
+{
+ /*
+ * Only mention uninitialized submodules when their
+ * paths have been specified.
+ */
+ if (suc->warn_if_uninitialized) {
+ strbuf_addf(out,
+ _("Submodule path '%s' not initialized"),
+ displaypath);
+ strbuf_addch(out, '\n');
+ strbuf_addstr(out,
+ _("Maybe you want to use 'update --init'?"));
+ strbuf_addch(out, '\n');
+ }
+}
+
+/**
+ * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to
+ * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise.
+ */
+static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
+ struct child_process *child,
+ struct submodule_update_clone *suc,
+ struct strbuf *out)
+{
+ const struct submodule *sub = NULL;
+ struct strbuf displaypath_sb = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ const char *displaypath = NULL;
+ char *url = NULL;
+ int needs_cloning = 0;
+
+ if (ce_stage(ce)) {
+ if (suc->recursive_prefix)
+ strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
+ else
+ strbuf_addf(&sb, "%s", ce->name);
+ strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
+ strbuf_addch(out, '\n');
+ goto cleanup;
+ }
+
+ sub = submodule_from_path(null_sha1, ce->name);
+
+ if (suc->recursive_prefix)
+ displaypath = relative_path(suc->recursive_prefix,
+ ce->name, &displaypath_sb);
+ else
+ displaypath = ce->name;
+
+ if (!sub) {
+ next_submodule_warn_missing(suc, out, displaypath);
+ goto cleanup;
+ }
+
+ if (suc->update.type == SM_UPDATE_NONE
+ || (suc->update.type == SM_UPDATE_UNSPECIFIED
+ && sub->update_strategy.type == SM_UPDATE_NONE)) {
+ strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
+ strbuf_addch(out, '\n');
+ goto cleanup;
+ }
+
+ /*
+ * Looking up the url in .git/config.
+ * We must not fall back to .gitmodules as we only want
+ * to process configured submodules.
+ */
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ git_config_get_string(sb.buf, &url);
+ if (!url) {
+ next_submodule_warn_missing(suc, out, displaypath);
+ goto cleanup;
+ }
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/.git", ce->name);
+ needs_cloning = !file_exists(sb.buf);
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
+ sha1_to_hex(ce->sha1), ce_stage(ce),
+ needs_cloning, ce->name);
+ string_list_append(&suc->projectlines, sb.buf);
+
+ if (!needs_cloning)
+ goto cleanup;
+
+ child->git_cmd = 1;
+ child->no_stdin = 1;
+ child->stdout_to_stderr = 1;
+ child->err = -1;
+ argv_array_push(&child->args, "submodule--helper");
+ argv_array_push(&child->args, "clone");
+ if (suc->quiet)
+ argv_array_push(&child->args, "--quiet");
+ if (suc->prefix)
+ argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+ argv_array_pushl(&child->args, "--path", sub->path, NULL);
+ argv_array_pushl(&child->args, "--name", sub->name, NULL);
+ argv_array_pushl(&child->args, "--url", url, NULL);
+ if (suc->reference)
+ argv_array_push(&child->args, suc->reference);
+ if (suc->depth)
+ argv_array_push(&child->args, suc->depth);
+
+cleanup:
+ free(url);
+ strbuf_reset(&displaypath_sb);
+ strbuf_reset(&sb);
+
+ return needs_cloning;
+}
+
+static int update_clone_get_next_task(struct child_process *child,
+ struct strbuf *err,
+ void *suc_cb,
+ void **void_task_cb)
+{
+ struct submodule_update_clone *suc = suc_cb;
+
+ for (; suc->current < suc->list.nr; suc->current++) {
+ const struct cache_entry *ce = suc->list.entries[suc->current];
+ if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+ suc->current++;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int update_clone_start_failure(struct strbuf *err,
+ void *suc_cb,
+ void *void_task_cb)
+{
+ struct submodule_update_clone *suc = suc_cb;
+ suc->quickstop = 1;
+ return 1;
+}
+
+static int update_clone_task_finished(int result,
+ struct strbuf *err,
+ void *suc_cb,
+ void *void_task_cb)
+{
+ struct submodule_update_clone *suc = suc_cb;
+
+ if (!result)
+ return 0;
+
+ suc->quickstop = 1;
+ return 1;
+}
+
+static int update_clone(int argc, const char **argv, const char *prefix)
+{
+ const char *update = NULL;
+ int max_jobs = -1;
+ struct string_list_item *item;
+ struct pathspec pathspec;
+ struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+
+ struct option module_update_clone_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("path into the working tree")),
+ OPT_STRING(0, "recursive-prefix", &suc.recursive_prefix,
+ N_("path"),
+ N_("path into the working tree, across nested "
+ "submodule boundaries")),
+ OPT_STRING(0, "update", &update,
+ N_("string"),
+ N_("rebase, merge, checkout or none")),
+ OPT_STRING(0, "reference", &suc.reference, N_("repo"),
+ N_("reference repository")),
+ OPT_STRING(0, "depth", &suc.depth, "<depth>",
+ N_("Create a shallow clone truncated to the "
+ "specified number of revisions")),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("parallel jobs")),
+ OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
+ NULL
+ };
+ suc.prefix = prefix;
+
+ argc = parse_options(argc, argv, prefix, module_update_clone_options,
+ git_submodule_helper_usage, 0);
+
+ if (update)
+ if (parse_submodule_update_strategy(update, &suc.update) < 0)
+ die(_("bad value for update parameter"));
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0)
+ return 1;
+
+ if (pathspec.nr)
+ suc.warn_if_uninitialized = 1;
+
+ /* Overlay the parsed .gitmodules file with .git/config */
+ gitmodules_config();
+ git_config(submodule_config, NULL);
+
+ if (max_jobs < 0)
+ max_jobs = parallel_submodules();
+
+ run_processes_parallel(max_jobs,
+ update_clone_get_next_task,
+ update_clone_start_failure,
+ update_clone_task_finished,
+ &suc);
+
+ /*
+ * We saved the output and put it out all at once now.
+ * That means:
+ * - the listener does not have to interleave their (checkout)
+ * work with our fetching. The writes involved in a
+ * checkout involve more straightforward sequential I/O.
+ * - the listener can avoid doing any work if fetching failed.
+ */
+ if (suc.quickstop)
+ return 1;
+
+ for_each_string_list_item(item, &suc.projectlines)
+ utf8_fprintf(stdout, "%s", item->string);
+
+ return 0;
+}
+
+static int resolve_relative_path(int argc, const char **argv, const char *prefix)
+{
+ struct strbuf sb = STRBUF_INIT;
+ if (argc != 3)
+ die("submodule--helper relative_path takes exactly 2 arguments, got %d", argc);
+
+ printf("%s", relative_path(argv[1], argv[2], &sb));
+ strbuf_release(&sb);
+ return 0;
+}
+
struct cmd_struct {
const char *cmd;
int (*fn)(int, const char **, const char *);
@@ -253,19 +851,24 @@ static struct cmd_struct commands[] = {
{"list", module_list},
{"name", module_name},
{"clone", module_clone},
+ {"update-clone", update_clone},
+ {"relative-path", resolve_relative_path},
+ {"resolve-relative-url", resolve_relative_url},
+ {"resolve-relative-url-test", resolve_relative_url_test},
+ {"init", module_init}
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
{
int i;
if (argc < 2)
- die(_("fatal: submodule--helper subcommand must be "
+ die(_("submodule--helper subcommand must be "
"called with a subcommand"));
for (i = 0; i < ARRAY_SIZE(commands); i++)
if (!strcmp(argv[1], commands[i].cmd))
return commands[i].fn(argc - 1, argv + 1, prefix);
- die(_("fatal: '%s' is not a valid submodule--helper "
+ die(_("'%s' is not a valid submodule--helper "
"subcommand"), argv[1]);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index 1705c94665..50e4ae5678 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -29,6 +29,7 @@ static const char * const git_tag_usage[] = {
};
static unsigned int colopts;
+static int force_sign_annotate;
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
{
@@ -104,13 +105,7 @@ static int delete_tag(const char *name, const char *ref,
static int verify_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
- const char *argv_verify_tag[] = {"verify-tag",
- "-v", "SHA1_HEX", NULL};
- argv_verify_tag[2] = sha1_to_hex(sha1);
-
- if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
- return error(_("could not verify the tag '%s'"), name);
- return 0;
+ return gpg_verify_tag(sha1, name, GPG_VERIFY_VERBOSE);
}
static int do_sign(struct strbuf *buffer)
@@ -166,6 +161,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
status = git_gpg_config(var, value, cb);
if (status)
return status;
+ if (!strcmp(var, "tag.forcesignannotated")) {
+ force_sign_annotate = git_config_bool(var, value);
+ return 0;
+ }
+
if (starts_with(var, "column."))
return git_column_config(var, value, "tag", &colopts);
return git_default_config(var, value, cb);
@@ -327,7 +327,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
char *cleanup_arg = NULL;
int create_reflog = 0;
int annotate = 0, force = 0;
- int cmdmode = 0;
+ int cmdmode = 0, create_tag_object = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct ref_transaction *transaction;
@@ -385,12 +385,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
set_signing_key(keyid);
}
- if (opt.sign)
- annotate = 1;
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
if (argc == 0 && !cmdmode)
cmdmode = 'l';
- if ((annotate || msg.given || msgfile || force) && (cmdmode != 0))
+ if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
finalize_colopts(&colopts, -1);
@@ -431,7 +431,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (msg.given || msgfile) {
if (msg.given && msgfile)
die(_("only one -F or -m option is allowed."));
- annotate = 1;
if (msg.given)
strbuf_addbuf(&buf, &(msg.buf));
else {
@@ -474,8 +473,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
else
die(_("Invalid cleanup mode %s"), cleanup_arg);
- if (annotate)
+ if (create_tag_object) {
+ if (force_sign_annotate && !annotate)
+ opt.sign = 1;
create_tag(object, tag, &buf, &opt, prev, object);
+ }
transaction = ref_transaction_begin(&err);
if (!transaction ||
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 1c94ca59bf..b8b8522249 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -255,7 +255,7 @@ static int process_lstat_error(const char *path, int err)
{
if (err == ENOENT || err == ENOTDIR)
return remove_one_path(path);
- return error("lstat(\"%s\"): %s", path, strerror(errno));
+ return error("lstat(\"%s\"): %s", path, strerror(err));
}
static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index dbfe14f3fe..2caedf1849 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -104,8 +104,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
pfd[1].events = POLLIN;
if (poll(pfd, 2, -1) < 0) {
if (errno != EINTR) {
- error("poll failed resuming: %s",
- strerror(errno));
+ error_errno("poll failed resuming");
sleep(1);
}
continue;
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 00663f6a30..99f8148cf7 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -18,55 +18,6 @@ static const char * const verify_tag_usage[] = {
NULL
};
-static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
-{
- struct signature_check sigc;
- int len;
- int ret;
-
- memset(&sigc, 0, sizeof(sigc));
-
- len = parse_signature(buf, size);
-
- if (size == len) {
- if (flags & GPG_VERIFY_VERBOSE)
- write_in_full(1, buf, len);
- return error("no signature found");
- }
-
- ret = check_signature(buf, len, buf + len, size - len, &sigc);
- print_signature_buffer(&sigc, flags);
-
- signature_check_clear(&sigc);
- return ret;
-}
-
-static int verify_tag(const char *name, unsigned flags)
-{
- enum object_type type;
- unsigned char sha1[20];
- char *buf;
- unsigned long size;
- int ret;
-
- if (get_sha1(name, sha1))
- return error("tag '%s' not found.", name);
-
- type = sha1_object_info(sha1, NULL);
- if (type != OBJ_TAG)
- return error("%s: cannot verify a non-tag object of type %s.",
- name, typename(type));
-
- buf = read_sha1_file(sha1, &type, &size);
- if (!buf)
- return error("%s: unable to read file.", name);
-
- ret = run_gpg_verify(buf, size, flags);
-
- free(buf);
- return ret;
-}
-
static int git_verify_tag_config(const char *var, const char *value, void *cb)
{
int status = git_gpg_config(var, value, cb);
@@ -95,11 +46,13 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
if (verbose)
flags |= GPG_VERIFY_VERBOSE;
- /* sometimes the program was terminated because this signal
- * was received in the process of writing the gpg input: */
- signal(SIGPIPE, SIG_IGN);
- while (i < argc)
- if (verify_tag(argv[i++], flags))
+ while (i < argc) {
+ unsigned char sha1[20];
+ const char *name = argv[i++];
+ if (get_sha1(name, sha1))
+ had_error = !!error("tag '%s' not found.", name);
+ else if (gpg_verify_tag(sha1, name, flags))
had_error = 1;
+ }
return had_error;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 38b56096bd..96a2834a18 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -21,6 +21,7 @@ static const char * const worktree_usage[] = {
struct add_opts {
int force;
int detach;
+ int checkout;
const char *new_branch;
int force_new_branch;
};
@@ -109,7 +110,7 @@ static void prune_worktrees(void)
if (ret < 0 && errno == ENOTDIR)
ret = unlink(path.buf);
if (ret)
- error(_("failed to remove: %s"), strerror(errno));
+ error_errno(_("failed to remove '%s'"), path.buf);
}
closedir(dir);
if (!show_only)
@@ -204,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
ref_exists(symref.buf)) { /* it's a branch */
if (!opts->force)
- die_if_checked_out(symref.buf);
+ die_if_checked_out(symref.buf, 0);
} else { /* must be a commit */
commit = lookup_commit_reference_by_name(refname);
if (!commit)
@@ -284,18 +285,22 @@ static int add_worktree(const char *path, const char *refname,
if (ret)
goto done;
- cp.argv = NULL;
- argv_array_clear(&cp.args);
- argv_array_pushl(&cp.args, "reset", "--hard", NULL);
- cp.env = child_env.argv;
- ret = run_command(&cp);
- if (!ret) {
- is_junk = 0;
- free(junk_work_tree);
- free(junk_git_dir);
- junk_work_tree = NULL;
- junk_git_dir = NULL;
+ if (opts->checkout) {
+ cp.argv = NULL;
+ argv_array_clear(&cp.args);
+ argv_array_pushl(&cp.args, "reset", "--hard", NULL);
+ cp.env = child_env.argv;
+ ret = run_command(&cp);
+ if (ret)
+ goto done;
}
+
+ is_junk = 0;
+ free(junk_work_tree);
+ free(junk_git_dir);
+ junk_work_tree = NULL;
+ junk_git_dir = NULL;
+
done:
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
@@ -320,10 +325,12 @@ static int add(int ac, const char **av, const char *prefix)
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
+ OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_END()
};
memset(&opts, 0, sizeof(opts));
+ opts.checkout = 1;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)
die(_("-b, -B, and --detach are mutually exclusive"));
@@ -342,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
if (!opts.force &&
!strbuf_check_branch_ref(&symref, opts.new_branch) &&
ref_exists(symref.buf))
- die_if_checked_out(symref.buf);
+ die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
}