diff options
author | Junio C Hamano <gitster@pobox.com> | 2011-07-06 15:38:18 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2011-07-06 15:38:18 -0700 |
commit | 71ee7fd15457a0252c089420b5b66de266dcbd2f (patch) | |
tree | 98d4d7d1f72c30fd7695f011be78d3e45158c41a /builtin-revert.c | |
parent | Merge commit 'v1.6.0' into jc/checkout-reflog-fix (diff) | |
parent | Git 1.7.0 (diff) | |
download | tgif-71ee7fd15457a0252c089420b5b66de266dcbd2f.tar.xz |
Merge commit 'v1.7.0' into jc/checkout-reflog-fix
* commit 'v1.7.0': (4188 commits)
Git 1.7.0
Fix typo in 1.6.6.2 release notes
Re-fix check-ref-format documentation mark-up
archive documentation: attributes are taken from the tree by default
Documentation: minor fixes to RelNotes-1.7.0
bash: support 'git am's new '--continue' option
filter-branch: Fix error message for --prune-empty --commit-filter
am: switch --resolved to --continue
Update draft release notes to 1.7.0 one more time
Git 1.6.6.2
t8003: check exit code of command and error message separately
check-ref-format documentation: fix enumeration mark-up
Documentation: quote braces in {upstream} notation
t3902: Protect against OS X normalization
blame: prevent a segv when -L given start > EOF
git-push: document all the status flags used in the output
Fix parsing of imap.preformattedHTML and imap.sslverify
git-add documentation: Fix shell quoting example
Revert "pack-objects: fix pack generation when using pack_size_limit"
archive: simplify archive format guessing
...
Diffstat (limited to 'builtin-revert.c')
-rw-r--r-- | builtin-revert.c | 129 |
1 files changed, 73 insertions, 56 deletions
diff --git a/builtin-revert.c b/builtin-revert.c index 27881e9493..8ac86f0943 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -11,6 +11,8 @@ #include "cache-tree.h" #include "diff.h" #include "revision.h" +#include "rerere.h" +#include "merge-recursive.h" /* * This implements the builtins revert and cherry-pick. @@ -36,6 +38,7 @@ static const char * const cherry_pick_usage[] = { static int edit, no_replay, no_commit, mainline, signoff; static enum { REVERT, CHERRY_PICK } action; static struct commit *commit; +static int allow_rerere_auto; static const char *me; @@ -55,10 +58,11 @@ static void parse_args(int argc, const char **argv) OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"), OPT_INTEGER('m', "mainline", &mainline, "parent number"), + OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_END(), }; - if (parse_options(argc, argv, options, usage_str, 0) != 1) + if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1) usage_with_options(usage_str, options); arg = argv[0]; @@ -133,7 +137,7 @@ static void add_to_msg(const char *string) { int len = strlen(string); if (write_in_full(msg_fd, string, len) < 0) - die ("Could not write to MERGE_MSG"); + die_errno ("Could not write to MERGE_MSG"); } static void add_message_to_msg(const char *message) @@ -200,36 +204,6 @@ static void set_author_ident_env(const char *message) sha1_to_hex(commit->object.sha1)); } -static int merge_recursive(const char *base_sha1, - const char *head_sha1, const char *head_name, - const char *next_sha1, const char *next_name) -{ - char buffer[256]; - const char *argv[6]; - int i = 0; - - sprintf(buffer, "GITHEAD_%s", head_sha1); - setenv(buffer, head_name, 1); - sprintf(buffer, "GITHEAD_%s", next_sha1); - setenv(buffer, next_name, 1); - - /* - * This three way merge is an interesting one. We are at - * $head, and would want to apply the change between $commit - * and $prev on top of us (when reverting), or the change between - * $prev and $commit on top of us (when cherry-picking or replaying). - */ - argv[i++] = "merge-recursive"; - if (base_sha1) - argv[i++] = base_sha1; - argv[i++] = "--"; - argv[i++] = head_sha1; - argv[i++] = next_sha1; - argv[i++] = NULL; - - return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD); -} - static char *help_msg(const unsigned char *sha1) { static char helpbuf[1024]; @@ -251,25 +225,40 @@ static char *help_msg(const unsigned char *sha1) return helpbuf; } -static int index_is_dirty(void) +static struct tree *empty_tree(void) { - struct rev_info rev; - init_revisions(&rev, NULL); - setup_revisions(0, NULL, &rev, "HEAD"); - DIFF_OPT_SET(&rev.diffopt, QUIET); - DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); - run_diff_index(&rev, 1); - return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); + struct tree *tree = xcalloc(1, sizeof(struct tree)); + + tree->object.parsed = 1; + tree->object.type = OBJ_TREE; + pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1); + return tree; +} + +static NORETURN void die_dirty_index(const char *me) +{ + if (read_cache_unmerged()) { + die_resolve_conflict(me); + } else { + if (advice_commit_before_merge) + die("Your local changes would be overwritten by %s.\n" + "Please, commit your changes or stash them to proceed.", me); + else + die("Your local changes would be overwritten by %s.\n", me); + } } static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; struct commit *base, *next, *parent; - int i; + int i, index_fd, clean; char *oneline, *reencoded_message = NULL; const char *message, *encoding; - const char *defmsg = xstrdup(git_path("MERGE_MSG")); + char *defmsg = git_pathdup("MERGE_MSG"); + struct merge_options o; + struct tree *result, *next_tree, *base_tree, *head_tree; + static struct lock_file index_lock; git_config(git_default_config, NULL); me = action == REVERT ? "revert" : "cherry-pick"; @@ -280,6 +269,8 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (action == REVERT && !no_replay) die("revert is incompatible with replay"); + if (read_cache() < 0) + die("git %s: failed to read the index", me); if (no_commit) { /* * We do not intend to commit immediately. We just want to @@ -292,12 +283,12 @@ static int revert_or_cherry_pick(int argc, const char **argv) } else { if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); - if (read_cache() < 0) - die("could not read the index"); - if (index_is_dirty()) - die ("Dirty index: cannot %s", me); - discard_cache(); + if (index_differs_from("HEAD", 0)) + die_dirty_index(me); } + discard_cache(); + + index_fd = hold_locked_index(&index_lock, 1); if (!commit->parents) { if (action == REVERT) @@ -331,6 +322,10 @@ static int revert_or_cherry_pick(int argc, const char **argv) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); + if (parent && parse_commit(parent) < 0) + die("%s: cannot parse parent commit %s", + me, sha1_to_hex(parent->object.sha1)); + /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" @@ -338,13 +333,14 @@ static int revert_or_cherry_pick(int argc, const char **argv) * reverse of it if we are revert. */ - msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1); + msg_fd = hold_lock_file_for_update(&msg_file, defmsg, + LOCK_DIE_ON_ERROR); encoding = get_encoding(message); if (!encoding) - encoding = "utf-8"; + encoding = "UTF-8"; if (!git_commit_encoding) - git_commit_encoding = "utf-8"; + git_commit_encoding = "UTF-8"; if ((reencoded_message = reencode_string(message, git_commit_encoding, encoding))) message = reencoded_message; @@ -360,6 +356,11 @@ static int revert_or_cherry_pick(int argc, const char **argv) add_to_msg(oneline_body + 1); add_to_msg("\"\n\nThis reverts commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); + + if (commit->parents->next) { + add_to_msg(", reversing\nchanges made to "); + add_to_msg(sha1_to_hex(parent->object.sha1)); + } add_to_msg(".\n"); } else { base = parent; @@ -373,13 +374,27 @@ static int revert_or_cherry_pick(int argc, const char **argv) } } - if (merge_recursive(base == NULL ? - NULL : sha1_to_hex(base->object.sha1), - sha1_to_hex(head), "HEAD", - sha1_to_hex(next->object.sha1), oneline) || - write_cache_as_tree(head, 0, NULL)) { + read_cache(); + init_merge_options(&o); + o.branch1 = "HEAD"; + o.branch2 = oneline; + + head_tree = parse_tree_indirect(head); + next_tree = next ? next->tree : empty_tree(); + base_tree = base ? base->tree : empty_tree(); + + clean = merge_trees(&o, + head_tree, + next_tree, base_tree, &result); + + if (active_cache_changed && + (write_cache(index_fd, active_cache, active_nr) || + commit_locked_index(&index_lock))) + die("%s: Unable to write new index file", me); + rollback_lock_file(&index_lock); + + if (!clean) { add_to_msg("\nConflicts:\n\n"); - read_cache(); for (i = 0; i < active_nr;) { struct cache_entry *ce = active_cache[i++]; if (ce_stage(ce)) { @@ -395,6 +410,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) die ("Error wrapping up %s", defmsg); fprintf(stderr, "Automatic %s failed.%s\n", me, help_msg(commit->object.sha1)); + rerere(allow_rerere_auto); exit(1); } if (commit_lock_file(&msg_file) < 0) @@ -426,6 +442,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) return execv_git_cmd(args); } free(reencoded_message); + free(defmsg); return 0; } |