From: Junio C Hamano Date: Fri, 20 Apr 2012 22:50:37 +0000 (-0700) Subject: Merge branch 'ct/advise-push-default' X-Git-Tag: v1.7.11-rc0~135 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/c5da24a73a96a9908ae715f6430e65294115646a?ds=inline;hp=-c Merge branch 'ct/advise-push-default' Break down the cases in which "git push" fails due to non-ff into three categories, and give separate advise messages for each case. By Christopher Tiwald (2) and Jeff King (1) * ct/advise-push-default: Fix httpd tests that broke when non-ff push advice changed clean up struct ref's nonfastforward field push: Provide situational hints for non-fast-forward errors --- c5da24a73a96a9908ae715f6430e65294115646a diff --combined builtin/push.c index b6c0fee4c6,8a14e4bcc1..693671315e --- a/builtin/push.c +++ b/builtin/push.c @@@ -24,6 -24,7 +24,7 @@@ static int progress = -1 static const char **refspec; static int refspec_nr; static int refspec_alloc; + static int default_matching_used; static void add_refspec(const char *ref) { @@@ -65,16 -66,6 +66,16 @@@ static void set_refspecs(const char **r } } +static int push_url_of_remote(struct remote *remote, const char ***url_p) +{ + if (remote->pushurl_nr) { + *url_p = remote->pushurl; + return remote->pushurl_nr; + } + *url_p = remote->url; + return remote->url_nr; +} + static void setup_push_upstream(struct remote *remote) { struct strbuf refspec = STRBUF_INIT; @@@ -86,7 -77,7 +87,7 @@@ "\n" " git push %s HEAD:\n"), remote->name); - if (!branch->merge_nr || !branch->merge) + if (!branch->merge_nr || !branch->merge || !branch->remote_name) die(_("The current branch %s has no upstream branch.\n" "To push the current branch and set the remote as upstream, use\n" "\n" @@@ -97,12 -88,6 +98,12 @@@ if (branch->merge_nr != 1) die(_("The current branch %s has multiple upstream branches, " "refusing to push."), branch->name); + if (strcmp(branch->remote_name, remote->name)) + die(_("You are pushing to remote '%s', which is not the upstream of\n" + "your current branch '%s', without telling me what to push\n" + "to update which remote branch."), + remote->name, branch->name); + strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); add_refspec(refspec.buf); } @@@ -111,6 -96,9 +112,9 @@@ static void setup_default_push_refspecs { switch (push_default) { default: + case PUSH_DEFAULT_UNSPECIFIED: + default_matching_used = 1; + /* fallthru */ case PUSH_DEFAULT_MATCHING: add_refspec(":"); break; @@@ -130,6 -118,45 +134,45 @@@ } } + static const char message_advice_pull_before_push[] = + N_("Updates were rejected because the tip of your current branch is behind\n" + "its remote counterpart. Merge the remote changes (e.g. 'git pull')\n" + "before pushing again.\n" + "See the 'Note about fast-forwards' in 'git push --help' for details."); + + static const char message_advice_use_upstream[] = + N_("Updates were rejected because a pushed branch tip is behind its remote\n" + "counterpart. If you did not intend to push that branch, you may want to\n" + "specify branches to push or set the 'push.default' configuration\n" + "variable to 'current' or 'upstream' to push only the current branch."); + + static const char message_advice_checkout_pull_push[] = + N_("Updates were rejected because a pushed branch tip is behind its remote\n" + "counterpart. Check out this branch and merge the remote changes\n" + "(e.g. 'git pull') before pushing again.\n" + "See the 'Note about fast-forwards' in 'git push --help' for details."); + + static void advise_pull_before_push(void) + { + if (!advice_push_non_ff_current || !advice_push_nonfastforward) + return; + advise(_(message_advice_pull_before_push)); + } + + static void advise_use_upstream(void) + { + if (!advice_push_non_ff_default || !advice_push_nonfastforward) + return; + advise(_(message_advice_use_upstream)); + } + + static void advise_checkout_pull_push(void) + { + if (!advice_push_non_ff_matching || !advice_push_nonfastforward) + return; + advise(_(message_advice_checkout_pull_push)); + } + static int push_with_options(struct transport *transport, int flags) { int err; @@@ -151,14 -178,21 +194,21 @@@ error(_("failed to push some refs to '%s'"), transport->url); err |= transport_disconnect(transport); - if (!err) return 0; - if (nonfastforward && advice_push_nonfastforward) { - fprintf(stderr, _("To prevent you from losing history, non-fast-forward updates were rejected\n" - "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n" - "'Note about fast-forwards' section of 'git push --help' for details.\n")); + switch (nonfastforward) { + default: + break; + case NON_FF_HEAD: + advise_pull_before_push(); + break; + case NON_FF_OTHER: + if (default_matching_used) + advise_use_upstream(); + else + advise_checkout_pull_push(); + break; } return 1; @@@ -212,7 -246,13 +262,7 @@@ static int do_push(const char *repo, in setup_default_push_refspecs(remote); } errs = 0; - if (remote->pushurl_nr) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } else { - url = remote->url; - url_nr = remote->url_nr; - } + url_nr = push_url_of_remote(remote, &url); if (url_nr) { for (i = 0; i < url_nr; i++) { struct transport *transport = diff --combined cache.h index 5e6eef9b4d,35f30752c6..5bf59ff5c3 --- a/cache.h +++ b/cache.h @@@ -625,7 -625,8 +625,8 @@@ enum push_default_type PUSH_DEFAULT_NOTHING = 0, PUSH_DEFAULT_MATCHING, PUSH_DEFAULT_UPSTREAM, - PUSH_DEFAULT_CURRENT + PUSH_DEFAULT_CURRENT, + PUSH_DEFAULT_UNSPECIFIED }; extern enum branch_track git_branch_track; @@@ -708,19 -709,6 +709,19 @@@ static inline void hashclr(unsigned cha #define EMPTY_TREE_SHA1_BIN \ ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL) +#define EMPTY_BLOB_SHA1_HEX \ + "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" +#define EMPTY_BLOB_SHA1_BIN_LITERAL \ + "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \ + "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91" +#define EMPTY_BLOB_SHA1_BIN \ + ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL) + +static inline int is_empty_blob_sha1(const unsigned char *sha1) +{ + return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN); +} + int git_mkstemp(char *path, size_t n, const char *template); int git_mkstemps(char *path, size_t n, const char *template, int suffix_len); @@@ -941,22 -929,6 +942,22 @@@ extern const char *fmt_name(const char extern const char *git_editor(void); extern const char *git_pager(int stdout_is_tty); +struct ident_split { + const char *name_begin; + const char *name_end; + const char *mail_begin; + const char *mail_end; + const char *date_begin; + const char *date_end; + const char *tz_begin; + const char *tz_end; +}; +/* + * Signals an success with 0, but time part of the result may be NULL + * if the input lacks timestamp and zone + */ +extern int split_ident_line(struct ident_split *, const char *, int); + struct checkout { const char *base_dir; int base_dir_len; @@@ -1305,6 -1277,4 +1306,6 @@@ extern struct startup_info *startup_inf /* builtin/merge.c */ int checkout_fast_forward(const unsigned char *from, const unsigned char *to); +int sane_execvp(const char *file, char *const argv[]); + #endif /* CACHE_H */ diff --combined t/t5541-http-push.sh index d7964c7eb5,57c3e48dda..5b170be2c0 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@@ -30,7 -30,6 +30,7 @@@ test_expect_success 'setup remote repos git clone --bare test_repo test_repo.git && cd test_repo.git && git config http.receivepack true && + git config core.logallrefupdates true && ORIG_HEAD=$(git rev-parse --verify HEAD) && cd - && mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH" @@@ -168,7 -167,7 +168,7 @@@ test_expect_success 'push fails for non ' test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' ' - test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" \ + test_i18ngrep "Updates were rejected because" \ output ' @@@ -223,25 -222,5 +223,25 @@@ test_expect_success TTY 'quiet push' test_cmp /dev/null output ' +test_expect_success 'http push gives sane defaults to reflog' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit reflog-test && + git push "$HTTPD_URL"/smart/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -g -1 --format="%gn <%ge>" >actual && + echo "anonymous " >expect && + test_cmp expect actual +' + +test_expect_success 'http push respects GIT_COMMITTER_* in reflog' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit custom-reflog-test && + git push "$HTTPD_URL"/smart_custom_env/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -g -1 --format="%gn <%ge>" >actual && + echo "Custom User " >expect && + test_cmp expect actual +' + stop_httpd test_done diff --combined transport.c index ea9dcb6612,7864007c9c..2dfac700b6 --- a/transport.c +++ b/transport.c @@@ -721,6 -721,10 +721,10 @@@ void transport_print_push_status(const { struct ref *ref; int n = 0; + unsigned char head_sha1[20]; + char *head; + + head = resolve_refdup("HEAD", head_sha1, 1, NULL); if (verbose) { for (ref = refs; ref; ref = ref->next) @@@ -738,8 -742,13 +742,13 @@@ ref->status != REF_STATUS_UPTODATE && ref->status != REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); - if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) - *nonfastforward = 1; + if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD && + *nonfastforward != NON_FF_HEAD) { + if (!strcmp(head, ref->name)) + *nonfastforward = NON_FF_HEAD; + else + *nonfastforward = NON_FF_OTHER; + } } } @@@ -1154,7 -1163,7 +1163,7 @@@ int transport_disconnect(struct transpo } /* - * Strip username (and password) from an url and return + * Strip username (and password) from a URL and return * it in a newly allocated string. */ char *transport_anonymize_url(const char *url)