summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c4
-rw-r--r--builtin/apply.c7
-rw-r--r--builtin/checkout.c262
-rw-r--r--builtin/clone.c2
-rw-r--r--builtin/commit.c172
-rw-r--r--builtin/config.c35
-rw-r--r--builtin/describe.c4
-rw-r--r--builtin/diff-files.c2
-rw-r--r--builtin/diff.c16
-rw-r--r--builtin/fast-export.c6
-rw-r--r--builtin/fetch.c6
-rw-r--r--builtin/grep.c200
-rw-r--r--builtin/hash-object.c2
-rw-r--r--builtin/log.c2
-rw-r--r--builtin/merge.c52
-rw-r--r--builtin/notes.c16
-rw-r--r--builtin/patch-id.c2
-rw-r--r--builtin/push.c10
-rw-r--r--builtin/read-tree.c4
-rw-r--r--builtin/rerere.c14
-rw-r--r--builtin/revert.c104
-rw-r--r--builtin/tag.c2
-rw-r--r--builtin/update-index.c8
23 files changed, 505 insertions, 427 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 42c906ea06..f7a17e43f6 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -86,7 +86,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
struct rev_info rev;
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
- rev.prune_data = pathspec;
+ init_pathspec(&rev.prune_data, pathspec);
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
data.flags = flags;
@@ -322,7 +322,7 @@ static struct option builtin_add_options[] = {
OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
- OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
+ OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked 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_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
diff --git a/builtin/apply.c b/builtin/apply.c
index a231c0c7ca..36e150768b 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -204,6 +204,7 @@ struct line {
unsigned hash : 24;
unsigned flag : 8;
#define LINE_COMMON 1
+#define LINE_PATCHED 2
};
/*
@@ -2085,7 +2086,8 @@ static int match_fragment(struct image *img,
/* Quick hash check */
for (i = 0; i < preimage_limit; i++)
- if (preimage->line[i].hash != img->line[try_lno + i].hash)
+ if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
+ (preimage->line[i].hash != img->line[try_lno + i].hash))
return 0;
if (preimage_limit == preimage->nr) {
@@ -2428,6 +2430,9 @@ static void update_image(struct image *img,
memcpy(img->line + applied_pos,
postimage->line,
postimage->nr * sizeof(*img->line));
+ for (i = 0; i < postimage->nr; i++)
+ img->line[applied_pos + i].flag |= LINE_PATCHED;
+
img->nr = nr;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cd7f56e6c4..cc97dbc30f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -30,6 +30,7 @@ struct checkout_opts {
int quiet;
int merge;
int force;
+ int force_detach;
int writeout_stage;
int writeout_error;
@@ -297,7 +298,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
run_diff_index(&rev, 0);
}
-static void describe_detached_head(char *msg, struct commit *commit)
+static void describe_detached_head(const char *msg, struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
struct pretty_print_context ctx = {0};
@@ -541,7 +542,17 @@ static void update_refs_for_switch(struct checkout_opts *opts,
strbuf_addf(&msg, "checkout: moving from %s to %s",
old_desc ? old_desc : "(invalid)", new->name);
- if (new->path) {
+ if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
+ /* Nothing to do. */
+ } else if (opts->force_detach || !new->path) { /* No longer on any branch. */
+ update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+ REF_NODEREF, DIE_ON_ERR);
+ if (!opts->quiet) {
+ if (old->path && advice_detached_head)
+ detach_advice(old->path, new->name);
+ describe_detached_head("HEAD is now at", new->commit);
+ }
+ } else if (new->path) { /* Switch branches. */
create_symref("HEAD", new->path, msg.buf);
if (!opts->quiet) {
if (old->path && !strcmp(new->path, old->path))
@@ -563,18 +574,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,
if (!file_exists(ref_file) && file_exists(log_file))
remove_path(log_file);
}
- } else if (strcmp(new->name, "HEAD")) {
- update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
- REF_NODEREF, DIE_ON_ERR);
- if (!opts->quiet) {
- if (old->path && advice_detached_head)
- detach_advice(old->path, new->name);
- describe_detached_head("HEAD is now at", new->commit);
- }
}
remove_branch_state();
strbuf_release(&msg);
- if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
+ if (!opts->quiet &&
+ (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
report_tracking(new);
}
@@ -675,11 +679,123 @@ static const char *unique_tracking_name(const char *name)
return NULL;
}
+static int parse_branchname_arg(int argc, const char **argv,
+ int dwim_new_local_branch_ok,
+ struct branch_info *new,
+ struct tree **source_tree,
+ unsigned char rev[20],
+ const char **new_branch)
+{
+ int argcount = 0;
+ unsigned char branch_rev[20];
+ const char *arg;
+ int has_dash_dash;
+
+ /*
+ * case 1: git checkout <ref> -- [<paths>]
+ *
+ * <ref> must be a valid tree, everything after the '--' must be
+ * a path.
+ *
+ * case 2: git checkout -- [<paths>]
+ *
+ * everything after the '--' must be paths.
+ *
+ * case 3: git checkout <something> [<paths>]
+ *
+ * With no paths, if <something> is a commit, that is to
+ * switch to the branch or detach HEAD at it. As a special case,
+ * if <something> is A...B (missing A or B means HEAD but you can
+ * omit at most one side), and if there is a unique merge base
+ * between A and B, A...B names that merge base.
+ *
+ * With no paths, if <something> is _not_ a commit, no -t nor -b
+ * was given, and there is a tracking branch whose name is
+ * <something> in one and only one remote, then this is a short-hand
+ * to fork local <something> from that remote-tracking branch.
+ *
+ * Otherwise <something> shall not be ambiguous.
+ * - If it's *only* a reference, treat it like case (1).
+ * - If it's only a path, treat it like case (2).
+ * - else: fail.
+ *
+ */
+ if (!argc)
+ return 0;
+
+ if (!strcmp(argv[0], "--")) /* case (2) */
+ return 1;
+
+ arg = argv[0];
+ has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+
+ if (!strcmp(arg, "-"))
+ arg = "@{-1}";
+
+ if (get_sha1_mb(arg, rev)) {
+ if (has_dash_dash) /* case (1) */
+ die("invalid reference: %s", arg);
+ if (dwim_new_local_branch_ok &&
+ !check_filename(NULL, arg) &&
+ argc == 1) {
+ const char *remote = unique_tracking_name(arg);
+ if (!remote || get_sha1(remote, rev))
+ return argcount;
+ *new_branch = arg;
+ arg = remote;
+ /* DWIMmed to create local branch */
+ } else {
+ return argcount;
+ }
+ }
+
+ /* we can't end up being in (2) anymore, eat the argument */
+ argcount++;
+ argv++;
+ argc--;
+
+ new->name = arg;
+ setup_branch_path(new);
+
+ if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+ resolve_ref(new->path, branch_rev, 1, NULL))
+ hashcpy(rev, branch_rev);
+ else
+ new->path = NULL; /* not an existing branch */
+
+ new->commit = lookup_commit_reference_gently(rev, 1);
+ if (!new->commit) {
+ /* not a commit */
+ *source_tree = parse_tree_indirect(rev);
+ } else {
+ parse_commit(new->commit);
+ *source_tree = new->commit->tree;
+ }
+
+ if (!*source_tree) /* case (1): want a tree */
+ die("reference is not a tree: %s", arg);
+ if (!has_dash_dash) {/* case (3 -> 1) */
+ /*
+ * Do not complain the most common case
+ * git checkout branch
+ * even if there happen to be a file called 'branch';
+ * it would be extremely annoying.
+ */
+ if (argc)
+ verify_non_filename(NULL, arg);
+ } else {
+ argcount++;
+ argv++;
+ argc--;
+ }
+
+ return argcount;
+}
+
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
unsigned char rev[20];
- const char *arg;
struct branch_info new;
struct tree *source_tree = NULL;
char *conflict_style = NULL;
@@ -692,6 +808,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
"create/reset and checkout a branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
+ OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"),
OPT_SET_INT('t', "track", &opts.track, "set upstream info for new branch",
BRANCH_TRACK_EXPLICIT),
OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
@@ -709,7 +826,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
OPT_END(),
};
- int has_dash_dash;
memset(&opts, 0, sizeof(opts));
memset(&new, 0, sizeof(new));
@@ -731,9 +847,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.new_branch = opts.new_branch_force;
if (patch_mode && (opts.track > 0 || opts.new_branch
- || opts.new_branch_log || opts.merge || opts.force))
+ || opts.new_branch_log || opts.merge || opts.force
+ || opts.force_detach))
die ("--patch is incompatible with all other options");
+ if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
+ die("--detach cannot be used with -b/-B/--orphan");
+ if (opts.force_detach && 0 < opts.track)
+ die("--detach cannot be used with -t");
+
/* --track without -b should DWIM */
if (0 < opts.track && !opts.new_branch) {
const char *argv0 = argv[0];
@@ -766,105 +888,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
die("git checkout: -f and -m are incompatible");
/*
- * case 1: git checkout <ref> -- [<paths>]
- *
- * <ref> must be a valid tree, everything after the '--' must be
- * a path.
- *
- * case 2: git checkout -- [<paths>]
- *
- * everything after the '--' must be paths.
- *
- * case 3: git checkout <something> [<paths>]
- *
- * With no paths, if <something> is a commit, that is to
- * switch to the branch or detach HEAD at it. As a special case,
- * if <something> is A...B (missing A or B means HEAD but you can
- * omit at most one side), and if there is a unique merge base
- * between A and B, A...B names that merge base.
+ * Extract branch name from command line arguments, so
+ * all that is left is pathspecs.
*
- * With no paths, if <something> is _not_ a commit, no -t nor -b
- * was given, and there is a remote-tracking branch whose name is
- * <something> in one and only one remote, then this is a short-hand
- * to fork local <something> from that remote-tracking branch.
+ * Handle
*
- * Otherwise <something> shall not be ambiguous.
- * - If it's *only* a reference, treat it like case (1).
- * - If it's only a path, treat it like case (2).
- * - else: fail.
+ * 1) git checkout <tree> -- [<paths>]
+ * 2) git checkout -- [<paths>]
+ * 3) git checkout <something> [<paths>]
*
+ * including "last branch" syntax and DWIM-ery for names of
+ * remote branches, erroring out for invalid or ambiguous cases.
*/
if (argc) {
- if (!strcmp(argv[0], "--")) { /* case (2) */
- argv++;
- argc--;
- goto no_reference;
- }
-
- arg = argv[0];
- has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
-
- if (!strcmp(arg, "-"))
- arg = "@{-1}";
-
- if (get_sha1_mb(arg, rev)) {
- if (has_dash_dash) /* case (1) */
- die("invalid reference: %s", arg);
- if (!patch_mode &&
- dwim_new_local_branch &&
- opts.track == BRANCH_TRACK_UNSPECIFIED &&
- !opts.new_branch &&
- !check_filename(NULL, arg) &&
- argc == 1) {
- const char *remote = unique_tracking_name(arg);
- if (!remote || get_sha1(remote, rev))
- goto no_reference;
- opts.new_branch = arg;
- arg = remote;
- /* DWIMmed to create local branch */
- }
- else
- goto no_reference;
- }
-
- /* we can't end up being in (2) anymore, eat the argument */
- argv++;
- argc--;
-
- new.name = arg;
- if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
- setup_branch_path(&new);
-
- if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
- resolve_ref(new.path, rev, 1, NULL))
- ;
- else
- new.path = NULL;
- parse_commit(new.commit);
- source_tree = new.commit->tree;
- } else
- source_tree = parse_tree_indirect(rev);
-
- if (!source_tree) /* case (1): want a tree */
- die("reference is not a tree: %s", arg);
- if (!has_dash_dash) {/* case (3 -> 1) */
- /*
- * Do not complain the most common case
- * git checkout branch
- * even if there happen to be a file called 'branch';
- * it would be extremely annoying.
- */
- if (argc)
- verify_non_filename(NULL, arg);
- }
- else {
- argv++;
- argc--;
- }
+ int dwim_ok =
+ !patch_mode &&
+ dwim_new_local_branch &&
+ opts.track == BRANCH_TRACK_UNSPECIFIED &&
+ !opts.new_branch;
+ int n = parse_branchname_arg(argc, argv, dwim_ok,
+ &new, &source_tree, rev, &opts.new_branch);
+ argv += n;
+ argc -= n;
}
-no_reference:
-
if (opts.track == BRANCH_TRACK_UNSPECIFIED)
opts.track = git_branch_track;
@@ -886,6 +933,9 @@ no_reference:
}
}
+ if (opts.force_detach)
+ die("git checkout: --detach does not take a path argument");
+
if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
diff --git a/builtin/clone.c b/builtin/clone.c
index 60d9a64280..2ee1fa9846 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -413,7 +413,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (path)
repo = xstrdup(make_nonrelative_path(repo_name));
else if (!strchr(repo_name, ':'))
- repo = xstrdup(make_absolute_path(repo_name));
+ die("repository '%s' does not exist", repo_name);
else
repo = repo_name;
is_local = path && !is_bundle;
diff --git a/builtin/commit.c b/builtin/commit.c
index d7f55e3d46..82092e5c82 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -54,9 +54,17 @@ static const char empty_amend_advice[] =
"it empty. You can repeat your command with --allow-empty, or you can\n"
"remove the commit entirely with \"git reset HEAD^\".\n";
+static const char empty_cherry_pick_advice[] =
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+" git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n";
+
static unsigned char head_sha1[20];
-static char *use_message_buffer;
+static const char *use_message_buffer;
static const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
static struct lock_file false_lock; /* used only for partial commits */
@@ -68,6 +76,11 @@ static enum {
static const char *logfile, *force_author;
static const char *template_file;
+/*
+ * The _message variables are commit names from which to take
+ * the commit message and/or authorship.
+ */
+static const char *author_message, *author_message_buffer;
static char *edit_message, *use_message;
static char *fixup_message, *squash_message;
static int all, edit_flag, also, interactive, only, amend, signoff;
@@ -88,7 +101,8 @@ static enum {
} cleanup_mode;
static char *cleanup_arg;
-static int use_editor = 1, initial_commit, in_merge, include_status = 1;
+static enum commit_whence whence;
+static int use_editor = 1, initial_commit, include_status = 1;
static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message;
@@ -119,13 +133,13 @@ static struct option builtin_commit_options[] = {
OPT_GROUP("Commit message options"),
OPT_FILENAME('F', "file", &logfile, "read message from file"),
- OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
- OPT_STRING(0, "date", &force_date, "DATE", "override date for commit"),
- OPT_CALLBACK('m', "message", &message, "MESSAGE", "commit message", opt_parse_m),
- OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
- OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
- OPT_STRING(0, "fixup", &fixup_message, "COMMIT", "use autosquash formatted message to fixup specified commit"),
- OPT_STRING(0, "squash", &squash_message, "COMMIT", "use autosquash formatted message to squash specified commit"),
+ OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
+ OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
+ OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
+ OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
+ OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
+ OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
+ OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
@@ -163,6 +177,36 @@ static struct option builtin_commit_options[] = {
OPT_END()
};
+static void determine_whence(struct wt_status *s)
+{
+ if (file_exists(git_path("MERGE_HEAD")))
+ whence = FROM_MERGE;
+ else if (file_exists(git_path("CHERRY_PICK_HEAD")))
+ whence = FROM_CHERRY_PICK;
+ else
+ whence = FROM_COMMIT;
+ if (s)
+ s->whence = whence;
+}
+
+static const char *whence_s(void)
+{
+ char *s = "";
+
+ switch (whence) {
+ case FROM_COMMIT:
+ break;
+ case FROM_MERGE:
+ s = "merge";
+ break;
+ case FROM_CHERRY_PICK:
+ s = "cherry-pick";
+ break;
+ }
+
+ return s;
+}
+
static void rollback_index_files(void)
{
switch (commit_style) {
@@ -378,8 +422,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
*/
commit_style = COMMIT_PARTIAL;
- if (in_merge)
- die("cannot do a partial commit during a merge.");
+ if (whence != FROM_COMMIT)
+ die("cannot do a partial commit during a %s.", whence_s());
memset(&partial, 0, sizeof(partial));
partial.strdup_strings = 1;
@@ -469,18 +513,18 @@ static void determine_author_info(struct strbuf *author_ident)
email = getenv("GIT_AUTHOR_EMAIL");
date = getenv("GIT_AUTHOR_DATE");
- if (use_message && !renew_authorship) {
+ if (author_message) {
const char *a, *lb, *rb, *eol;
- a = strstr(use_message_buffer, "\nauthor ");
+ a = strstr(author_message_buffer, "\nauthor ");
if (!a)
- die("invalid commit: %s", use_message);
+ die("invalid commit: %s", author_message);
lb = strchrnul(a + strlen("\nauthor "), '<');
rb = strchrnul(lb, '>');
eol = strchrnul(rb, '\n');
if (!*lb || !*rb || !*eol)
- die("invalid commit: %s", use_message);
+ die("invalid commit: %s", author_message);
if (lb == a + strlen("\nauthor "))
/* \nauthor <foo@example.com> */
@@ -634,18 +678,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
die_errno("could not read SQUASH_MSG");
hook_arg1 = "squash";
- } else if (template_file && !stat(template_file, &statbuf)) {
+ } else if (template_file) {
if (strbuf_read_file(&sb, template_file, 0) < 0)
die_errno("could not read '%s'", template_file);
hook_arg1 = "template";
}
/*
- * This final case does not modify the template message,
- * it just sets the argument to the prepare-commit-msg hook.
+ * The remaining cases don't modify the template message, but
+ * just set the argument(s) to the prepare-commit-msg hook.
*/
- else if (in_merge)
+ else if (whence == FROM_MERGE)
hook_arg1 = "merge";
+ else if (whence == FROM_CHERRY_PICK) {
+ hook_arg1 = "commit";
+ hook_arg2 = "CHERRY_PICK_HEAD";
+ }
if (squash_message) {
/*
@@ -694,16 +742,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
strbuf_addstr(&committer_ident, git_committer_info(0));
if (use_editor && include_status) {
char *ai_tmp, *ci_tmp;
- if (in_merge)
+ if (whence != FROM_COMMIT)
fprintf(fp,
"#\n"
- "# It looks like you may be committing a MERGE.\n"
+ "# It looks like you may be committing a %s.\n"
"# If this is not correct, please remove the file\n"
"# %s\n"
"# and try again.\n"
"#\n",
- git_path("MERGE_HEAD"));
-
+ whence_s(),
+ git_path(whence == FROM_MERGE
+ ? "MERGE_HEAD"
+ : "CHERRY_PICK_HEAD"));
fprintf(fp,
"\n"
"# Please enter the commit message for your changes.");
@@ -766,11 +816,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fclose(fp);
- if (!commitable && !in_merge && !allow_empty &&
+ /*
+ * Reject an attempt to record a non-merge empty commit without
+ * explicit --allow-empty. In the cherry-pick case, it may be
+ * empty due to conflict resolution, which the user should okay.
+ */
+ if (!commitable && whence != FROM_MERGE && !allow_empty &&
!(amend && is_a_merge(head_sha1))) {
run_status(stdout, index_file, prefix, 0, s);
if (amend)
fputs(empty_amend_advice, stderr);
+ else if (whence == FROM_CHERRY_PICK)
+ fputs(empty_cherry_pick_advice, stderr);
return 0;
}
@@ -898,6 +955,28 @@ static void handle_untracked_files_arg(struct wt_status *s)
die("Invalid untracked files mode '%s'", untracked_files_arg);
}
+static const char *read_commit_message(const char *name)
+{
+ const char *out_enc, *out;
+ struct commit *commit;
+
+ commit = lookup_commit_reference_by_name(name);
+ if (!commit)
+ die("could not lookup commit %s", name);
+ out_enc = get_commit_output_encoding();
+ out = logmsg_reencode(commit, out_enc);
+
+ /*
+ * If we failed to reencode the buffer, just copy it
+ * byte for byte so the user can try to fix it up.
+ * This also handles the case where input and output
+ * encodings are identical.
+ */
+ if (out == NULL)
+ out = xstrdup(commit->buffer);
+ return out;
+}
+
static int parse_and_validate_options(int argc, const char *argv[],
const char * const usage[],
const char *prefix,
@@ -927,8 +1006,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
/* Sanity check options */
if (amend && initial_commit)
die("You have nothing to amend.");
- if (amend && in_merge)
- die("You are in the middle of a merge -- cannot amend.");
+ if (amend && whence != FROM_COMMIT)
+ die("You are in the middle of a %s -- cannot amend.", whence_s());
if (fixup_message && squash_message)
die("Options --squash and --fixup cannot be used together");
if (use_message)
@@ -947,26 +1026,18 @@ static int parse_and_validate_options(int argc, const char *argv[],
use_message = edit_message;
if (amend && !use_message && !fixup_message)
use_message = "HEAD";
- if (!use_message && renew_authorship)
+ if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
die("--reset-author can be used only with -C, -c or --amend.");
if (use_message) {
- const char *out_enc;
- struct commit *commit;
-
- commit = lookup_commit_reference_by_name(use_message);
- if (!commit)
- die("could not lookup commit %s", use_message);
- out_enc = get_commit_output_encoding();
- use_message_buffer = logmsg_reencode(commit, out_enc);
-
- /*
- * If we failed to reencode the buffer, just copy it
- * byte for byte so the user can try to fix it up.
- * This also handles the case where input and output
- * encodings are identical.
- */
- if (use_message_buffer == NULL)
- use_message_buffer = xstrdup(commit->buffer);
+ use_message_buffer = read_commit_message(use_message);
+ if (!renew_authorship) {
+ author_message = use_message;
+ author_message_buffer = use_message_buffer;
+ }
+ }
+ if (whence == FROM_CHERRY_PICK && !renew_authorship) {
+ author_message = "CHERRY_PICK_HEAD";
+ author_message_buffer = read_commit_message(author_message);
}
if (!!also + !!only + !!all + !!interactive > 1)
@@ -1117,7 +1188,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
wt_status_prepare(&s);
gitmodules_config();
git_config(git_status_config, &s);
- in_merge = file_exists(git_path("MERGE_HEAD"));
+ determine_whence(&s);
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
@@ -1140,7 +1211,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
}
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
- s.in_merge = in_merge;
s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(&s);
@@ -1302,8 +1372,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
wt_status_prepare(&s);
git_config(git_commit_config, &s);
- in_merge = file_exists(git_path("MERGE_HEAD"));
- s.in_merge = in_merge;
+ determine_whence(&s);
if (s.use_color == -1)
s.use_color = git_use_color_default;
@@ -1340,7 +1409,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
for (c = commit->parents; c; c = c->next)
pptr = &commit_list_insert(c->item, pptr)->next;
- } else if (in_merge) {
+ } else if (whence == FROM_MERGE) {
struct strbuf m = STRBUF_INIT;
FILE *fp;
@@ -1369,7 +1438,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
parents = reduce_heads(parents);
} else {
if (!reflog_msg)
- reflog_msg = "commit";
+ reflog_msg = (whence == FROM_CHERRY_PICK)
+ ? "commit (cherry-pick)"
+ : "commit";
pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
}
@@ -1424,6 +1495,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die("cannot update HEAD ref");
}
+ unlink(git_path("CHERRY_PICK_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
diff --git a/builtin/config.c b/builtin/config.c
index ca4a0db4a7..76be0b786f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -52,7 +52,7 @@ static struct option builtin_config_options[] = {
OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
- OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
+ OPT_STRING('f', "file", &given_config_file, "file", "use given config file"),
OPT_GROUP("Action"),
OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
@@ -153,7 +153,6 @@ static int show_config(const char *key_, const char *value_, void *cb)
static int get_value(const char *key_, const char *regex_)
{
int ret = -1;
- char *tl;
char *global = NULL, *repo_config = NULL;
const char *system_wide = NULL, *local;
@@ -167,18 +166,32 @@ static int get_value(const char *key_, const char *regex_)
system_wide = git_etc_gitconfig();
}
- key = xstrdup(key_);
- for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
- *tl = tolower(*tl);
- for (tl=key; *tl && *tl != '.'; ++tl)
- *tl = tolower(*tl);
-
if (use_key_regexp) {
+ char *tl;
+
+ /*
+ * NEEDSWORK: this naive pattern lowercasing obviously does not
+ * work for more complex patterns like "^[^.]*Foo.*bar".
+ * Perhaps we should deprecate this altogether someday.
+ */
+
+ key = xstrdup(key_);
+ for (tl = key + strlen(key) - 1;
+ tl >= key && *tl != '.';
+ tl--)
+ *tl = tolower(*tl);
+ for (tl = key; *tl && *tl != '.'; tl++)
+ *tl = tolower(*tl);
+
key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(key_regexp, key, REG_EXTENDED)) {
fprintf(stderr, "Invalid key pattern: %s\n", key_);
+ free(key);
goto free_strings;
}
+ } else {
+ if (git_config_parse_key(key_, &key, NULL))
+ goto free_strings;
}
if (regex_) {
@@ -500,3 +513,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return 0;
}
+
+int cmd_repo_config(int argc, const char **argv, const char *prefix)
+{
+ fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n");
+ return cmd_config(argc, argv, prefix);
+}
diff --git a/builtin/describe.c b/builtin/describe.c
index 342129fdbd..3ba26dc819 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -63,7 +63,7 @@ static inline struct commit_name *find_commit_name(const unsigned char *peeled)
return n;
}
-static int set_util(void *chain)
+static int set_util(void *chain, void *data)
{
struct commit_name *n;
for (n = chain; n; n = n->next) {
@@ -289,7 +289,7 @@ static void describe(const char *arg, int last_one)
fprintf(stderr, "searching to describe %s\n", arg);
if (!have_util) {
- for_each_hash(&names, set_util);
+ for_each_hash(&names, set_util, NULL);
have_util = 1;
}
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 951c7c8994..46085f862f 1006