From: Junio C Hamano Date: Wed, 1 Feb 2012 06:30:42 +0000 (-0800) Subject: Merge branch 'jc/pull-signed-tag' X-Git-Tag: v1.7.10-rc0~123 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/af6b37fab1a97943c804f4983452c7fde5b0fbbe?ds=inline;hp=-c Merge branch 'jc/pull-signed-tag' * jc/pull-signed-tag: merge: use editor by default in interactive sessions Conflicts: Documentation/merge-options.txt --- af6b37fab1a97943c804f4983452c7fde5b0fbbe diff --combined builtin/merge.c index 3a451727d0,0006175d15..bfb75476aa --- a/builtin/merge.c +++ b/builtin/merge.c @@@ -26,8 -26,6 +26,8 @@@ #include "merge-recursive.h" #include "resolve-undo.h" #include "remote.h" +#include "fmt-merge-msg.h" +#include "gpg-interface.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@@ -46,12 -44,11 +46,12 @@@ static const char * const builtin_merge NULL }; -static int show_diffstat = 1, shortlog_len, squash; +static int show_diffstat = 1, shortlog_len = -1, squash; static int option_commit = 1, allow_fast_forward = 1; - static int fast_forward_only, option_edit; + static int fast_forward_only, option_edit = -1; static int allow_trivial = 1, have_message; -static struct strbuf merge_msg; +static int overwrite_ignore = 1; +static struct strbuf merge_msg = STRBUF_INIT; static struct commit_list *remoteheads; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; @@@ -65,7 -62,6 +65,7 @@@ static int allow_rerere_auto static int abort_current_merge; static int show_progress = -1; static int default_to_upstream; +static const char *sign_commit; static struct strategy all_strategy[] = { { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, @@@ -193,7 -189,7 +193,7 @@@ static struct option builtin_merge_opti "create a single commit instead of doing a merge"), OPT_BOOLEAN(0, "commit", &option_commit, "perform a commit if the merge succeeds (default)"), - OPT_BOOLEAN('e', "edit", &option_edit, + OPT_BOOL('e', "edit", &option_edit, "edit message before committing"), OPT_BOOLEAN(0, "ff", &allow_fast_forward, "allow fast-forward (default)"), @@@ -211,9 -207,6 +211,9 @@@ OPT_BOOLEAN(0, "abort", &abort_current_merge, "abort the current in-progress merge"), OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1), + { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id", + "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"), OPT_END() }; @@@ -323,15 -316,13 +323,15 @@@ static void squash_message(struct commi struct rev_info rev; struct strbuf out = STRBUF_INIT; struct commit_list *j; + const char *filename; int fd; struct pretty_print_context ctx = {0}; printf(_("Squash commit -- not updating HEAD\n")); - fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666); + filename = git_path("SQUASH_MSG"); + fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) - die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG")); + die_errno(_("Could not write to '%s'"), filename); init_revisions(&rev, NULL); rev.ignore_merges = 1; @@@ -419,7 -410,7 +419,7 @@@ static void finish(struct commit *head_ static void merge_name(const char *remote, struct strbuf *msg) { struct commit *remote_head; - unsigned char branch_head[20], buf_sha[20]; + unsigned char branch_head[20]; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; @@@ -483,7 -474,7 +483,7 @@@ strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); - if (resolve_ref(truname.buf, buf_sha, 1, NULL)) { + if (ref_exists(truname.buf)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->object.sha1), @@@ -496,16 -487,14 +496,16 @@@ if (!strcmp(remote, "FETCH_HEAD") && !access(git_path("FETCH_HEAD"), R_OK)) { + const char *filename; FILE *fp; struct strbuf line = STRBUF_INIT; char *ptr; - fp = fopen(git_path("FETCH_HEAD"), "r"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "r"); if (!fp) die_errno(_("could not open '%s' for reading"), - git_path("FETCH_HEAD")); + filename); strbuf_getline(&line, fp, '\n'); fclose(fp); ptr = strstr(line.buf, "\tnot-for-merge\t"); @@@ -544,8 -533,6 +544,8 @@@ static void parse_branch_merge_options( static int git_merge_config(const char *k, const char *v, void *cb) { + int status; + if (branch && !prefixcmp(k, "branch.") && !prefixcmp(k + 7, branch) && !strcmp(k + 7 + strlen(branch), ".mergeoptions")) { @@@ -562,7 -549,15 +562,7 @@@ return git_config_string(&pull_octopus, k, v); else if (!strcmp(k, "merge.renormalize")) option_renormalize = git_config_bool(k, v); - else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) { - int is_bool; - shortlog_len = git_config_bool_or_int(k, v, &is_bool); - if (!is_bool && shortlog_len < 0) - return error(_("%s: negative length %s"), k, v); - if (is_bool && shortlog_len) - shortlog_len = DEFAULT_MERGE_LOG_LEN; - return 0; - } else if (!strcmp(k, "merge.ff")) { + else if (!strcmp(k, "merge.ff")) { int boolval = git_config_maybe_bool(k, v); if (0 <= boolval) { allow_fast_forward = boolval; @@@ -575,13 -570,6 +575,13 @@@ default_to_upstream = git_config_bool(k, v); return 0; } + + status = fmt_merge_msg_config(k, v, cb); + if (status) + return status; + status = git_gpg_config(k, v, NULL); + if (status) + return status; return git_diff_ui_config(k, v, cb); } @@@ -776,12 -764,10 +776,12 @@@ int checkout_fast_forward(const unsigne memset(&trees, 0, sizeof(trees)); memset(&opts, 0, sizeof(opts)); memset(&t, 0, sizeof(t)); - memset(&dir, 0, sizeof(dir)); - dir.flags |= DIR_SHOW_IGNORED; - dir.exclude_per_dir = ".gitignore"; - opts.dir = &dir; + if (overwrite_ignore) { + memset(&dir, 0, sizeof(dir)); + dir.flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(&dir); + opts.dir = &dir; + } opts.head_idx = 1; opts.src_index = &the_index; @@@ -856,22 -842,20 +856,22 @@@ static void add_strategies(const char * static void write_merge_msg(struct strbuf *msg) { - int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); + const char *filename = git_path("MERGE_MSG"); + int fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MSG")); + filename); if (write_in_full(fd, msg->buf, msg->len) != msg->len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG")); + die_errno(_("Could not write to '%s'"), filename); close(fd); } static void read_merge_msg(struct strbuf *msg) { + const char *filename = git_path("MERGE_MSG"); strbuf_reset(msg); - if (strbuf_read_file(msg, git_path("MERGE_MSG"), 0) < 0) - die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG")); + if (strbuf_read_file(msg, filename, 0) < 0) + die_errno(_("Could not read from '%s'"), filename); } static void write_merge_state(void); @@@ -893,12 -877,12 +893,12 @@@ static void prepare_to_commit(void write_merge_msg(&msg); run_hook(get_index_file(), "prepare-commit-msg", git_path("MERGE_MSG"), "merge", NULL, NULL); - if (option_edit) { + if (0 < option_edit) { if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) abort_commit(NULL); } read_merge_msg(&msg); - stripspace(&msg, option_edit); + stripspace(&msg, 0 < option_edit); if (!msg.len) abort_commit(_("Empty commit message.")); strbuf_release(&merge_msg); @@@ -918,9 -902,7 +918,9 @@@ static int merge_trivial(struct commit parent->next->item = remoteheads->item; parent->next->next = NULL; prepare_to_commit(); - commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL); + if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL, + sign_commit)) + die(_("failed to write commit object")); finish(head, result_commit, "In-index merge"); drop_save(); return 0; @@@ -951,9 -933,7 +951,9 @@@ static int finish_automerge(struct comm strbuf_addch(&merge_msg, '\n'); prepare_to_commit(); free_commit_list(remoteheads); - commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL); + if (commit_tree(&merge_msg, result_tree, parents, result_commit, + NULL, sign_commit)) + die(_("failed to write commit object")); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); finish(head, result_commit, buf.buf); strbuf_release(&buf); @@@ -963,14 -943,13 +963,14 @@@ static int suggest_conflicts(int renormalizing) { + const char *filename; FILE *fp; int pos; - fp = fopen(git_path("MERGE_MSG"), "a"); + filename = git_path("MERGE_MSG"); + fp = fopen(filename, "a"); if (!fp) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MSG")); + die_errno(_("Could not open '%s' for writing"), filename); fprintf(fp, "\nConflicts:\n"); for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; @@@ -1062,7 -1041,6 +1062,7 @@@ static int setup_with_upstream(const ch static void write_merge_state(void) { + const char *filename; int fd; struct commit_list *j; struct strbuf buf = STRBUF_INIT; @@@ -1077,28 -1055,54 +1077,55 @@@ } strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1)); } - fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666); + filename = git_path("MERGE_HEAD"); + fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_HEAD")); + die_errno(_("Could not open '%s' for writing"), filename); if (write_in_full(fd, buf.buf, buf.len) != buf.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD")); + die_errno(_("Could not write to '%s'"), filename); close(fd); strbuf_addch(&merge_msg, '\n'); write_merge_msg(&merge_msg); - fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666); + + filename = git_path("MERGE_MODE"); + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MODE")); + die_errno(_("Could not open '%s' for writing"), filename); strbuf_reset(&buf); if (!allow_fast_forward) strbuf_addf(&buf, "no-ff"); if (write_in_full(fd, buf.buf, buf.len) != buf.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE")); + die_errno(_("Could not write to '%s'"), filename); close(fd); } + static int default_edit_option(void) + { + static const char name[] = "GIT_MERGE_AUTOEDIT"; + const char *e = getenv(name); + struct stat st_stdin, st_stdout; + + if (have_message) + /* an explicit -m msg without --[no-]edit */ + return 0; + + if (e) { + int v = git_config_maybe_bool(name, e); + if (v < 0) + die("Bad value '%s' in environment '%s'", e, name); + return v; + } + + /* Use editor if stdin and stdout are the same and is a tty */ + return (!fstat(0, &st_stdin) && + !fstat(1, &st_stdout) && + isatty(0) && + st_stdin.st_dev == st_stdout.st_dev && + st_stdin.st_ino == st_stdout.st_ino && + st_stdin.st_mode == st_stdout.st_mode); + } + + int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; @@@ -1107,12 -1111,11 +1134,12 @@@ struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; - int flag, i; + int flag, i, ret = 0; 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; struct commit_list **remotes = &remoteheads; + void *branch_to_free; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_merge_usage, builtin_merge_options); @@@ -1121,7 -1124,7 +1148,7 @@@ * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = resolve_ref("HEAD", head_sha1, 0, &flag); + branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag); if (branch && !prefixcmp(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) @@@ -1135,8 -1138,6 +1162,8 @@@ parse_branch_merge_options(branch_mergeoptions); argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); + if (shortlog_len < 0) + shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; if (verbosity < 0 && show_progress == -1) show_progress = 0; @@@ -1149,8 -1150,7 +1176,8 @@@ die(_("There is no merge to abort (MERGE_HEAD missing).")); /* Invoke 'git reset --merge' */ - return cmd_reset(nargc, nargv, prefix); + ret = cmd_reset(nargc, nargv, prefix); + goto done; } if (read_cache_unmerged()) @@@ -1189,12 -1189,9 +1216,12 @@@ die(_("You cannot combine --no-ff with --ff-only.")); if (!abort_current_merge) { - if (!argc && default_to_upstream) - argc = setup_with_upstream(&argv); - else if (argc == 1 && !strcmp(argv[0], "-")) + if (!argc) { + if (default_to_upstream) + argc = setup_with_upstream(&argv); + else + die(_("No commit specified and merge.defaultToUpstream not set.")); + } else if (argc == 1 && !strcmp(argv[0], "-")) argv[0] = "@{-1}"; } if (!argc) @@@ -1237,7 -1234,7 +1264,7 @@@ read_empty(remote_head->object.sha1, 0); update_ref("initial pull", "HEAD", remote_head->object.sha1, NULL, 0, DIE_ON_ERR); - return 0; + goto done; } else { struct strbuf merge_names = STRBUF_INIT; @@@ -1291,6 -1288,9 +1318,9 @@@ } } + if (option_edit < 0) + option_edit = default_edit_option(); + if (!use_strategies) { if (!remoteheads->next) add_strategies(pull_twohead, DEFAULT_TWOHEAD); @@@ -1326,7 -1326,7 +1356,7 @@@ * but first the most common case of merging one remote. */ finish_up_to_date("Already up-to-date."); - return 0; + goto done; } else if (allow_fast_forward && !remoteheads->next && !common->next && !hashcmp(common->item->object.sha1, head_commit->object.sha1)) { @@@ -1347,20 -1347,16 +1377,20 @@@ strbuf_addstr(&msg, " (no commit created; -m option ignored)"); commit = remoteheads->item; - if (!commit) - return 1; + if (!commit) { + ret = 1; + goto done; + } if (checkout_fast_forward(head_commit->object.sha1, - commit->object.sha1)) - return 1; + commit->object.sha1)) { + ret = 1; + goto done; + } finish(head_commit, commit->object.sha1, msg.buf); drop_save(); - return 0; + goto done; } else if (!remoteheads->next && common->next) ; /* @@@ -1378,11 -1374,8 +1408,11 @@@ git_committer_info(IDENT_ERROR_ON_NO_NAME); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, - head_commit->object.sha1, remoteheads->item->object.sha1)) - return merge_trivial(head_commit); + head_commit->object.sha1, + remoteheads->item->object.sha1)) { + ret = merge_trivial(head_commit); + goto done; + } printf(_("Nope.\n")); } } else { @@@ -1410,7 -1403,7 +1440,7 @@@ } if (up_to_date) { finish_up_to_date("Already up-to-date. Yeeah!"); - return 0; + goto done; } } @@@ -1492,11 -1485,9 +1522,11 @@@ * If we have a resulting tree, that means the strategy module * auto resolved the merge cleanly. */ - if (automerge_was_ok) - return finish_automerge(head_commit, common, result_tree, - wt_strategy); + if (automerge_was_ok) { + ret = finish_automerge(head_commit, common, result_tree, + wt_strategy); + goto done; + } /* * Pick the result from the best strategy and have the user fix @@@ -1510,8 -1501,7 +1540,8 @@@ else fprintf(stderr, _("Merge with strategy %s failed.\n"), use_strategies[0]->name); - return 2; + ret = 2; + goto done; } else if (best_strategy == wt_strategy) ; /* We already have its result in the working tree. */ else { @@@ -1527,13 -1517,10 +1557,13 @@@ else write_merge_state(); - if (merge_was_ok) { + if (merge_was_ok) fprintf(stderr, _("Automatic merge went well; " "stopped before committing as requested\n")); - return 0; - } else - return suggest_conflicts(option_renormalize); + else + ret = suggest_conflicts(option_renormalize); + +done: + free(branch_to_free); + return ret; } diff --combined t/test-lib.sh index 709a30067e,ed32c2ab7b..b22bee7c84 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@@ -44,7 -44,6 +44,7 @@@ export LANG LC_ALL PAGER TERM T EDITOR=: unset VISUAL unset EMAIL +unset LANGUAGE unset $(perl -e ' my @env = keys %ENV; my $ok = join("|", qw( @@@ -64,7 -63,8 +64,8 @@@ GIT_AUTHOR_NAME='A U Thor GIT_COMMITTER_EMAIL=committer@example.com GIT_COMMITTER_NAME='C O Mitter' GIT_MERGE_VERBOSITY=5 - export GIT_MERGE_VERBOSITY + GIT_MERGE_AUTOEDIT=no + export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME export EDITOR @@@ -192,7 -192,6 +193,7 @@@ the fi exec 5>&1 +exec 6<&0 if test "$verbose" = "t" then exec 4>&2 3>&1 @@@ -329,19 -328,6 +330,19 @@@ test_tick () export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } +# Stop execution and start a shell. This is useful for debugging tests and +# only makes sense together with "-v". +# +# Be sure to remove all invocations of this command before submitting. + +test_pause () { + if test "$verbose" = t; then + "$SHELL_PATH" <&6 >&3 2>&4 + else + error >&5 "test_pause requires --verbose" + fi +} + # Call test_commit with the arguments " [ []]" # # This will commit a file with the given contents and the given commit @@@ -394,11 -380,6 +395,11 @@@ test_config () git config "$@" } +test_config_global () { + test_when_finished "test_unconfig --global '$1'" && + git config --global "$@" +} + # Use test_set_prereq to tell that a particular prerequisite is available. # The prerequisite can later be checked for in two ways: # @@@ -489,7 -470,7 +490,7 @@@ test_debug () test_eval_ () { # This is a separate function because some tests use # "return" to end a test_expect_success block early. - eval >&3 2>&4 "$*" + eval &3 2>&4 "$*" } test_run_ () { @@@ -1133,14 -1114,12 +1134,14 @@@ esa test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PYTHON" && test_set_prereq PYTHON test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE +test -z "$NO_GETTEXT" && test_set_prereq GETTEXT # Can we rely on git's output in the C locale? if test -n "$GETTEXT_POISON" then GIT_GETTEXT_POISON=YesPlease export GIT_GETTEXT_POISON + test_set_prereq GETTEXT_POISON else test_set_prereq C_LOCALE_OUTPUT fi