git/builtin/send-pack.c

346 lines
8.9 KiB
C
Raw Normal View History

Fix sparse warnings Fix warnings from 'make check'. - These files don't include 'builtin.h' causing sparse to complain that cmd_* isn't declared: builtin/clone.c:364, builtin/fetch-pack.c:797, builtin/fmt-merge-msg.c:34, builtin/hash-object.c:78, builtin/merge-index.c:69, builtin/merge-recursive.c:22 builtin/merge-tree.c:341, builtin/mktag.c:156, builtin/notes.c:426 builtin/notes.c:822, builtin/pack-redundant.c:596, builtin/pack-refs.c:10, builtin/patch-id.c:60, builtin/patch-id.c:149, builtin/remote.c:1512, builtin/remote-ext.c:240, builtin/remote-fd.c:53, builtin/reset.c:236, builtin/send-pack.c:384, builtin/unpack-file.c:25, builtin/var.c:75 - These files have symbols which should be marked static since they're only file scope: submodule.c:12, diff.c:631, replace_object.c:92, submodule.c:13, submodule.c:14, trace.c:78, transport.c:195, transport-helper.c:79, unpack-trees.c:19, url.c:3, url.c:18, url.c:104, url.c:117, url.c:123, url.c:129, url.c:136, thread-utils.c:21, thread-utils.c:48 - These files redeclare symbols to be different types: builtin/index-pack.c:210, parse-options.c:564, parse-options.c:571, usage.c:49, usage.c:58, usage.c:63, usage.c:72 - These files use a literal integer 0 when they really should use a NULL pointer: daemon.c:663, fast-import.c:2942, imap-send.c:1072, notes-merge.c:362 While we're in the area, clean up some unused #includes in builtin files (mostly exec_cmd.h). Signed-off-by: Stephen Boyd <bebarino@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-03-22 07:51:05 +00:00
#include "builtin.h"
#include "config.h"
#include "commit.h"
#include "refs.h"
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
#include "remote.h"
#include "connect.h"
#include "send-pack.h"
#include "quote.h"
#include "transport.h"
#include "version.h"
#include "oid-array.h"
#include "gpg-interface.h"
#include "gettext.h"
#include "protocol.h"
static const char * const send_pack_usage[] = {
N_("git send-pack [--mirror] [--dry-run] [--force]\n"
" [--receive-pack=<git-receive-pack>]\n"
" [--verbose] [--thin] [--atomic]\n"
" [--[no-]signed | --signed=(true|false|if-asked)]\n"
" [<host>:]<directory> (--all | <ref>...)"),
NULL,
};
static struct send_pack_args args;
static void print_helper_status(struct ref *ref)
{
struct strbuf buf = STRBUF_INIT;
2020-08-27 15:45:46 +00:00
struct ref_push_report *report;
for (; ref; ref = ref->next) {
const char *msg = NULL;
const char *res;
2020-08-27 15:45:46 +00:00
int count = 0;
switch(ref->status) {
case REF_STATUS_NONE:
res = "error";
msg = "no match";
break;
case REF_STATUS_OK:
res = "ok";
break;
case REF_STATUS_UPTODATE:
res = "ok";
msg = "up to date";
break;
case REF_STATUS_REJECT_NONFASTFORWARD:
res = "error";
msg = "non-fast forward";
break;
push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE When we push to update an existing ref, if: * the object at the tip of the remote is not a commit; or * the object we are pushing is not a commit, it won't be correct to suggest to fetch, integrate and push again, as the old and new objects will not "merge". We should explain that the push must be forced when there is a non-committish object is involved in such a case. If we do not have the current object at the tip of the remote, we do not even know that object, when fetched, is something that can be merged. In such a case, suggesting to pull first just like non-fast-forward case may not be technically correct, but in practice, most such failures are seen when you try to push your work to a branch without knowing that somebody else already pushed to update the same branch since you forked, so "pull first" would work as a suggestion most of the time. And if the object at the tip is not a commit, "pull first" will fail, without making any permanent damage. As a side effect, it also makes the error message the user will get during the next "push" attempt easier to understand, now the user is aware that a non-commit object is involved. In these cases, the current code already rejects such a push on the client end, but we used the same error and advice messages as the ones used when rejecting a non-fast-forward push, i.e. pull from there and integrate before pushing again. Introduce new rejection reasons and reword the messages appropriately. [jc: with help by Peff on message details] Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-23 21:55:30 +00:00
case REF_STATUS_REJECT_FETCH_FIRST:
res = "error";
msg = "fetch first";
break;
case REF_STATUS_REJECT_NEEDS_FORCE:
res = "error";
msg = "needs force";
break;
case REF_STATUS_REJECT_STALE:
res = "error";
msg = "stale info";
break;
case REF_STATUS_REJECT_REMOTE_UPDATED:
res = "error";
msg = "remote ref updated since checkout";
break;
case REF_STATUS_REJECT_ALREADY_EXISTS:
res = "error";
msg = "already exists";
break;
case REF_STATUS_REJECT_NODELETE:
case REF_STATUS_REMOTE_REJECT:
res = "error";
break;
case REF_STATUS_EXPECTING_REPORT:
send-pack: complain about "expecting report" with --helper-status When pushing to a server which erroneously omits the final ref-status report, the client side should complain about the refs for which we didn't receive the status (because we can't just assume they were updated). This works over most transports like ssh, but for http we'll print a very misleading "Everything up-to-date". It works for ssh because send-pack internally sets the status of each ref to REF_STATUS_EXPECTING_REPORT, and then if the server doesn't tell us about a particular ref, it will stay at that value. When we print the final status table, we'll see that we're still on EXPECTING_REPORT and complain then. But for http, we go through remote-curl, which invokes send-pack with "--stateless-rpc --helper-status". The latter option causes send-pack to return a machine-readable list of ref statuses to the remote helper. But ever since its inception in de1a2fdd38 (Smart push over HTTP: client side, 2009-10-30), the send-pack code has simply omitted mention of any ref which ended up in EXPECTING_REPORT. In the remote helper, we then take the absence of any status report from send-pack to mean that the ref was not even something we tried to send, and thus it prints "Everything up-to-date". Fortunately it does detect the eventual non-zero exit from send-pack, and propagates that in its own non-zero exit code. So at least a careful script invoking "git push" would notice the failure. But sending the misleading message on stderr is certainly confusing for humans (not to mention the machine-readable "push --porcelain" output, though again, any careful script should be checking the exit code from push, too). Nobody seems to have noticed because the server in this instance has to be misbehaving: it has promised to support the ref-status capability (otherwise the client will not set EXPECTING_REPORT at all), but didn't send us any. If the connection were simply cut, then send-pack would complain about getting EOF while trying to read the status. But if the server actually sends a flush packet (i.e., saying "now you have all of the ref statuses" without actually sending any), then the client ends up in this confused situation. The fix is simple: we should return an error message from "send-pack --helper-status", just like we would for any other error per-ref error condition (in the test I included, the server simply omits all ref status responses, but a more insidious version of this would skip only some of them). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-18 19:43:47 +00:00
res = "error";
msg = "expecting report";
break;
default:
continue;
}
strbuf_reset(&buf);
strbuf_addf(&buf, "%s %s", res, ref->name);
if (ref->remote_status)
msg = ref->remote_status;
if (msg) {
strbuf_addch(&buf, ' ');
quote_two_c_style(&buf, "", msg, 0);
}
strbuf_addch(&buf, '\n');
2020-08-27 15:45:46 +00:00
if (ref->status == REF_STATUS_OK) {
for (report = ref->report; report; report = report->next) {
if (count++ > 0)
strbuf_addf(&buf, "ok %s\n", ref->name);
if (report->ref_name)
strbuf_addf(&buf, "option refname %s\n",
report->ref_name);
if (report->old_oid)
strbuf_addf(&buf, "option old-oid %s\n",
oid_to_hex(report->old_oid));
if (report->new_oid)
strbuf_addf(&buf, "option new-oid %s\n",
oid_to_hex(report->new_oid));
if (report->forced_update)
strbuf_addstr(&buf, "option forced-update\n");
}
}
write_or_die(1, buf.buf, buf.len);
}
strbuf_release(&buf);
}
static int send_pack_config(const char *k, const char *v, void *cb)
{
git_gpg_config(k, v, NULL);
if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
switch (git_parse_maybe_bool(value)) {
case 0:
args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
break;
case 1:
args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
break;
default:
if (value && !strcasecmp(value, "if-asked"))
args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
else
return error(_("invalid value for '%s'"), k);
}
}
}
return git_default_config(k, v, cb);
}
int cmd_send_pack(int argc, const char **argv, const char *prefix)
{
struct refspec rs = REFSPEC_INIT_PUSH;
const char *remote_name = NULL;
struct remote *remote = NULL;
const char *dest = NULL;
int fd[2];
struct child_process *conn;
struct oid_array extra_have = OID_ARRAY_INIT;
struct oid_array shallow = OID_ARRAY_INIT;
struct ref *remote_refs, *local_refs;
int ret;
int helper_status = 0;
int send_all = 0;
int verbose = 0;
const char *receivepack = "git-receive-pack";
unsigned dry_run = 0;
unsigned send_mirror = 0;
unsigned force_update = 0;
unsigned quiet = 0;
int push_cert = 0;
struct string_list push_options = STRING_LIST_INIT_NODUP;
unsigned use_thin_pack = 0;
unsigned atomic = 0;
unsigned stateless_rpc = 0;
int flags;
unsigned int reject_reasons;
int progress = -1;
int from_stdin = 0;
remote.c: add command line option parser for "--force-with-lease" Update "git push" and "git send-pack" to parse this commnd line option. The intended sematics is: * "--force-with-lease" alone, without specifying the details, will protect _all_ remote refs that are going to be updated by requiring their current value to be the same as some reasonable default, unless otherwise specified; * "--force-with-lease=refname", without specifying the expected value, will protect that refname, if it is going to be updated, by requiring its current value to be the same as some reasonable default. * "--force-with-lease=refname:value" will protect that refname, if it is going to be updated, by requiring its current value to be the same as the specified value; and * "--no-force-with-lease" will cancel all the previous --force-with-lease on the command line. For now, "some reasonable default" is tentatively defined as "the value of the remote-tracking branch we have for the ref of the remote being updated", and it is an error if we do not have such a remote-tracking branch. But this is known to be fragile, its use is not yet recommended, and hopefully we will find more reasonable default as we gain experience with this feature. The manual marks the feature as experimental unless the expected value is specified explicitly for this reason. Because the command line options are parsed _before_ we know which remote we are pushing to, there needs further processing to the parsed data after we instantiate the transport object to: * expand "refname" given by the user to a full refname to be matched with the list of "struct ref" used in match_push_refs() and set_ref_status_for_push(); and * learning the actual local ref that is the remote-tracking branch for the specified remote ref. Further, some processing need to be deferred until we find the set of remote refs and match_push_refs() returns in order to find the ones that need to be checked after explicit ones have been processed for "--force-with-lease" (no specific details). These post-processing will be the topic of the next patch. This option was originally called "cas" (for "compare and swap"), the name which nobody liked because it was too technical. The second attempt called it "lockref" (because it is conceptually like pushing after taking a lock) but the word "lock" was hated because it implied that it may reject push by others, which is not the way this option works. This round calls it "force-with-lease". You assume you took the lease on the ref when you fetched to decide what the rebased history should be, and you can push back only if the lease has not been broken. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 22:34:36 +00:00
struct push_cas_option cas = {0};
int force_if_includes = 0;
struct packet_reader reader;
struct option options[] = {
OPT__VERBOSITY(&verbose),
OPT_STRING(0, "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
OPT_STRING(0, "exec", &receivepack, "receive-pack", N_("receive pack program")),
OPT_STRING(0, "remote", &remote_name, "remote", N_("remote name")),
OPT_BOOL(0, "all", &send_all, N_("push all refs")),
OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")),
OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")),
OPT_BOOL('f', "force", &force_update, N_("force updates")),
OPT_CALLBACK_F(0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
PARSE_OPT_OPTARG, option_parse_push_signed),
OPT_STRING_LIST(0, "push-option", &push_options,
N_("server-specific"),
N_("option to transmit")),
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
N_("require old value of ref to be at this value"),
PARSE_OPT_OPTARG, parseopt_push_cas_option),
OPT_BOOL(0, TRANS_OPT_FORCE_IF_INCLUDES, &force_if_includes,
N_("require remote updates to be integrated locally")),
OPT_END()
};
git_config(send_pack_config, NULL);
argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);
if (argc > 0) {
dest = argv[0];
refspec_appendn(&rs, argv + 1, argc - 1);
}
if (!dest)
usage_with_options(send_pack_usage, options);
args.verbose = verbose;
args.dry_run = dry_run;
args.send_mirror = send_mirror;
args.force_update = force_update;
args.quiet = quiet;
args.push_cert = push_cert;
args.progress = progress;
args.use_thin_pack = use_thin_pack;
args.atomic = atomic;
args.stateless_rpc = stateless_rpc;
args.push_options = push_options.nr ? &push_options : NULL;
args.url = dest;
if (from_stdin) {
if (args.stateless_rpc) {
const char *buf;
while ((buf = packet_read_line(0, NULL)))
refspec_append(&rs, buf);
} else {
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, stdin) != EOF)
refspec_append(&rs, line.buf);
strbuf_release(&line);
}
}
/*
* --all and --mirror are incompatible; neither makes sense
* with any refspecs.
*/
if ((rs.nr > 0 && (send_all || args.send_mirror)) ||
(send_all && args.send_mirror))
usage_with_options(send_pack_usage, options);
if (remote_name) {
remote = remote_get(remote_name);
if (!remote_has_url(remote, dest)) {
die("Destination %s is not a uri for %s",
dest, remote_name);
}
}
if (progress == -1)
progress = !args.quiet && isatty(2);
args.progress = progress;
if (args.stateless_rpc) {
conn = NULL;
fd[0] = 0;
fd[1] = 1;
} else {
conn = git_connect(fd, dest, receivepack,
args.verbose ? CONNECT_VERBOSE : 0);
}
packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
switch (discover_version(&reader)) {
case protocol_v2:
die("support for protocol v2 not implemented yet");
break;
case protocol_v1:
case protocol_v0:
get_remote_heads(&reader, &remote_refs, REF_NORMAL,
&extra_have, &shallow);
break;
case protocol_unknown_version:
BUG("unknown protocol version");
}
local_refs = get_local_heads();
flags = MATCH_REFS_NONE;
if (send_all)
flags |= MATCH_REFS_ALL;
if (args.send_mirror)
flags |= MATCH_REFS_MIRROR;
/* match them up */
if (match_push_refs(local_refs, &remote_refs, &rs, flags))
return -1;
if (!is_empty_cas(&cas))
apply_push_cas(&cas, remote, remote_refs);
if (!is_empty_cas(&cas) && force_if_includes)
cas.use_force_if_includes = 1;
set_ref_status_for_push(remote_refs, args.send_mirror,
args.force_update);
ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
if (helper_status)
print_helper_status(remote_refs);
close(fd[1]);
close(fd[0]);
ret |= finish_connect(conn);
if (!helper_status)
transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_reasons);
if (!args.dry_run && remote) {
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next)
transport_update_tracking_ref(remote, ref, args.verbose);
}
if (!ret && !transport_refs_pushed(remote_refs))
fprintf(stderr, "Everything up-to-date\n");
return ret;
}