2009-08-05 13:01:53 +08:00
|
|
|
#include "cache.h"
|
|
|
|
#include "transport.h"
|
2009-10-31 08:47:30 +08:00
|
|
|
#include "quote.h"
|
2009-08-05 13:01:53 +08:00
|
|
|
#include "run-command.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
2009-11-18 09:42:28 +08:00
|
|
|
#include "remote.h"
|
2010-03-30 00:48:27 +08:00
|
|
|
#include "string-list.h"
|
2010-11-18 01:15:34 +08:00
|
|
|
#include "thread-utils.h"
|
disconnect from remote helpers more gently
When git spawns a remote helper program (like git-remote-http),
the last thing we do before closing the pipe to the child
process is to send a blank line, telling the helper that we
are done issuing commands. However, the helper may already
have exited, in which case the parent git process will
receive SIGPIPE and die.
In particular, this can happen with the remote-curl helper
when it encounters errors during a push. The helper reports
individual errors for each ref back to git-push, and then
exits with a non-zero exit code. Depending on the exact
timing of the write, the parent process may or may not
receive SIGPIPE.
This causes intermittent test failure in t5541.8, and is a
side effect of 5238cbf (remote-curl: Fix push status report
when all branches fail). Before that commit, remote-curl
would not send the final blank line to indicate that the
list of status lines was complete; it would just exit,
closing the pipe. The parent git-push would notice the
closed pipe while reading the status report and exit
immediately itself, propagating the failing exit code. But
post-5238cbf, remote-curl completes the status list before
exiting, git-push actually runs to completion, and then it
tries to cleanly disconnect the helper, leading to the
SIGPIPE race above.
This patch drops all error-checking when sending the final
"we are about to hang up" blank line to helpers. There is
nothing useful for the parent process to do about errors at
that point anyway, and certainly failing to send our "we are
done with commands" line to a helper that has already exited
is not a problem.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-02-23 18:04:34 +08:00
|
|
|
#include "sigchain.h"
|
2012-09-19 23:21:19 +08:00
|
|
|
#include "argv-array.h"
|
2013-04-18 12:14:33 +08:00
|
|
|
#include "refs.h"
|
2018-05-17 06:57:48 +08:00
|
|
|
#include "refspec.h"
|
2017-12-15 05:44:45 +08:00
|
|
|
#include "transport-internal.h"
|
2018-03-16 01:31:34 +08:00
|
|
|
#include "protocol.h"
|
2010-11-18 01:15:34 +08:00
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
static int debug;
|
|
|
|
|
2011-03-16 15:08:34 +08:00
|
|
|
struct helper_data {
|
2009-08-05 13:01:53 +08:00
|
|
|
const char *name;
|
|
|
|
struct child_process *helper;
|
2009-10-31 08:47:28 +08:00
|
|
|
FILE *out;
|
2009-10-31 08:47:29 +08:00
|
|
|
unsigned fetch : 1,
|
2009-12-07 14:40:16 +08:00
|
|
|
import : 1,
|
2012-09-19 23:21:19 +08:00
|
|
|
bidi_import : 1,
|
2010-03-30 00:48:27 +08:00
|
|
|
export : 1,
|
2009-10-31 08:47:30 +08:00
|
|
|
option : 1,
|
2009-12-09 23:26:32 +08:00
|
|
|
push : 1,
|
|
|
|
connect : 1,
|
2018-03-16 01:31:34 +08:00
|
|
|
stateless_connect : 1,
|
2013-04-14 18:57:08 +08:00
|
|
|
signed_tags : 1,
|
2013-07-21 16:18:05 +08:00
|
|
|
check_connectivity : 1,
|
2013-09-03 23:45:14 +08:00
|
|
|
no_disconnect_req : 1,
|
2020-05-26 03:59:03 +08:00
|
|
|
no_private_update : 1,
|
|
|
|
object_format : 1;
|
transport-helper: skip ls-refs if unnecessary
Commit e70a3030e7 ("fetch: do not list refs if fetching only hashes",
2018-10-07) and its ancestors taught Git, as an optimization, to skip
the ls-refs step when it is not necessary during a protocol v2 fetch
(for example, when lazy fetching a missing object in a partial clone, or
when running "git fetch --no-tags <remote> <SHA-1>"). But that was only
done for natively supported protocols; in particular, HTTP was not
supported.
Teach Git to skip ls-refs when using remote helpers that support connect
or stateless-connect. To do this, fetch() is made an acceptable entry
point. Because fetch() can now be the first function in the vtable
called, "get_helper(transport);" has to be added to the beginning of
that function to set the transport up (if not yet set up) before
process_connect() is invoked.
When fetch() is called, the transport could be taken over (this happens
if "connect" or "stateless-connect" is successfully run without any
"fallback" response), or not. If the transport is taken over, execution
continues like execution for natively supported protocols
(fetch_refs_via_pack() is executed, which will fetch refs using ls-refs
if needed). If not, the remote helper interface will invoke
get_refs_list() if it hasn't been invoked yet, preserving existing
behavior.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-22 06:20:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* As an optimization, the transport code may invoke fetch before
|
|
|
|
* get_refs_list. If this happens, and if the transport helper doesn't
|
|
|
|
* support connect or stateless_connect, we need to invoke
|
|
|
|
* get_refs_list ourselves if we haven't already done so. Keep track of
|
|
|
|
* whether we have invoked get_refs_list.
|
|
|
|
*/
|
|
|
|
unsigned get_refs_list_called : 1;
|
|
|
|
|
2011-07-16 21:03:40 +08:00
|
|
|
char *export_marks;
|
|
|
|
char *import_marks;
|
2009-11-18 09:42:28 +08:00
|
|
|
/* These go from remote name (as in "list") to private name */
|
2018-05-17 06:58:03 +08:00
|
|
|
struct refspec rs;
|
2009-12-09 23:26:31 +08:00
|
|
|
/* Transport options for fetch-pack/send-pack (should one of
|
|
|
|
* those be invoked).
|
|
|
|
*/
|
|
|
|
struct git_transport_options transport_options;
|
2009-08-05 13:01:53 +08:00
|
|
|
};
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
static void sendline(struct helper_data *helper, struct strbuf *buffer)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
|
avoid "write_in_full(fd, buf, len) != len" pattern
The return value of write_in_full() is either "-1", or the
requested number of bytes[1]. If we make a partial write
before seeing an error, we still return -1, not a partial
value. This goes back to f6aa66cb95 (write_in_full: really
write in full or return error on disk full., 2007-01-11).
So checking anything except "was the return value negative"
is pointless. And there are a couple of reasons not to do
so:
1. It can do a funny signed/unsigned comparison. If your
"len" is signed (e.g., a size_t) then the compiler will
promote the "-1" to its unsigned variant.
This works out for "!= len" (unless you really were
trying to write the maximum size_t bytes), but is a
bug if you check "< len" (an example of which was fixed
recently in config.c).
We should avoid promoting the mental model that you
need to check the length at all, so that new sites are
not tempted to copy us.
2. Checking for a negative value is shorter to type,
especially when the length is an expression.
3. Linus says so. In d34cf19b89 (Clean up write_in_full()
users, 2007-01-11), right after the write_in_full()
semantics were changed, he wrote:
I really wish every "write_in_full()" user would just
check against "<0" now, but this fixes the nasty and
stupid ones.
Appeals to authority aside, this makes it clear that
writing it this way does not have an intentional
benefit. It's a historical curiosity that we never
bothered to clean up (and which was undoubtedly
cargo-culted into new sites).
So let's convert these obviously-correct cases (this
includes write_str_in_full(), which is just a wrapper for
write_in_full()).
[1] A careful reader may notice there is one way that
write_in_full() can return a different value. If we ask
write() to write N bytes and get a return value that is
_larger_ than N, we could return a larger total. But
besides the fact that this would imply a totally broken
version of write(), it would already invoke undefined
behavior. Our internal remaining counter is an unsigned
size_t, which means that subtracting too many byte will
wrap it around to a very large number. So we'll instantly
begin reading off the end of the buffer, trying to write
gigabytes (or petabytes) of data.
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-14 01:16:03 +08:00
|
|
|
if (write_in_full(helper->helper->in, buffer->buf, buffer->len) < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die_errno(_("full write to remote helper failed"));
|
2009-12-09 23:26:27 +08:00
|
|
|
}
|
|
|
|
|
2018-03-16 01:31:32 +08:00
|
|
|
static int recvline_fh(FILE *helper, struct strbuf *buffer)
|
2009-12-09 23:26:27 +08:00
|
|
|
{
|
|
|
|
strbuf_reset(buffer);
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Remote helper: Waiting...\n");
|
2015-10-29 04:36:00 +08:00
|
|
|
if (strbuf_getline(buffer, helper) == EOF) {
|
2009-12-09 23:26:27 +08:00
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Remote helper quit.\n");
|
2014-04-13 04:33:29 +08:00
|
|
|
return 1;
|
2009-12-09 23:26:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-09 23:26:32 +08:00
|
|
|
static int recvline(struct helper_data *helper, struct strbuf *buffer)
|
|
|
|
{
|
2018-03-16 01:31:32 +08:00
|
|
|
return recvline_fh(helper->out, buffer);
|
2009-12-09 23:26:32 +08:00
|
|
|
}
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
static void write_constant(int fd, const char *str)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Remote helper: -> %s", str);
|
avoid "write_in_full(fd, buf, len) != len" pattern
The return value of write_in_full() is either "-1", or the
requested number of bytes[1]. If we make a partial write
before seeing an error, we still return -1, not a partial
value. This goes back to f6aa66cb95 (write_in_full: really
write in full or return error on disk full., 2007-01-11).
So checking anything except "was the return value negative"
is pointless. And there are a couple of reasons not to do
so:
1. It can do a funny signed/unsigned comparison. If your
"len" is signed (e.g., a size_t) then the compiler will
promote the "-1" to its unsigned variant.
This works out for "!= len" (unless you really were
trying to write the maximum size_t bytes), but is a
bug if you check "< len" (an example of which was fixed
recently in config.c).
We should avoid promoting the mental model that you
need to check the length at all, so that new sites are
not tempted to copy us.
2. Checking for a negative value is shorter to type,
especially when the length is an expression.
3. Linus says so. In d34cf19b89 (Clean up write_in_full()
users, 2007-01-11), right after the write_in_full()
semantics were changed, he wrote:
I really wish every "write_in_full()" user would just
check against "<0" now, but this fixes the nasty and
stupid ones.
Appeals to authority aside, this makes it clear that
writing it this way does not have an intentional
benefit. It's a historical curiosity that we never
bothered to clean up (and which was undoubtedly
cargo-culted into new sites).
So let's convert these obviously-correct cases (this
includes write_str_in_full(), which is just a wrapper for
write_in_full()).
[1] A careful reader may notice there is one way that
write_in_full() can return a different value. If we ask
write() to write N bytes and get a return value that is
_larger_ than N, we could return a larger total. But
besides the fact that this would imply a totally broken
version of write(), it would already invoke undefined
behavior. Our internal remaining counter is an unsigned
size_t, which means that subtracting too many byte will
wrap it around to a very large number. So we'll instantly
begin reading off the end of the buffer, trying to write
gigabytes (or petabytes) of data.
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-14 01:16:03 +08:00
|
|
|
if (write_in_full(fd, str, strlen(str)) < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die_errno(_("full write to remote helper failed"));
|
2009-12-09 23:26:27 +08:00
|
|
|
}
|
|
|
|
|
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 15:51:05 +08:00
|
|
|
static const char *remove_ext_force(const char *url)
|
2009-12-09 23:26:29 +08:00
|
|
|
{
|
|
|
|
if (url) {
|
|
|
|
const char *colon = strchr(url, ':');
|
|
|
|
if (colon && colon[1] == ':')
|
|
|
|
return colon + 2;
|
|
|
|
}
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
2009-12-09 23:26:32 +08:00
|
|
|
static void do_take_over(struct transport *transport)
|
|
|
|
{
|
|
|
|
struct helper_data *data;
|
|
|
|
data = (struct helper_data *)transport->data;
|
|
|
|
transport_take_over(transport, data->helper);
|
|
|
|
fclose(data->out);
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
|
2015-02-13 13:24:45 +08:00
|
|
|
static void standard_options(struct transport *t);
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
static struct child_process *get_helper(struct transport *transport)
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct child_process *helper;
|
2009-12-09 23:26:31 +08:00
|
|
|
int duped;
|
2010-01-13 03:53:29 +08:00
|
|
|
int code;
|
2009-08-05 13:01:53 +08:00
|
|
|
|
|
|
|
if (data->helper)
|
|
|
|
return data->helper;
|
|
|
|
|
2014-08-20 03:10:48 +08:00
|
|
|
helper = xmalloc(sizeof(*helper));
|
|
|
|
child_process_init(helper);
|
2009-08-05 13:01:53 +08:00
|
|
|
helper->in = -1;
|
|
|
|
helper->out = -1;
|
|
|
|
helper->err = 0;
|
2014-05-15 16:34:18 +08:00
|
|
|
argv_array_pushf(&helper->args, "git-remote-%s", data->name);
|
|
|
|
argv_array_push(&helper->args, transport->remote->name);
|
|
|
|
argv_array_push(&helper->args, remove_ext_force(transport->url));
|
2010-01-13 03:53:29 +08:00
|
|
|
helper->git_cmd = 0;
|
|
|
|
helper->silent_exec_failure = 1;
|
2011-07-16 21:03:28 +08:00
|
|
|
|
2017-02-15 04:36:19 +08:00
|
|
|
if (have_git_dir())
|
|
|
|
argv_array_pushf(&helper->env_array, "%s=%s",
|
|
|
|
GIT_DIR_ENVIRONMENT, get_git_dir());
|
2011-07-16 21:03:28 +08:00
|
|
|
|
2019-02-23 06:25:05 +08:00
|
|
|
helper->trace2_child_class = helper->args.argv[0]; /* "remote-<name>" */
|
|
|
|
|
2010-01-13 03:53:29 +08:00
|
|
|
code = start_command(helper);
|
|
|
|
if (code < 0 && errno == ENOENT)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("unable to find remote helper for '%s'"), data->name);
|
2010-01-13 03:53:29 +08:00
|
|
|
else if (code != 0)
|
|
|
|
exit(code);
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
data->helper = helper;
|
2009-12-09 23:26:32 +08:00
|
|
|
data->no_disconnect_req = 0;
|
2018-05-17 06:58:03 +08:00
|
|
|
refspec_init(&data->rs, REFSPEC_FETCH);
|
2009-08-05 13:01:53 +08:00
|
|
|
|
2009-12-09 23:26:31 +08:00
|
|
|
/*
|
2016-01-14 10:32:23 +08:00
|
|
|
* Open the output as FILE* so strbuf_getline_*() family of
|
|
|
|
* functions can be used.
|
2009-12-09 23:26:31 +08:00
|
|
|
* Do this with duped fd because fclose() will close the fd,
|
|
|
|
* and stuff like taking over will require the fd to remain.
|
|
|
|
*/
|
|
|
|
duped = dup(helper->out);
|
|
|
|
if (duped < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die_errno(_("can't dup helper output fd"));
|
2009-12-09 23:26:31 +08:00
|
|
|
data->out = xfdopen(duped, "r");
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
write_constant(helper->in, "capabilities\n");
|
2009-09-04 10:13:51 +08:00
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
while (1) {
|
2014-06-19 03:47:17 +08:00
|
|
|
const char *capname, *arg;
|
2009-12-09 23:26:28 +08:00
|
|
|
int mandatory = 0;
|
2014-04-13 04:33:29 +08:00
|
|
|
if (recvline(data, &buf))
|
|
|
|
exit(128);
|
2009-08-05 13:01:53 +08:00
|
|
|
|
|
|
|
if (!*buf.buf)
|
|
|
|
break;
|
2009-12-09 23:26:28 +08:00
|
|
|
|
|
|
|
if (*buf.buf == '*') {
|
|
|
|
capname = buf.buf + 1;
|
|
|
|
mandatory = 1;
|
|
|
|
} else
|
|
|
|
capname = buf.buf;
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
if (debug)
|
2009-12-09 23:26:28 +08:00
|
|
|
fprintf(stderr, "Debug: Got cap %s\n", capname);
|
|
|
|
if (!strcmp(capname, "fetch"))
|
2009-08-05 13:01:53 +08:00
|
|
|
data->fetch = 1;
|
2009-12-09 23:26:28 +08:00
|
|
|
else if (!strcmp(capname, "option"))
|
2009-10-31 08:47:29 +08:00
|
|
|
data->option = 1;
|
2009-12-09 23:26:28 +08:00
|
|
|
else if (!strcmp(capname, "push"))
|
2009-10-31 08:47:30 +08:00
|
|
|
data->push = 1;
|
2009-12-09 23:26:28 +08:00
|
|
|
else if (!strcmp(capname, "import"))
|
2009-11-18 09:42:27 +08:00
|
|
|
data->import = 1;
|
2012-09-19 23:21:19 +08:00
|
|
|
else if (!strcmp(capname, "bidi-import"))
|
|
|
|
data->bidi_import = 1;
|
2010-03-30 00:48:27 +08:00
|
|
|
else if (!strcmp(capname, "export"))
|
|
|
|
data->export = 1;
|
2013-07-21 16:18:05 +08:00
|
|
|
else if (!strcmp(capname, "check-connectivity"))
|
|
|
|
data->check_connectivity = 1;
|
2018-05-17 06:58:03 +08:00
|
|
|
else if (skip_prefix(capname, "refspec ", &arg)) {
|
|
|
|
refspec_append(&data->rs, arg);
|
2009-12-09 23:26:32 +08:00
|
|
|
} else if (!strcmp(capname, "connect")) {
|
|
|
|
data->connect = 1;
|
2018-03-16 01:31:34 +08:00
|
|
|
} else if (!strcmp(capname, "stateless-connect")) {
|
|
|
|
data->stateless_connect = 1;
|
2013-04-14 18:57:08 +08:00
|
|
|
} else if (!strcmp(capname, "signed-tags")) {
|
|
|
|
data->signed_tags = 1;
|
2014-06-19 03:47:17 +08:00
|
|
|
} else if (skip_prefix(capname, "export-marks ", &arg)) {
|
|
|
|
data->export_marks = xstrdup(arg);
|
|
|
|
} else if (skip_prefix(capname, "import-marks ", &arg)) {
|
|
|
|
data->import_marks = xstrdup(arg);
|
2013-12-01 04:55:40 +08:00
|
|
|
} else if (starts_with(capname, "no-private-update")) {
|
2013-09-03 23:45:14 +08:00
|
|
|
data->no_private_update = 1;
|
2020-05-26 03:59:03 +08:00
|
|
|
} else if (starts_with(capname, "object-format")) {
|
|
|
|
data->object_format = 1;
|
2009-12-09 23:26:28 +08:00
|
|
|
} else if (mandatory) {
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("unknown mandatory capability %s; this remote "
|
|
|
|
"helper probably needs newer version of Git"),
|
2009-12-09 23:26:28 +08:00
|
|
|
capname);
|
2009-11-18 09:42:28 +08:00
|
|
|
}
|
|
|
|
}
|
2018-05-17 06:58:03 +08:00
|
|
|
if (!data->rs.nr && (data->import || data->bidi_import || data->export)) {
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("this remote helper should implement refspec capability"));
|
2009-08-05 13:01:53 +08:00
|
|
|
}
|
2009-11-18 09:42:29 +08:00
|
|
|
strbuf_release(&buf);
|
2009-12-09 23:26:27 +08:00
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Capabilities complete.\n");
|
2015-02-13 13:24:45 +08:00
|
|
|
standard_options(transport);
|
2009-08-05 13:01:53 +08:00
|
|
|
return data->helper;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int disconnect_helper(struct transport *transport)
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
2011-07-16 21:03:35 +08:00
|
|
|
int res = 0;
|
2009-12-09 23:26:27 +08:00
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
if (data->helper) {
|
2009-12-09 23:26:27 +08:00
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Disconnecting.\n");
|
2009-12-09 23:26:32 +08:00
|
|
|
if (!data->no_disconnect_req) {
|
disconnect from remote helpers more gently
When git spawns a remote helper program (like git-remote-http),
the last thing we do before closing the pipe to the child
process is to send a blank line, telling the helper that we
are done issuing commands. However, the helper may already
have exited, in which case the parent git process will
receive SIGPIPE and die.
In particular, this can happen with the remote-curl helper
when it encounters errors during a push. The helper reports
individual errors for each ref back to git-push, and then
exits with a non-zero exit code. Depending on the exact
timing of the write, the parent process may or may not
receive SIGPIPE.
This causes intermittent test failure in t5541.8, and is a
side effect of 5238cbf (remote-curl: Fix push status report
when all branches fail). Before that commit, remote-curl
would not send the final blank line to indicate that the
list of status lines was complete; it would just exit,
closing the pipe. The parent git-push would notice the
closed pipe while reading the status report and exit
immediately itself, propagating the failing exit code. But
post-5238cbf, remote-curl completes the status list before
exiting, git-push actually runs to completion, and then it
tries to cleanly disconnect the helper, leading to the
SIGPIPE race above.
This patch drops all error-checking when sending the final
"we are about to hang up" blank line to helpers. There is
nothing useful for the parent process to do about errors at
that point anyway, and certainly failing to send our "we are
done with commands" line to a helper that has already exited
is not a problem.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-02-23 18:04:34 +08:00
|
|
|
/*
|
|
|
|
* Ignore write errors; there's nothing we can do,
|
|
|
|
* since we're about to close the pipe anyway. And the
|
|
|
|
* most likely error is EPIPE due to the helper dying
|
|
|
|
* to report an error itself.
|
|
|
|
*/
|
|
|
|
sigchain_push(SIGPIPE, SIG_IGN);
|
|
|
|
xwrite(data->helper->in, "\n", 1);
|
|
|
|
sigchain_pop(SIGPIPE);
|
2009-12-09 23:26:32 +08:00
|
|
|
}
|
2009-08-05 13:01:53 +08:00
|
|
|
close(data->helper->in);
|
2009-12-09 23:26:31 +08:00
|
|
|
close(data->helper->out);
|
2009-10-31 08:47:28 +08:00
|
|
|
fclose(data->out);
|
2011-07-16 21:03:35 +08:00
|
|
|
res = finish_command(data->helper);
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(data->helper);
|
2009-08-05 13:01:53 +08:00
|
|
|
}
|
2011-07-16 21:03:35 +08:00
|
|
|
return res;
|
2009-08-05 13:01:53 +08:00
|
|
|
}
|
|
|
|
|
2009-10-31 08:47:29 +08:00
|
|
|
static const char *unsupported_options[] = {
|
|
|
|
TRANS_OPT_UPLOADPACK,
|
|
|
|
TRANS_OPT_RECEIVEPACK,
|
|
|
|
TRANS_OPT_THIN,
|
|
|
|
TRANS_OPT_KEEP
|
|
|
|
};
|
2013-10-31 17:25:40 +08:00
|
|
|
|
2009-10-31 08:47:29 +08:00
|
|
|
static const char *boolean_options[] = {
|
|
|
|
TRANS_OPT_THIN,
|
|
|
|
TRANS_OPT_KEEP,
|
2014-09-16 05:59:00 +08:00
|
|
|
TRANS_OPT_FOLLOWTAGS,
|
fetch, upload-pack: --deepen=N extends shallow boundary by N commits
In git-fetch, --depth argument is always relative with the latest
remote refs. This makes it a bit difficult to cover this use case,
where the user wants to make the shallow history, say 3 levels
deeper. It would work if remote refs have not moved yet, but nobody
can guarantee that, especially when that use case is performed a
couple months after the last clone or "git fetch --depth". Also,
modifying shallow boundary using --depth does not work well with
clones created by --since or --not.
This patch fixes that. A new argument --deepen=<N> will add <N> more (*)
parent commits to the current history regardless of where remote refs
are.
Have/Want negotiation is still respected. So if remote refs move, the
server will send two chunks: one between "have" and "want" and another
to extend shallow history. In theory, the client could send no "want"s
in order to get the second chunk only. But the protocol does not allow
that. Either you send no want lines, which means ls-remote; or you
have to send at least one want line that carries deep-relative to the
server..
The main work was done by Dongcan Jiang. I fixed it up here and there.
And of course all the bugs belong to me.
(*) We could even support --deepen=<N> where <N> is negative. In that
case we can cut some history from the shallow clone. This operation
(and --depth=<shorter depth>) does not require interaction with remote
side (and more complicated to implement as a result).
Helped-by: Duy Nguyen <pclouds@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Dongcan Jiang <dongcan.jiang@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-12 18:54:09 +08:00
|
|
|
TRANS_OPT_DEEPEN_RELATIVE
|
2009-10-31 08:47:29 +08:00
|
|
|
};
|
|
|
|
|
2016-06-12 18:53:44 +08:00
|
|
|
static int strbuf_set_helper_option(struct helper_data *data,
|
|
|
|
struct strbuf *buf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sendline(data, buf);
|
|
|
|
if (recvline(data, buf))
|
|
|
|
exit(128);
|
|
|
|
|
|
|
|
if (!strcmp(buf->buf, "ok"))
|
|
|
|
ret = 0;
|
|
|
|
else if (starts_with(buf->buf, "error"))
|
|
|
|
ret = -1;
|
|
|
|
else if (!strcmp(buf->buf, "unsupported"))
|
|
|
|
ret = 1;
|
|
|
|
else {
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("%s unexpectedly said: '%s'"), data->name, buf->buf);
|
2016-06-12 18:53:44 +08:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-06-12 18:54:04 +08:00
|
|
|
static int string_list_set_helper_option(struct helper_data *data,
|
|
|
|
const char *name,
|
|
|
|
struct string_list *list)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int i, ret = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < list->nr; i++) {
|
|
|
|
strbuf_addf(&buf, "option %s ", name);
|
|
|
|
quote_c_style(list->items[i].string, &buf, NULL, 0);
|
|
|
|
strbuf_addch(&buf, '\n');
|
|
|
|
|
|
|
|
if ((ret = strbuf_set_helper_option(data, &buf)))
|
|
|
|
break;
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
}
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-31 08:47:29 +08:00
|
|
|
static int set_helper_option(struct transport *transport,
|
|
|
|
const char *name, const char *value)
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int i, ret, is_bool = 0;
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
get_helper(transport);
|
|
|
|
|
2009-10-31 08:47:29 +08:00
|
|
|
if (!data->option)
|
|
|
|
return 1;
|
|
|
|
|
2016-06-12 18:54:04 +08:00
|
|
|
if (!strcmp(name, "deepen-not"))
|
|
|
|
return string_list_set_helper_option(data, name,
|
|
|
|
(struct string_list *)value);
|
|
|
|
|
2009-10-31 08:47:29 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
|
|
|
|
if (!strcmp(name, unsupported_options[i]))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
|
|
|
|
if (!strcmp(name, boolean_options[i])) {
|
|
|
|
is_bool = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addf(&buf, "option %s ", name);
|
|
|
|
if (is_bool)
|
|
|
|
strbuf_addstr(&buf, value ? "true" : "false");
|
|
|
|
else
|
|
|
|
quote_c_style(value, &buf, NULL, 0);
|
|
|
|
strbuf_addch(&buf, '\n');
|
|
|
|
|
2016-06-12 18:53:44 +08:00
|
|
|
ret = strbuf_set_helper_option(data, &buf);
|
2009-10-31 08:47:29 +08:00
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void standard_options(struct transport *t)
|
|
|
|
{
|
|
|
|
char buf[16];
|
|
|
|
int v = t->verbose;
|
|
|
|
|
2010-02-24 20:50:26 +08:00
|
|
|
set_helper_option(t, "progress", t->progress ? "true" : "false");
|
2009-10-31 08:47:29 +08:00
|
|
|
|
2017-03-29 03:47:00 +08:00
|
|
|
xsnprintf(buf, sizeof(buf), "%d", v + 1);
|
2009-10-31 08:47:29 +08:00
|
|
|
set_helper_option(t, "verbosity", buf);
|
2016-02-03 12:09:14 +08:00
|
|
|
|
|
|
|
switch (t->family) {
|
|
|
|
case TRANSPORT_FAMILY_ALL:
|
|
|
|
/*
|
|
|
|
* this is already the default,
|
|
|
|
* do not break old remote helpers by setting "all" here
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
case TRANSPORT_FAMILY_IPV4:
|
|
|
|
set_helper_option(t, "family", "ipv4");
|
|
|
|
break;
|
|
|
|
case TRANSPORT_FAMILY_IPV6:
|
|
|
|
set_helper_option(t, "family", "ipv6");
|
|
|
|
break;
|
|
|
|
}
|
2009-10-31 08:47:29 +08:00
|
|
|
}
|
|
|
|
|
2009-11-18 09:42:21 +08:00
|
|
|
static int release_helper(struct transport *transport)
|
|
|
|
{
|
2011-07-16 21:03:35 +08:00
|
|
|
int res = 0;
|
2009-11-18 09:42:28 +08:00
|
|
|
struct helper_data *data = transport->data;
|
2018-05-17 06:58:03 +08:00
|
|
|
refspec_clear(&data->rs);
|
2011-07-16 21:03:35 +08:00
|
|
|
res = disconnect_helper(transport);
|
2009-11-18 09:42:21 +08:00
|
|
|
free(transport->data);
|
2011-07-16 21:03:35 +08:00
|
|
|
return res;
|
2009-11-18 09:42:21 +08:00
|
|
|
}
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
static int fetch_with_fetch(struct transport *transport,
|
2009-11-18 09:42:24 +08:00
|
|
|
int nr_heads, struct ref **to_fetch)
|
2009-08-05 13:01:53 +08:00
|
|
|
{
|
2009-10-31 08:47:28 +08:00
|
|
|
struct helper_data *data = transport->data;
|
2009-08-05 13:01:53 +08:00
|
|
|
int i;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_heads; i++) {
|
2009-08-05 13:01:59 +08:00
|
|
|
const struct ref *posn = to_fetch[i];
|
2009-08-05 13:01:53 +08:00
|
|
|
if (posn->status & REF_STATUS_UPTODATE)
|
|
|
|
continue;
|
2009-09-04 10:13:51 +08:00
|
|
|
|
|
|
|
strbuf_addf(&buf, "fetch %s %s\n",
|
2015-11-10 10:22:20 +08:00
|
|
|
oid_to_hex(&posn->old_oid),
|
transport-helper: do not request symbolic refs to remote helpers
A typical remote helper will return a `list` of refs containing a symbolic
ref HEAD, pointing to, e.g. refs/heads/master. In the case of a clone, all
the refs are being requested through `fetch` or `import`, including the
symbolic ref.
While this works properly, in some cases of a fetch, like `git fetch url`
or `git fetch origin HEAD`, or any fetch command involving a symbolic ref
without also fetching the corresponding ref it points to, the fetch command
fails with:
fatal: bad object 0000000000000000000000000000000000000000
error: <remote> did not send all necessary objects
(in the case the remote helper returned '?' values to the `list` command).
This is because there is only one ref given to fetch(), and it's not
further resolved to something at the end of fetch_with_import().
While this can be somehow handled in the remote helper itself, by adding
a refspec for the symbolic ref, and storing an explicit ref in a private
namespace, and then handling the `import` for that symbolic ref
specifically, very few existing remote helpers are actually doing that.
So, instead of requesting the exact list of wanted refs to remote helpers,
treat symbolic refs differently and request the ref they point to instead.
Then, resolve the symbolic refs values based on the pointed ref.
This assumes there is no more than one level of indirection (a symbolic
ref doesn't point to another symbolic ref).
Signed-off-by: Mike Hommey <mh@glandium.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-19 09:35:07 +08:00
|
|
|
posn->symref ? posn->symref : posn->name);
|
2009-10-31 08:47:28 +08:00
|
|
|
}
|
2009-09-04 10:13:51 +08:00
|
|
|
|
2009-10-31 08:47:28 +08:00
|
|
|
strbuf_addch(&buf, '\n');
|
2009-12-09 23:26:27 +08:00
|
|
|
sendline(data, &buf);
|
2009-10-31 08:47:28 +08:00
|
|
|
|
|
|
|
while (1) {
|
2020-01-31 03:35:46 +08:00
|
|
|
const char *name;
|
|
|
|
|
2014-04-13 04:33:29 +08:00
|
|
|
if (recvline(data, &buf))
|
|
|
|
exit(128);
|
2009-10-31 08:47:28 +08:00
|
|
|
|
2020-01-31 03:35:46 +08:00
|
|
|
if (skip_prefix(buf.buf, "lock ", &name)) {
|
fetch-pack: support more than one pack lockfile
Whenever a fetch results in a packfile being downloaded, a .keep file is
generated, so that the packfile can be preserved (from, say, a running
"git repack") until refs are written referring to the contents of the
packfile.
In a subsequent patch, a successful fetch using protocol v2 may result
in more than one .keep file being generated. Therefore, teach
fetch_pack() and the transport mechanism to support multiple .keep
files.
Implementation notes:
- builtin/fetch-pack.c normally does not generate .keep files, and thus
is unaffected by this or future changes. However, it has an
undocumented "--lock-pack" feature, used by remote-curl.c when
implementing the "fetch" remote helper command. In keeping with the
remote helper protocol, only one "lock" line will ever be written;
the rest will result in warnings to stderr. However, in practice,
warnings will never be written because the remote-curl.c "fetch" is
only used for protocol v0/v1 (which will not generate multiple .keep
files). (Protocol v2 uses the "stateless-connect" command, not the
"fetch" command.)
- connected.c has an optimization in that connectivity checks on a ref
need not be done if the target object is in a pack known to be
self-contained and connected. If there are multiple packfiles, this
optimization can no longer be done.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-06-11 04:57:22 +08:00
|
|
|
if (transport->pack_lockfiles.nr)
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("%s also locked %s"), data->name, name);
|
2009-10-31 08:47:28 +08:00
|
|
|
else
|
fetch-pack: support more than one pack lockfile
Whenever a fetch results in a packfile being downloaded, a .keep file is
generated, so that the packfile can be preserved (from, say, a running
"git repack") until refs are written referring to the contents of the
packfile.
In a subsequent patch, a successful fetch using protocol v2 may result
in more than one .keep file being generated. Therefore, teach
fetch_pack() and the transport mechanism to support multiple .keep
files.
Implementation notes:
- builtin/fetch-pack.c normally does not generate .keep files, and thus
is unaffected by this or future changes. However, it has an
undocumented "--lock-pack" feature, used by remote-curl.c when
implementing the "fetch" remote helper command. In keeping with the
remote helper protocol, only one "lock" line will ever be written;
the rest will result in warnings to stderr. However, in practice,
warnings will never be written because the remote-curl.c "fetch" is
only used for protocol v0/v1 (which will not generate multiple .keep
files). (Protocol v2 uses the "stateless-connect" command, not the
"fetch" command.)
- connected.c has an optimization in that connectivity checks on a ref
need not be done if the target object is in a pack known to be
self-contained and connected. If there are multiple packfiles, this
optimization can no longer be done.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-06-11 04:57:22 +08:00
|
|
|
string_list_append(&transport->pack_lockfiles,
|
|
|
|
name);
|
2009-10-31 08:47:28 +08:00
|
|
|
}
|
2013-07-21 16:18:05 +08:00
|
|
|
else if (data->check_connectivity &&
|
|
|
|
data->transport_options.check_self_contained_and_connected &&
|
|
|
|
!strcmp(buf.buf, "connectivity-ok"))
|
|
|
|
data->transport_options.self_contained_and_connected = 1;
|
2009-10-31 08:47:28 +08:00
|
|
|
else if (!buf.len)
|
|
|
|
break;
|
|
|
|
else
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf);
|
2009-08-05 13:01:53 +08:00
|
|
|
}
|
2009-10-31 08:47:28 +08:00
|
|
|
strbuf_release(&buf);
|
2009-08-05 13:01:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-18 09:42:27 +08:00
|
|
|
static int get_importer(struct transport *transport, struct child_process *fastimport)
|
|
|
|
{
|
|
|
|
struct child_process *helper = get_helper(transport);
|
2012-09-19 23:21:19 +08:00
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
int cat_blob_fd, code;
|
2014-08-20 03:10:48 +08:00
|
|
|
child_process_init(fastimport);
|
2019-05-16 08:37:35 +08:00
|
|
|
fastimport->in = xdup(helper->out);
|
2014-05-15 16:35:06 +08:00
|
|
|
argv_array_push(&fastimport->args, "fast-import");
|
fast-import: disallow "feature export-marks" by default
The fast-import stream command "feature export-marks=<path>" lets the
stream write marks to an arbitrary path. This may be surprising if you
are running fast-import against an untrusted input (which otherwise
cannot do anything except update Git objects and refs).
Let's disallow the use of this feature by default, and provide a
command-line option to re-enable it (you can always just use the
command-line --export-marks as well, but the in-stream version provides
an easy way for exporters to control the process).
This is a backwards-incompatible change, since the default is flipping
to the new, safer behavior. However, since the main users of the
in-stream versions would be import/export-based remote helpers, and
since we trust remote helpers already (which are already running
arbitrary code), we'll pass the new option by default when reading a
remote helper's stream. This should minimize the impact.
Note that the implementation isn't totally simple, as we have to work
around the fact that fast-import doesn't parse its command-line options
until after it has read any "feature" lines from the stream. This is how
it lets command-line options override in-stream. But in our case, it's
important to parse the new --allow-unsafe-features first.
There are three options for resolving this:
1. Do a separate "early" pass over the options. This is easy for us to
do because there are no command-line options that allow the
"unstuck" form (so there's no chance of us mistaking an argument
for an option), though it does introduce a risk of incorrect
parsing later (e.g,. if we convert to parse-options).
2. Move the option parsing phase back to the start of the program, but
teach the stream-reading code never to override an existing value.
This is tricky, because stream "feature" lines override each other
(meaning we'd have to start tracking the source for every option).
3. Accept that we might parse a "feature export-marks" line that is
forbidden, as long we don't _act_ on it until after we've parsed
the command line options.
This would, in fact, work with the current code, but only because
the previous patch fixed the export-marks parser to avoid touching
the filesystem.
So while it works, it does carry risk of somebody getting it wrong
in the future in a rather subtle and unsafe way.
I've gone with option (1) here as simple, safe, and unlikely to cause
regressions.
This fixes CVE-2019-1348.
Signed-off-by: Jeff King <peff@peff.net>
2019-08-30 02:37:26 +08:00
|
|
|
argv_array_push(&fastimport->args, "--allow-unsafe-features");
|
2014-05-15 16:35:06 +08:00
|
|
|
argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
|
2009-11-18 09:42:27 +08:00
|
|
|
|
2012-09-19 23:21:19 +08:00
|
|
|
if (data->bidi_import) {
|
|
|
|
cat_blob_fd = xdup(helper->in);
|
2014-05-15 16:35:06 +08:00
|
|
|
argv_array_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd);
|
2012-09-19 23:21:19 +08:00
|
|
|
}
|
2009-11-18 09:42:27 +08:00
|
|
|
fastimport->git_cmd = 1;
|
2012-09-19 23:21:19 +08:00
|
|
|
|
|
|
|
code = start_command(fastimport);
|
|
|
|
return code;
|
2009-11-18 09:42:27 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 00:48:27 +08:00
|
|
|
static int get_exporter(struct transport *transport,
|
|
|
|
struct child_process *fastexport,
|
|
|
|
struct string_list *revlist_args)
|
|
|
|
{
|
2011-07-16 21:03:40 +08:00
|
|
|
struct helper_data *data = transport->data;
|
2010-03-30 00:48:27 +08:00
|
|
|
struct child_process *helper = get_helper(transport);
|
2014-05-15 16:34:44 +08:00
|
|
|
int i;
|
2014-04-13 04:33:31 +08:00
|
|
|
|
2014-10-29 04:52:34 +08:00
|
|
|
child_process_init(fastexport);
|
2010-03-30 00:48:27 +08:00
|
|
|
|
|
|
|
/* we need to duplicate helper->in because we want to use it after
|
|
|
|
* fastexport is done with it. */
|
|
|
|
fastexport->out = dup(helper->in);
|
2014-05-15 16:34:44 +08:00
|
|
|
argv_array_push(&fastexport->args, "fast-export");
|
|
|
|
argv_array_push(&fastexport->args, "--use-done-feature");
|
|
|
|
argv_array_push(&fastexport->args, data->signed_tags ?
|
|
|
|
"--signed-tags=verbatim" : "--signed-tags=warn-strip");
|
|
|
|
if (data->export_marks)
|
|
|
|
argv_array_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks);
|
|
|
|
if (data->import_marks)
|
|
|
|
argv_array_pushf(&fastexport->args, "--import-marks=%s", data->import_marks);
|
2010-03-30 00:48:27 +08:00
|
|
|
|
|
|
|
for (i = 0; i < revlist_args->nr; i++)
|
2014-05-15 16:34:44 +08:00
|
|
|
argv_array_push(&fastexport->args, revlist_args->items[i].string);
|
2010-03-30 00:48:27 +08:00
|
|
|
|
|
|
|
fastexport->git_cmd = 1;
|
|
|
|
return start_command(fastexport);
|
|
|
|
}
|
|
|
|
|
2009-11-18 09:42:27 +08:00
|
|
|
static int fetch_with_import(struct transport *transport,
|
|
|
|
int nr_heads, struct ref **to_fetch)
|
|
|
|
{
|
|
|
|
struct child_process fastimport;
|
2009-11-18 09:42:28 +08:00
|
|
|
struct helper_data *data = transport->data;
|
2009-11-18 09:42:27 +08:00
|
|
|
int i;
|
|
|
|
struct ref *posn;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
get_helper(transport);
|
|
|
|
|
2009-11-18 09:42:27 +08:00
|
|
|
if (get_importer(transport, &fastimport))
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("couldn't run fast-import"));
|
2009-11-18 09:42:27 +08:00
|
|
|
|
|
|
|
for (i = 0; i < nr_heads; i++) {
|
|
|
|
posn = to_fetch[i];
|
|
|
|
if (posn->status & REF_STATUS_UPTODATE)
|
|
|
|
continue;
|
|
|
|
|
transport-helper: do not request symbolic refs to remote helpers
A typical remote helper will return a `list` of refs containing a symbolic
ref HEAD, pointing to, e.g. refs/heads/master. In the case of a clone, all
the refs are being requested through `fetch` or `import`, including the
symbolic ref.
While this works properly, in some cases of a fetch, like `git fetch url`
or `git fetch origin HEAD`, or any fetch command involving a symbolic ref
without also fetching the corresponding ref it points to, the fetch command
fails with:
fatal: bad object 0000000000000000000000000000000000000000
error: <remote> did not send all necessary objects
(in the case the remote helper returned '?' values to the `list` command).
This is because there is only one ref given to fetch(), and it's not
further resolved to something at the end of fetch_with_import().
While this can be somehow handled in the remote helper itself, by adding
a refspec for the symbolic ref, and storing an explicit ref in a private
namespace, and then handling the `import` for that symbolic ref
specifically, very few existing remote helpers are actually doing that.
So, instead of requesting the exact list of wanted refs to remote helpers,
treat symbolic refs differently and request the ref they point to instead.
Then, resolve the symbolic refs values based on the pointed ref.
This assumes there is no more than one level of indirection (a symbolic
ref doesn't point to another symbolic ref).
Signed-off-by: Mike Hommey <mh@glandium.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-19 09:35:07 +08:00
|
|
|
strbuf_addf(&buf, "import %s\n",
|
|
|
|
posn->symref ? posn->symref : posn->name);
|
2009-12-09 23:26:27 +08:00
|
|
|
sendline(data, &buf);
|
2009-11-18 09:42:27 +08:00
|
|
|
strbuf_reset(&buf);
|
|
|
|
}
|
2011-07-16 21:03:38 +08:00
|
|
|
|
|
|
|
write_constant(data->helper->in, "\n");
|
2012-09-19 23:21:19 +08:00
|
|
|
/*
|
|
|
|
* remote-helpers that advertise the bidi-import capability are required to
|
|
|
|
* buffer the complete batch of import commands until this newline before
|
|
|
|
* sending data to fast-import.
|
|
|
|
* These helpers read back data from fast-import on their stdin, which could
|
|
|
|
* be mixed with import commands, otherwise.
|
|
|
|
*/
|
2011-07-16 21:03:38 +08:00
|
|
|
|
2011-07-16 21:03:35 +08:00
|
|
|
if (finish_command(&fastimport))
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("error while running fast-import"));
|
2009-11-18 09:42:27 +08:00
|
|
|
|
2012-07-30 22:31:18 +08:00
|
|
|
/*
|
|
|
|
* The fast-import stream of a remote helper that advertises
|
|
|
|
* the "refspec" capability writes to the refs named after the
|
|
|
|
* right hand side of the first refspec matching each ref we
|
|
|
|
* were fetching.
|
|
|
|
*
|
|
|
|
* (If no "refspec" capability was specified, for historical
|
2013-04-18 12:14:28 +08:00
|
|
|
* reasons we default to the equivalent of *:*.)
|
2012-07-30 22:31:18 +08:00
|
|
|
*
|
|
|
|
* Store the result in to_fetch[i].old_sha1. Callers such
|
|
|
|
* as "git fetch" can use the value to write feedback to the
|
|
|
|
* terminal, populate FETCH_HEAD, and determine what new value
|
|
|
|
* should be written to peer_ref if the update is a
|
|
|
|
* fast-forward or this is a forced update.
|
|
|
|
*/
|
2009-11-18 09:42:27 +08:00
|
|
|
for (i = 0; i < nr_heads; i++) {
|
transport-helper: do not request symbolic refs to remote helpers
A typical remote helper will return a `list` of refs containing a symbolic
ref HEAD, pointing to, e.g. refs/heads/master. In the case of a clone, all
the refs are being requested through `fetch` or `import`, including the
symbolic ref.
While this works properly, in some cases of a fetch, like `git fetch url`
or `git fetch origin HEAD`, or any fetch command involving a symbolic ref
without also fetching the corresponding ref it points to, the fetch command
fails with:
fatal: bad object 0000000000000000000000000000000000000000
error: <remote> did not send all necessary objects
(in the case the remote helper returned '?' values to the `list` command).
This is because there is only one ref given to fetch(), and it's not
further resolved to something at the end of fetch_with_import().
While this can be somehow handled in the remote helper itself, by adding
a refspec for the symbolic ref, and storing an explicit ref in a private
namespace, and then handling the `import` for that symbolic ref
specifically, very few existing remote helpers are actually doing that.
So, instead of requesting the exact list of wanted refs to remote helpers,
treat symbolic refs differently and request the ref they point to instead.
Then, resolve the symbolic refs values based on the pointed ref.
This assumes there is no more than one level of indirection (a symbolic
ref doesn't point to another symbolic ref).
Signed-off-by: Mike Hommey <mh@glandium.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-19 09:35:07 +08:00
|
|
|
char *private, *name;
|
2009-11-18 09:42:27 +08:00
|
|
|
posn = to_fetch[i];
|
|
|
|
if (posn->status & REF_STATUS_UPTODATE)
|
|
|
|
continue;
|
transport-helper: do not request symbolic refs to remote helpers
A typical remote helper will return a `list` of refs containing a symbolic
ref HEAD, pointing to, e.g. refs/heads/master. In the case of a clone, all
the refs are being requested through `fetch` or `import`, including the
symbolic ref.
While this works properly, in some cases of a fetch, like `git fetch url`
or `git fetch origin HEAD`, or any fetch command involving a symbolic ref
without also fetching the corresponding ref it points to, the fetch command
fails with:
fatal: bad object 0000000000000000000000000000000000000000
error: <remote> did not send all necessary objects
(in the case the remote helper returned '?' values to the `list` command).
This is because there is only one ref given to fetch(), and it's not
further resolved to something at the end of fetch_with_import().
While this can be somehow handled in the remote helper itself, by adding
a refspec for the symbolic ref, and storing an explicit ref in a private
namespace, and then handling the `import` for that symbolic ref
specifically, very few existing remote helpers are actually doing that.
So, instead of requesting the exact list of wanted refs to remote helpers,
treat symbolic refs differently and request the ref they point to instead.
Then, resolve the symbolic refs values based on the pointed ref.
This assumes there is no more than one level of indirection (a symbolic
ref doesn't point to another symbolic ref).
Signed-off-by: Mike Hommey <mh@glandium.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-19 09:35:07 +08:00
|
|
|
name = posn->symref ? posn->symref : posn->name;
|
2018-05-17 06:58:03 +08:00
|
|
|
if (data->rs.nr)
|
2018-05-17 06:58:11 +08:00
|
|
|
private = apply_refspecs(&data->rs, name);
|
2009-11-18 09:42:28 +08:00
|
|
|
else
|
transport-helper: do not request symbolic refs to remote helpers
A typical remote helper will return a `list` of refs containing a symbolic
ref HEAD, pointing to, e.g. refs/heads/master. In the case of a clone, all
the refs are being requested through `fetch` or `import`, including the
symbolic ref.
While this works properly, in some cases of a fetch, like `git fetch url`
or `git fetch origin HEAD`, or any fetch command involving a symbolic ref
without also fetching the corresponding ref it points to, the fetch command
fails with:
fatal: bad object 0000000000000000000000000000000000000000
error: <remote> did not send all necessary objects
(in the case the remote helper returned '?' values to the `list` command).
This is because there is only one ref given to fetch(), and it's not
further resolved to something at the end of fetch_with_import().
While this can be somehow handled in the remote helper itself, by adding
a refspec for the symbolic ref, and storing an explicit ref in a private
namespace, and then handling the `import` for that symbolic ref
specifically, very few existing remote helpers are actually doing that.
So, instead of requesting the exact list of wanted refs to remote helpers,
treat symbolic refs differently and request the ref they point to instead.
Then, resolve the symbolic refs values based on the pointed ref.
This assumes there is no more than one level of indirection (a symbolic
ref doesn't point to another symbolic ref).
Signed-off-by: Mike Hommey <mh@glandium.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-19 09:35:07 +08:00
|
|
|
private = xstrdup(name);
|
2011-09-16 05:10:38 +08:00
|
|
|
if (private) {
|
2017-10-16 06:06:56 +08:00
|
|
|
if (read_ref(private, &posn->old_oid) < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("could not read ref %s"), private);
|
2011-09-16 05:10:38 +08:00
|
|
|
free(private);
|
|
|
|
}
|
2009-11-18 09:42:27 +08:00
|
|
|
}
|
2009-11-18 09:42:29 +08:00
|
|
|
strbuf_release(&buf);
|
2009-11-18 09:42:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-16 01:31:33 +08:00
|
|
|
static int run_connect(struct transport *transport, struct strbuf *cmdbuf)
|
2009-12-09 23:26:32 +08:00
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
2018-03-16 01:31:33 +08:00
|
|
|
int ret = 0;
|
|
|
|
int duped;
|
2009-12-09 23:26:32 +08:00
|
|
|
FILE *input;
|
2018-03-16 01:31:33 +08:00
|
|
|
struct child_process *helper;
|
2009-12-09 23:26:32 +08:00
|
|
|
|
|
|
|
helper = get_helper(transport);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Yes, dup the pipe another time, as we need unbuffered version
|
|
|
|
* of input pipe as FILE*. fclose() closes the underlying fd and
|
|
|
|
* stream buffering only can be changed before first I/O operation
|
|
|
|
* on it.
|
|
|
|
*/
|
|
|
|
duped = dup(helper->out);
|
|
|
|
if (duped < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die_errno(_("can't dup helper output fd"));
|
2009-12-09 23:26:32 +08:00
|
|
|
input = xfdopen(duped, "r");
|
|
|
|
setvbuf(input, NULL, _IONBF, 0);
|
|
|
|
|
2018-03-16 01:31:33 +08:00
|
|
|
sendline(data, cmdbuf);
|
|
|
|
if (recvline_fh(input, cmdbuf))
|
|
|
|
exit(128);
|
|
|
|
|
|
|
|
if (!strcmp(cmdbuf->buf, "")) {
|
|
|
|
data->no_disconnect_req = 1;
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Smart transport connection "
|
|
|
|
"ready.\n");
|
|
|
|
ret = 1;
|
|
|
|
} else if (!strcmp(cmdbuf->buf, "fallback")) {
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Falling back to dumb "
|
|
|
|
"transport.\n");
|
|
|
|
} else {
|
2018-11-27 03:57:56 +08:00
|
|
|
die(_("unknown response to connect: %s"),
|
2018-07-21 15:49:41 +08:00
|
|
|
cmdbuf->buf);
|
2018-03-16 01:31:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(input);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_connect_service(struct transport *transport,
|
|
|
|
const char *name, const char *exec)
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
struct strbuf cmdbuf = STRBUF_INIT;
|
|
|
|
int ret = 0;
|
|
|
|
|
2009-12-09 23:26:32 +08:00
|
|
|
/*
|
|
|
|
* Handle --upload-pack and friends. This is fire and forget...
|
|
|
|
* just warn if it fails.
|
|
|
|
*/
|
|
|
|
if (strcmp(name, exec)) {
|
2018-03-16 01:31:33 +08:00
|
|
|
int r = set_helper_option(transport, "servpath", exec);
|
2009-12-09 23:26:32 +08:00
|
|
|
if (r > 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("setting remote service path not supported by protocol"));
|
2009-12-09 23:26:32 +08:00
|
|
|
else if (r < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("invalid remote service path"));
|
2009-12-09 23:26:32 +08:00
|
|
|
}
|
|
|
|
|
2018-03-16 01:31:33 +08:00
|
|
|
if (data->connect) {
|
2009-12-09 23:26:32 +08:00
|
|
|
strbuf_addf(&cmdbuf, "connect %s\n", name);
|
2018-03-16 01:31:33 +08:00
|
|
|
ret = run_connect(transport, &cmdbuf);
|
2018-03-16 01:31:34 +08:00
|
|
|
} else if (data->stateless_connect &&
|
|
|
|
(get_protocol_version_config() == protocol_v2) &&
|
|
|
|
!strcmp("git-upload-pack", name)) {
|
|
|
|
strbuf_addf(&cmdbuf, "stateless-connect %s\n", name);
|
|
|
|
ret = run_connect(transport, &cmdbuf);
|
|
|
|
if (ret)
|
|
|
|
transport->stateless_rpc = 1;
|
2018-03-16 01:31:33 +08:00
|
|
|
}
|
2009-12-09 23:26:32 +08:00
|
|
|
|
2017-08-31 02:20:15 +08:00
|
|
|
strbuf_release(&cmdbuf);
|
2009-12-09 23:26:32 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_connect(struct transport *transport,
|
|
|
|
int for_push)
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
const char *name;
|
|
|
|
const char *exec;
|
|
|
|
|
|
|
|
name = for_push ? "git-receive-pack" : "git-upload-pack";
|
|
|
|
if (for_push)
|
|
|
|
exec = data->transport_options.receivepack;
|
|
|
|
else
|
|
|
|
exec = data->transport_options.uploadpack;
|
|
|
|
|
|
|
|
return process_connect_service(transport, name, exec);
|
|
|
|
}
|
|
|
|
|
2009-12-09 23:26:33 +08:00
|
|
|
static int connect_helper(struct transport *transport, const char *name,
|
|
|
|
const char *exec, int fd[2])
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
|
|
|
|
/* Get_helper so connect is inited. */
|
|
|
|
get_helper(transport);
|
|
|
|
if (!data->connect)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("operation not supported by protocol"));
|
2009-12-09 23:26:33 +08:00
|
|
|
|
|
|
|
if (!process_connect_service(transport, name, exec))
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("can't connect to subservice %s"), name);
|
2009-12-09 23:26:33 +08:00
|
|
|
|
|
|
|
fd[0] = data->helper->out;
|
|
|
|
fd[1] = data->helper->in;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
transport-helper: skip ls-refs if unnecessary
Commit e70a3030e7 ("fetch: do not list refs if fetching only hashes",
2018-10-07) and its ancestors taught Git, as an optimization, to skip
the ls-refs step when it is not necessary during a protocol v2 fetch
(for example, when lazy fetching a missing object in a partial clone, or
when running "git fetch --no-tags <remote> <SHA-1>"). But that was only
done for natively supported protocols; in particular, HTTP was not
supported.
Teach Git to skip ls-refs when using remote helpers that support connect
or stateless-connect. To do this, fetch() is made an acceptable entry
point. Because fetch() can now be the first function in the vtable
called, "get_helper(transport);" has to be added to the beginning of
that function to set the transport up (if not yet set up) before
process_connect() is invoked.
When fetch() is called, the transport could be taken over (this happens
if "connect" or "stateless-connect" is successfully run without any
"fallback" response), or not. If the transport is taken over, execution
continues like execution for natively supported protocols
(fetch_refs_via_pack() is executed, which will fetch refs using ls-refs
if needed). If not, the remote helper interface will invoke
get_refs_list() if it hasn't been invoked yet, preserving existing
behavior.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-22 06:20:09 +08:00
|
|
|
static struct ref *get_refs_list_using_list(struct transport *transport,
|
|
|
|
int for_push);
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
static int fetch(struct transport *transport,
|
fetch-pack: unify ref in and out param
When a user fetches:
- at least one up-to-date ref and at least one non-up-to-date ref,
- using HTTP with protocol v0 (or something else that uses the fetch
command of a remote helper)
some refs might not be updated after the fetch.
This bug was introduced in commit 989b8c4452 ("fetch-pack: put shallow
info in output parameter", 2018-06-28) which allowed transports to
report the refs that they have fetched in a new out-parameter
"fetched_refs". If they do so, transport_fetch_refs() makes this
information available to its caller.
Users of "fetched_refs" rely on the following 3 properties:
(1) it is the complete list of refs that was passed to
transport_fetch_refs(),
(2) it has shallow information (REF_STATUS_REJECT_SHALLOW set if
relevant), and
(3) it has updated OIDs if ref-in-want was used (introduced after
989b8c4452).
In an effort to satisfy (1), whenever transport_fetch_refs()
filters the refs sent to the transport, it re-adds the filtered refs to
whatever the transport supplies before returning it to the user.
However, the implementation in 989b8c4452 unconditionally re-adds the
filtered refs without checking if the transport refrained from reporting
anything in "fetched_refs" (which it is allowed to do), resulting in an
incomplete list, no longer satisfying (1).
An earlier effort to resolve this [1] solved the issue by readding the
filtered refs only if the transport did not refrain from reporting in
"fetched_refs", but after further discussion, it seems that the better
solution is to revert the API change that introduced "fetched_refs".
This API change was first suggested as part of a ref-in-want
implementation that allowed for ref patterns and, thus, there could be
drastic differences between the input refs and the refs actually fetched
[2]; we eventually decided to only allow exact ref names, but this API
change remained even though its necessity was decreased.
Therefore, revert this API change by reverting commit 989b8c4452, and
make receive_wanted_refs() update the OIDs in the sought array (like how
update_shallow() updates shallow information in the sought array)
instead. A test is also included to show that the user-visible bug
discussed at the beginning of this commit message no longer exists.
[1] https://public-inbox.org/git/20180801171806.GA122458@google.com/
[2] https://public-inbox.org/git/86a128c5fb710a41791e7183207c4d64889f9307.1485381677.git.jonathantanmy@google.com/
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-02 04:13:20 +08:00
|
|
|
int nr_heads, struct ref **to_fetch)
|
2009-08-05 13:01:53 +08:00
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
int i, count;
|
|
|
|
|
transport-helper: skip ls-refs if unnecessary
Commit e70a3030e7 ("fetch: do not list refs if fetching only hashes",
2018-10-07) and its ancestors taught Git, as an optimization, to skip
the ls-refs step when it is not necessary during a protocol v2 fetch
(for example, when lazy fetching a missing object in a partial clone, or
when running "git fetch --no-tags <remote> <SHA-1>"). But that was only
done for natively supported protocols; in particular, HTTP was not
supported.
Teach Git to skip ls-refs when using remote helpers that support connect
or stateless-connect. To do this, fetch() is made an acceptable entry
point. Because fetch() can now be the first function in the vtable
called, "get_helper(transport);" has to be added to the beginning of
that function to set the transport up (if not yet set up) before
process_connect() is invoked.
When fetch() is called, the transport could be taken over (this happens
if "connect" or "stateless-connect" is successfully run without any
"fallback" response), or not. If the transport is taken over, execution
continues like execution for natively supported protocols
(fetch_refs_via_pack() is executed, which will fetch refs using ls-refs
if needed). If not, the remote helper interface will invoke
get_refs_list() if it hasn't been invoked yet, preserving existing
behavior.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-22 06:20:09 +08:00
|
|
|
get_helper(transport);
|
|
|
|
|
2009-12-09 23:26:32 +08:00
|
|
|
if (process_connect(transport, 0)) {
|
|
|
|
do_take_over(transport);
|
fetch-pack: unify ref in and out param
When a user fetches:
- at least one up-to-date ref and at least one non-up-to-date ref,
- using HTTP with protocol v0 (or something else that uses the fetch
command of a remote helper)
some refs might not be updated after the fetch.
This bug was introduced in commit 989b8c4452 ("fetch-pack: put shallow
info in output parameter", 2018-06-28) which allowed transports to
report the refs that they have fetched in a new out-parameter
"fetched_refs". If they do so, transport_fetch_refs() makes this
information available to its caller.
Users of "fetched_refs" rely on the following 3 properties:
(1) it is the complete list of refs that was passed to
transport_fetch_refs(),
(2) it has shallow information (REF_STATUS_REJECT_SHALLOW set if
relevant), and
(3) it has updated OIDs if ref-in-want was used (introduced after
989b8c4452).
In an effort to satisfy (1), whenever transport_fetch_refs()
filters the refs sent to the transport, it re-adds the filtered refs to
whatever the transport supplies before returning it to the user.
However, the implementation in 989b8c4452 unconditionally re-adds the
filtered refs without checking if the transport refrained from reporting
anything in "fetched_refs" (which it is allowed to do), resulting in an
incomplete list, no longer satisfying (1).
An earlier effort to resolve this [1] solved the issue by readding the
filtered refs only if the transport did not refrain from reporting in
"fetched_refs", but after further discussion, it seems that the better
solution is to revert the API change that introduced "fetched_refs".
This API change was first suggested as part of a ref-in-want
implementation that allowed for ref patterns and, thus, there could be
drastic differences between the input refs and the refs actually fetched
[2]; we eventually decided to only allow exact ref names, but this API
change remained even though its necessity was decreased.
Therefore, revert this API change by reverting commit 989b8c4452, and
make receive_wanted_refs() update the OIDs in the sought array (like how
update_shallow() updates shallow information in the sought array)
instead. A test is also included to show that the user-visible bug
discussed at the beginning of this commit message no longer exists.
[1] https://public-inbox.org/git/20180801171806.GA122458@google.com/
[2] https://public-inbox.org/git/86a128c5fb710a41791e7183207c4d64889f9307.1485381677.git.jonathantanmy@google.com/
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-02 04:13:20 +08:00
|
|
|
return transport->vtable->fetch(transport, nr_heads, to_fetch);
|
2009-12-09 23:26:32 +08:00
|
|
|
}
|
|
|
|
|
transport-helper: skip ls-refs if unnecessary
Commit e70a3030e7 ("fetch: do not list refs if fetching only hashes",
2018-10-07) and its ancestors taught Git, as an optimization, to skip
the ls-refs step when it is not necessary during a protocol v2 fetch
(for example, when lazy fetching a missing object in a partial clone, or
when running "git fetch --no-tags <remote> <SHA-1>"). But that was only
done for natively supported protocols; in particular, HTTP was not
supported.
Teach Git to skip ls-refs when using remote helpers that support connect
or stateless-connect. To do this, fetch() is made an acceptable entry
point. Because fetch() can now be the first function in the vtable
called, "get_helper(transport);" has to be added to the beginning of
that function to set the transport up (if not yet set up) before
process_connect() is invoked.
When fetch() is called, the transport could be taken over (this happens
if "connect" or "stateless-connect" is successfully run without any
"fallback" response), or not. If the transport is taken over, execution
continues like execution for natively supported protocols
(fetch_refs_via_pack() is executed, which will fetch refs using ls-refs
if needed). If not, the remote helper interface will invoke
get_refs_list() if it hasn't been invoked yet, preserving existing
behavior.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-22 06:20:09 +08:00
|
|
|
if (!data->get_refs_list_called)
|
|
|
|
get_refs_list_using_list(transport, 0);
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
count = 0;
|
|
|
|
for (i = 0; i < nr_heads; i++)
|
|
|
|
if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
|
|
|
|
count++;
|
|
|
|
|
|
|
|
if (!count)
|
|
|
|
return 0;
|
|
|
|
|
2015-02-13 13:24:46 +08:00
|
|
|
if (data->check_connectivity &&
|
|
|
|
data->transport_options.check_self_contained_and_connected)
|
|
|
|
set_helper_option(transport, "check-connectivity", "true");
|
|
|
|
|
|
|
|
if (transport->cloning)
|
|
|
|
set_helper_option(transport, "cloning", "true");
|
|
|
|
|
|
|
|
if (data->transport_options.update_shallow)
|
|
|
|
set_helper_option(transport, "update-shallow", "true");
|
|
|
|
|
2019-01-08 08:17:09 +08:00
|
|
|
if (data->transport_options.filter_options.choice) {
|
2019-06-28 06:54:10 +08:00
|
|
|
const char *spec = expand_list_objects_filter_spec(
|
|
|
|
&data->transport_options.filter_options);
|
|
|
|
set_helper_option(transport, "filter", spec);
|
2019-01-08 08:17:09 +08:00
|
|
|
}
|
2017-12-08 23:58:40 +08:00
|
|
|
|
2018-07-03 06:39:44 +08:00
|
|
|
if (data->transport_options.negotiation_tips)
|
|
|
|
warning("Ignoring --negotiation-tip because the protocol does not support it.");
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
if (data->fetch)
|
|
|
|
return fetch_with_fetch(transport, nr_heads, to_fetch);
|
|
|
|
|
2009-11-18 09:42:27 +08:00
|
|
|
if (data->import)
|
|
|
|
return fetch_with_import(transport, nr_heads, to_fetch);
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-04-18 12:14:33 +08:00
|
|
|
static int push_update_ref_status(struct strbuf *buf,
|
2011-07-16 21:03:34 +08:00
|
|
|
struct ref **ref,
|
|
|
|
struct ref *remote_refs)
|
|
|
|
{
|
|
|
|
char *refname, *msg;
|
2013-11-13 04:56:57 +08:00
|
|
|
int status, forced = 0;
|
2011-07-16 21:03:34 +08:00
|
|
|
|
2013-12-01 04:55:40 +08:00
|
|
|
if (starts_with(buf->buf, "ok ")) {
|
2011-07-16 21:03:34 +08:00
|
|
|
status = REF_STATUS_OK;
|
|
|
|
refname = buf->buf + 3;
|
2013-12-01 04:55:40 +08:00
|
|
|
} else if (starts_with(buf->buf, "error ")) {
|
2011-07-16 21:03:34 +08:00
|
|
|
status = REF_STATUS_REMOTE_REJECT;
|
|
|
|
refname = buf->buf + 6;
|
|
|
|
} else
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("expected ok/error, helper said '%s'"), buf->buf);
|
2011-07-16 21:03:34 +08:00
|
|
|
|
|
|
|
msg = strchr(refname, ' ');
|
|
|
|
if (msg) {
|
|
|
|
struct strbuf msg_buf = STRBUF_INIT;
|
|
|
|
const char *end;
|
|
|
|
|
|
|
|
*msg++ = '\0';
|
|
|
|
if (!unquote_c_style(&msg_buf, msg, &end))
|
|
|
|
msg = strbuf_detach(&msg_buf, NULL);
|
|
|
|
else
|
|
|
|
msg = xstrdup(msg);
|
|
|
|
strbuf_release(&msg_buf);
|
|
|
|
|
|
|
|
if (!strcmp(msg, "no match")) {
|
|
|
|
status = REF_STATUS_NONE;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
else if (!strcmp(msg, "up to date")) {
|
|
|
|
status = REF_STATUS_UPTODATE;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
else if (!strcmp(msg, "non-fast forward")) {
|
|
|
|
status = REF_STATUS_REJECT_NONFASTFORWARD;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
2012-11-30 09:41:37 +08:00
|
|
|
else if (!strcmp(msg, "already exists")) {
|
|
|
|
status = REF_STATUS_REJECT_ALREADY_EXISTS;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
2012-11-30 09:41:37 +08:00
|
|
|
}
|
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-24 05:55:30 +08:00
|
|
|
else if (!strcmp(msg, "fetch first")) {
|
|
|
|
status = REF_STATUS_REJECT_FETCH_FIRST;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
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-24 05:55:30 +08:00
|
|
|
}
|
|
|
|
else if (!strcmp(msg, "needs force")) {
|
|
|
|
status = REF_STATUS_REJECT_NEEDS_FORCE;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
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-24 05:55:30 +08:00
|
|
|
}
|
2013-07-09 05:42:40 +08:00
|
|
|
else if (!strcmp(msg, "stale info")) {
|
|
|
|
status = REF_STATUS_REJECT_STALE;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
2013-07-09 05:42:40 +08:00
|
|
|
}
|
2013-11-13 04:56:57 +08:00
|
|
|
else if (!strcmp(msg, "forced update")) {
|
|
|
|
forced = 1;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(msg);
|
2013-11-13 04:56:57 +08:00
|
|
|
}
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*ref)
|
|
|
|
*ref = find_ref_by_name(*ref, refname);
|
|
|
|
if (!*ref)
|
|
|
|
*ref = find_ref_by_name(remote_refs, refname);
|
|
|
|
if (!*ref) {
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("helper reported unexpected status of %s"), refname);
|
2013-04-18 12:14:33 +08:00
|
|
|
return 1;
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((*ref)->status != REF_STATUS_NONE) {
|
|
|
|
/*
|
|
|
|
* Earlier, the ref was marked not to be pushed, so ignore the ref
|
|
|
|
* status reported by the remote helper if the latter is 'no match'.
|
|
|
|
*/
|
|
|
|
if (status == REF_STATUS_NONE)
|
2013-04-18 12:14:33 +08:00
|
|
|
return 1;
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
(*ref)->status = status;
|
2014-02-21 17:55:59 +08:00
|
|
|
(*ref)->forced_update |= forced;
|
2011-07-16 21:03:34 +08:00
|
|
|
(*ref)->remote_status = msg;
|
2013-05-10 20:08:30 +08:00
|
|
|
return !(status == REF_STATUS_OK);
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
|
2014-04-13 04:33:30 +08:00
|
|
|
static int push_update_refs_status(struct helper_data *data,
|
2013-10-31 17:36:37 +08:00
|
|
|
struct ref *remote_refs,
|
|
|
|
int flags)
|
2011-07-16 21:03:34 +08:00
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct ref *ref = remote_refs;
|
2014-04-13 04:33:30 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2011-07-16 21:03:34 +08:00
|
|
|
for (;;) {
|
2013-04-18 12:14:33 +08:00
|
|
|
char *private;
|
|
|
|
|
2014-04-13 04:33:30 +08:00
|
|
|
if (recvline(data, &buf)) {
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-07-16 21:03:34 +08:00
|
|
|
if (!buf.len)
|
|
|
|
break;
|
|
|
|
|
2013-04-18 12:14:33 +08:00
|
|
|
if (push_update_ref_status(&buf, &ref, remote_refs))
|
|
|
|
continue;
|
|
|
|
|
2018-05-17 06:58:03 +08:00
|
|
|
if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
|
2013-04-18 12:14:33 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* propagate back the update to the remote namespace */
|
2018-05-17 06:58:11 +08:00
|
|
|
private = apply_refspecs(&data->rs, ref->name);
|
2013-04-18 12:14:33 +08:00
|
|
|
if (!private)
|
|
|
|
continue;
|
2017-10-16 06:06:51 +08:00
|
|
|
update_ref("update by helper", private, &ref->new_oid, NULL,
|
|
|
|
0, 0);
|
2013-04-18 12:14:33 +08:00
|
|
|
free(private);
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
strbuf_release(&buf);
|
2014-04-13 04:33:30 +08:00
|
|
|
return ret;
|
2011-07-16 21:03:34 +08:00
|
|
|
}
|
|
|
|
|
2015-08-19 23:26:46 +08:00
|
|
|
static void set_common_push_options(struct transport *transport,
|
|
|
|
const char *name, int flags)
|
|
|
|
{
|
|
|
|
if (flags & TRANSPORT_PUSH_DRY_RUN) {
|
|
|
|
if (set_helper_option(transport, "dry-run", "true") != 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("helper %s does not support dry-run"), name);
|
2015-08-19 23:26:46 +08:00
|
|
|
} else if (flags & TRANSPORT_PUSH_CERT_ALWAYS) {
|
|
|
|
if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("helper %s does not support --signed"), name);
|
2015-08-19 23:26:46 +08:00
|
|
|
} else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED) {
|
|
|
|
if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "if-asked") != 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("helper %s does not support --signed=if-asked"), name);
|
2015-08-19 23:26:46 +08:00
|
|
|
}
|
2017-02-09 06:04:00 +08:00
|
|
|
|
remote-curl: pass on atomic capability to remote side
When pushing more than one reference with the --atomic option, the
server is supposed to perform a single atomic transaction to update the
references, leaving them either all to succeed or all to fail. This
works fine when pushing locally or over SSH, but when pushing over HTTP,
we fail to pass the atomic capability to the remote side. In fact, we
have not reported this capability to any remote helpers during the life
of the feature.
Now normally, things happen to work nevertheless, since we actually
check for most types of failures, such as non-fast-forward updates, on
the client side, and just abort the entire attempt. However, if the
server side reports a problem, such as the inability to lock a ref, the
transaction isn't atomic, because we haven't passed the appropriate
capability over and the remote side has no way of knowing that we wanted
atomic behavior.
Fix this by passing the option from the transport code through to remote
helpers, and from the HTTP remote helper down to send-pack. With this
change, we can detect if the server side rejects the push and report
back appropriately. Note the difference in the messages: the remote
side reports "atomic transaction failed", while our own checking rejects
pushes with the message "atomic push failed".
Document the atomic option in the remote helper documentation, so other
implementers can implement it if they like.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-10-17 07:45:34 +08:00
|
|
|
if (flags & TRANSPORT_PUSH_ATOMIC)
|
|
|
|
if (set_helper_option(transport, TRANS_OPT_ATOMIC, "true") != 0)
|
|
|
|
die(_("helper %s does not support --atomic"), name);
|
|
|
|
|
2017-02-09 06:04:00 +08:00
|
|
|
if (flags & TRANSPORT_PUSH_OPTIONS) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
for_each_string_list_item(item, transport->push_options)
|
|
|
|
if (set_helper_option(transport, "push-option", item->string) != 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("helper %s does not support 'push-option'"), name);
|
2017-02-09 06:04:00 +08:00
|
|
|
}
|
2015-08-19 23:26:46 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 00:48:27 +08:00
|
|
|
static int push_refs_with_push(struct transport *transport,
|
2013-08-03 06:14:50 +08:00
|
|
|
struct ref *remote_refs, int flags)
|
2009-10-31 08:47:30 +08:00
|
|
|
{
|
|
|
|
int force_all = flags & TRANSPORT_PUSH_FORCE;
|
|
|
|
int mirror = flags & TRANSPORT_PUSH_MIRROR;
|
2019-07-12 05:19:19 +08:00
|
|
|
int atomic = flags & TRANSPORT_PUSH_ATOMIC;
|
2009-10-31 08:47:30 +08:00
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct ref *ref;
|
2013-08-03 06:14:50 +08:00
|
|
|
struct string_list cas_options = STRING_LIST_INIT_DUP;
|
|
|
|
struct string_list_item *cas_option;
|
2009-10-31 08:47:30 +08:00
|
|
|
|
2011-03-22 20:50:08 +08:00
|
|
|
get_helper(transport);
|
2009-10-31 08:47:30 +08:00
|
|
|
if (!data->push)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
2010-01-08 10:12:42 +08:00
|
|
|
if (!ref->peer_ref && !mirror)
|
2009-10-31 08:47:30 +08:00
|
|
|
continue;
|
|
|
|
|
2010-01-08 10:12:42 +08:00
|
|
|
/* Check for statuses set by set_ref_status_for_push() */
|
|
|
|
switch (ref->status) {
|
|
|
|
case REF_STATUS_REJECT_NONFASTFORWARD:
|
2013-07-09 05:42:40 +08:00
|
|
|
case REF_STATUS_REJECT_STALE:
|
2012-11-30 09:41:37 +08:00
|
|
|
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
2019-07-12 05:19:19 +08:00
|
|
|
if (atomic) {
|
2020-04-17 17:45:36 +08:00
|
|
|
reject_atomic_push(remote_refs, mirror);
|
2019-07-12 05:19:19 +08:00
|
|
|
string_list_clear(&cas_options, 0);
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
continue;
|
2010-01-08 10:12:42 +08:00
|
|
|
case REF_STATUS_UPTODATE:
|
2009-10-31 08:47:30 +08:00
|
|
|
continue;
|
2010-01-08 10:12:42 +08:00
|
|
|
default:
|
|
|
|
; /* do nothing */
|
2009-10-31 08:47:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (force_all)
|
|
|
|
ref->force = 1;
|
|
|
|
|
|
|
|
strbuf_addstr(&buf, "push ");
|
|
|
|
if (!ref->deletion) {
|
|
|
|
if (ref->force)
|
|
|
|
strbuf_addch(&buf, '+');
|
|
|
|
if (ref->peer_ref)
|
|
|
|
strbuf_addstr(&buf, ref->peer_ref->name);
|
|
|
|
else
|
2015-11-10 10:22:20 +08:00
|
|
|
strbuf_addstr(&buf, oid_to_hex(&ref->new_oid));
|
2009-10-31 08:47:30 +08:00
|
|
|
}
|
|
|
|
strbuf_addch(&buf, ':');
|
|
|
|
strbuf_addstr(&buf, ref->name);
|
|
|
|
strbuf_addch(&buf, '\n');
|
2013-08-03 06:14:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The "--force-with-lease" options without explicit
|
|
|
|
* values to expect have already been expanded into
|
2015-11-10 10:22:20 +08:00
|
|
|
* the ref->old_oid_expect[] field; we can ignore
|
2013-08-03 06:14:50 +08:00
|
|
|
* transport->smart_options->cas altogether and instead
|
|
|
|
* can enumerate them from the refs.
|
|
|
|
*/
|
|
|
|
if (ref->expect_old_sha1) {
|
|
|
|
struct strbuf cas = STRBUF_INIT;
|
|
|
|
strbuf_addf(&cas, "%s:%s",
|
2015-11-10 10:22:20 +08:00
|
|
|
ref->name, oid_to_hex(&ref->old_oid_expect));
|
2017-12-09 01:29:31 +08:00
|
|
|
string_list_append_nodup(&cas_options,
|
|
|
|
strbuf_detach(&cas, NULL));
|
2013-08-03 06:14:50 +08:00
|
|
|
}
|
2009-10-31 08:47:30 +08:00
|
|
|
}
|
2013-08-03 06:14:50 +08:00
|
|
|
if (buf.len == 0) {
|
|
|
|
string_list_clear(&cas_options, 0);
|
2009-10-31 08:47:31 +08:00
|
|
|
return 0;
|
2013-08-03 06:14:50 +08:00
|
|
|
}
|
2009-10-31 08:47:30 +08:00
|
|
|
|
2013-08-03 06:14:50 +08:00
|
|
|
for_each_string_list_item(cas_option, &cas_options)
|
|
|
|
set_helper_option(transport, "cas", cas_option->string);
|
2015-08-19 23:26:46 +08:00
|
|
|
set_common_push_options(transport, data->name, flags);
|
2009-10-31 08:47:30 +08:00
|
|
|
|
|
|
|
strbuf_addch(&buf, '\n');
|
2009-12-09 23:26:27 +08:00
|
|
|
sendline(data, &buf);
|
2009-10-31 08:47:30 +08:00
|
|
|
strbuf_release(&buf);
|
2017-12-09 01:29:31 +08:00
|
|
|
string_list_clear(&cas_options, 0);
|
2011-07-16 21:03:34 +08:00
|
|
|
|
2014-04-13 04:33:30 +08:00
|
|
|
return push_update_refs_status(data, remote_refs, flags);
|
2009-10-31 08:47:30 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 00:48:27 +08:00
|
|
|
static int push_refs_with_export(struct transport *transport,
|
|
|
|
struct ref *remote_refs, int flags)
|
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
struct child_process *helper, exporter;
|
|
|
|
struct helper_data *data = transport->data;
|
2014-04-21 02:59:25 +08:00
|
|
|
struct string_list revlist_args = STRING_LIST_INIT_DUP;
|
2010-03-30 00:48:27 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
2018-05-17 06:58:03 +08:00
|
|
|
if (!data->rs.nr)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("remote-helper doesn't support push; refspec needed"));
|
2013-04-18 12:14:30 +08:00
|
|
|
|
2015-08-19 23:26:46 +08:00
|
|
|
set_common_push_options(transport, data->name, flags);
|
2013-11-13 04:56:56 +08:00
|
|
|
if (flags & TRANSPORT_PUSH_FORCE) {
|
|
|
|
if (set_helper_option(transport, "force", "true") != 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
warning(_("helper %s does not support 'force'"), data->name);
|
2013-11-13 04:56:56 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 00:48:27 +08:00
|
|
|
helper = get_helper(transport);
|
|
|
|
|
|
|
|
write_constant(helper->in, "export\n");
|
|
|
|
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
|
|
char *private;
|
2015-11-10 10:22:24 +08:00
|
|
|
struct object_id oid;
|
2010-03-30 00:48:27 +08:00
|
|
|
|
2018-05-17 06:58:11 +08:00
|
|
|
private = apply_refspecs(&data->rs, ref->name);
|
sha1_name: convert get_sha1* to get_oid*
Now that all the callers of get_sha1 directly or indirectly use struct
object_id, rename the functions starting with get_sha1 to start with
get_oid. Convert the internals in sha1_name.c to use struct object_id
as well, and eliminate explicit length checks where possible. Convert a
use of 40 in get_oid_basic to GIT_SHA1_HEXSZ.
Outside of sha1_name.c and cache.h, this transition was made with the
following semantic patch:
@@
expression E1, E2;
@@
- get_sha1(E1, E2.hash)
+ get_oid(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1(E1, E2->hash)
+ get_oid(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_committish(E1, E2.hash)
+ get_oid_committish(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_committish(E1, E2->hash)
+ get_oid_committish(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_treeish(E1, E2.hash)
+ get_oid_treeish(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_treeish(E1, E2->hash)
+ get_oid_treeish(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_commit(E1, E2.hash)
+ get_oid_commit(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_commit(E1, E2->hash)
+ get_oid_commit(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_tree(E1, E2.hash)
+ get_oid_tree(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_tree(E1, E2->hash)
+ get_oid_tree(E1, E2)
@@
expression E1, E2;
@@
- get_sha1_blob(E1, E2.hash)
+ get_oid_blob(E1, &E2)
@@
expression E1, E2;
@@
- get_sha1_blob(E1, E2->hash)
+ get_oid_blob(E1, E2)
@@
expression E1, E2, E3, E4;
@@
- get_sha1_with_context(E1, E2, E3.hash, E4)
+ get_oid_with_context(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- get_sha1_with_context(E1, E2, E3->hash, E4)
+ get_oid_with_context(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-07-14 07:49:28 +08:00
|
|
|
if (private && !get_oid(private, &oid)) {
|
2010-03-30 00:48:27 +08:00
|
|
|
strbuf_addf(&buf, "^%s", private);
|
2017-12-09 01:29:31 +08:00
|
|
|
string_list_append_nodup(&revlist_args,
|
|
|
|
strbuf_detach(&buf, NULL));
|
2015-11-10 10:22:24 +08:00
|
|
|
oidcpy(&ref->old_oid, &oid);
|
2010-03-30 00:48:27 +08:00
|
|
|
}
|
2011-07-16 21:03:21 +08:00
|
|
|
free(private);
|
2010-03-30 00:48:27 +08:00
|
|
|
|
2013-05-21 09:02:45 +08:00
|
|
|
if (ref->peer_ref) {
|
2014-04-21 02:59:25 +08:00
|
|
|
if (strcmp(ref->name, ref->peer_ref->name)) {
|
2014-04-21 02:59:29 +08:00
|
|
|
if (!ref->deletion) {
|
|
|
|
const char *name;
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
/* Follow symbolic refs (mainly for HEAD). */
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-16 06:07:09 +08:00
|
|
|
name = resolve_ref_unsafe(ref->peer_ref->name,
|
|
|
|
RESOLVE_REF_READING,
|
|
|
|
&oid, &flag);
|
2014-04-21 02:59:29 +08:00
|
|
|
if (!name || !(flag & REF_ISSYMREF))
|
|
|
|
name = ref->peer_ref->name;
|
2014-04-21 02:59:26 +08:00
|
|
|
|
2014-04-21 02:59:29 +08:00
|
|
|
strbuf_addf(&buf, "%s:%s", name, ref->name);
|
|
|
|
} else
|
|
|
|
strbuf_addf(&buf, ":%s", ref->name);
|
2014-04-21 02:59:26 +08:00
|
|
|
|
2014-04-21 02:59:25 +08:00
|
|
|
string_list_append(&revlist_args, "--refspec");
|
|
|
|
string_list_append(&revlist_args, buf.buf);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
}
|
2014-04-21 02:59:29 +08:00
|
|
|
if (!ref->deletion)
|
|
|
|
string_list_append(&revlist_args, ref->peer_ref->name);
|
2013-05-21 09:02:45 +08:00
|
|
|
}
|
2010-03-30 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
2011-07-16 21:03:40 +08:00
|
|
|
if (get_exporter(transport, &exporter, &revlist_args))
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("couldn't run fast-export"));
|
2010-03-30 00:48:27 +08:00
|
|
|
|
2014-04-21 02:59:25 +08:00
|
|
|
string_list_clear(&revlist_args, 1);
|
|
|
|
|
2011-07-16 21:03:35 +08:00
|
|
|
if (finish_command(&exporter))
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("error while running fast-export"));
|
2014-04-13 04:33:32 +08:00
|
|
|
if (push_update_refs_status(data, remote_refs, flags))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (data->export_marks) {
|
|
|
|
strbuf_addf(&buf, "%s.tmp", data->export_marks);
|
|
|
|
rename(buf.buf, data->export_marks);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-03-30 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int push_refs(struct transport *transport,
|
|
|
|
struct ref *remote_refs, int flags)
|
|
|
|
{
|
|
|
|
struct helper_data *data = transport->data;
|
|
|
|
|
|
|
|
if (process_connect(transport, 1)) {
|
|
|
|
do_take_over(transport);
|
2017-12-15 05:44:45 +08:00
|
|
|
return transport->vtable->push_refs(transport, remote_refs, flags);
|
2010-03-30 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!remote_refs) {
|
2018-07-21 15:49:41 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
_("No refs in common and none specified; doing nothing.\n"
|
2020-06-24 22:46:29 +08:00
|
|
|
"Perhaps you should specify a branch.\n"));
|
2010-03-30 00:48:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->push)
|
|
|
|
return push_refs_with_push(transport, remote_refs, flags);
|
|
|
|
|
|
|
|
if (data->export)
|
|
|
|
return push_refs_with_export(transport, remote_refs, flags);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-09 18:25:21 +08:00
|
|
|
static int has_attribute(const char *attrs, const char *attr)
|
|
|
|
{
|
2009-11-18 09:42:30 +08:00
|
|
|
int len;
|
|
|
|
if (!attrs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
len = strlen(attr);
|
|
|
|
for (;;) {
|
|
|
|
const char *space = strchrnul(attrs, ' ');
|
|
|
|
if (len == space - attrs && !strncmp(attrs, attr, len))
|
|
|
|
return 1;
|
|
|
|
if (!*space)
|
|
|
|
return 0;
|
|
|
|
attrs = space + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-16 01:31:22 +08:00
|
|
|
static struct ref *get_refs_list(struct transport *transport, int for_push,
|
|
|
|
const struct argv_array *ref_prefixes)
|
transport-helper: skip ls-refs if unnecessary
Commit e70a3030e7 ("fetch: do not list refs if fetching only hashes",
2018-10-07) and its ancestors taught Git, as an optimization, to skip
the ls-refs step when it is not necessary during a protocol v2 fetch
(for example, when lazy fetching a missing object in a partial clone, or
when running "git fetch --no-tags <remote> <SHA-1>"). But that was only
done for natively supported protocols; in particular, HTTP was not
supported.
Teach Git to skip ls-refs when using remote helpers that support connect
or stateless-connect. To do this, fetch() is made an acceptable entry
point. Because fetch() can now be the first function in the vtable
called, "get_helper(transport);" has to be added to the beginning of
that function to set the transport up (if not yet set up) before
process_connect() is invoked.
When fetch() is called, the transport could be taken over (this happens
if "connect" or "stateless-connect" is successfully run without any
"fallback" response), or not. If the transport is taken over, execution
continues like execution for natively supported protocols
(fetch_refs_via_pack() is executed, which will fetch refs using ls-refs
if needed). If not, the remote helper interface will invoke
get_refs_list() if it hasn't been invoked yet, preserving existing
behavior.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-22 06:20:09 +08:00
|
|
|
{
|
|
|
|
get_helper(transport);
|
|
|
|
|
|
|
|
if (process_connect(transport, for_push)) {
|
|
|
|
do_take_over(transport);
|
|
|
|
return transport->vtable->get_refs_list(transport, for_push, ref_prefixes);
|
|
|
|
}
|
|
|
|
|
|
|
|
return get_refs_list_using_list(transport, for_push);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *get_refs_list_using_list(struct transport *transport,
|
|
|
|
int for_push)
|
2009-08-05 13:01:53 +08:00
|
|
|
{
|
2009-10-31 08:47:28 +08:00
|
|
|
struct helper_data *data = transport->data;
|
2009-08-05 13:01:53 +08:00
|
|
|
struct child_process *helper;
|
|
|
|
struct ref *ret = NULL;
|
|
|
|
struct ref **tail = &ret;
|
|
|
|
struct ref *posn;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
transport-helper: skip ls-refs if unnecessary
Commit e70a3030e7 ("fetch: do not list refs if fetching only hashes",
2018-10-07) and its ancestors taught Git, as an optimization, to skip
the ls-refs step when it is not necessary during a protocol v2 fetch
(for example, when lazy fetching a missing object in a partial clone, or
when running "git fetch --no-tags <remote> <SHA-1>"). But that was only
done for natively supported protocols; in particular, HTTP was not
supported.
Teach Git to skip ls-refs when using remote helpers that support connect
or stateless-connect. To do this, fetch() is made an acceptable entry
point. Because fetch() can now be the first function in the vtable
called, "get_helper(transport);" has to be added to the beginning of
that function to set the transport up (if not yet set up) before
process_connect() is invoked.
When fetch() is called, the transport could be taken over (this happens
if "connect" or "stateless-connect" is successfully run without any
"fallback" response), or not. If the transport is taken over, execution
continues like execution for natively supported protocols
(fetch_refs_via_pack() is executed, which will fetch refs using ls-refs
if needed). If not, the remote helper interface will invoke
get_refs_list() if it hasn't been invoked yet, preserving existing
behavior.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-22 06:20:09 +08:00
|
|
|
data->get_refs_list_called = 1;
|
2009-08-05 13:01:53 +08:00
|
|
|
helper = get_helper(transport);
|
2009-09-04 10:13:51 +08:00
|
|
|
|
2020-05-26 03:59:03 +08:00
|
|
|
if (data->object_format) {
|
|
|
|
write_str_in_full(helper->in, "option object-format\n");
|
|
|
|
if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
|
|
|
|
exit(128);
|
|
|
|
}
|
|
|
|
|
2009-10-31 08:47:30 +08:00
|
|
|
if (data->push && for_push)
|
|
|
|
write_str_in_full(helper->in, "list for-push\n");
|
|
|
|
else
|
|
|
|
write_str_in_full(helper->in, "list\n");
|
2009-08-05 13:01:53 +08:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
char *eov, *eon;
|
2014-04-13 04:33:29 +08:00
|
|
|
if (recvline(data, &buf))
|
|
|
|
exit(128);
|
2009-08-05 13:01:53 +08:00
|
|
|
|
|
|
|
if (!*buf.buf)
|
|
|
|
break;
|
2020-05-26 03:59:03 +08:00
|
|
|
else if (buf.buf[0] == ':') {
|
|
|
|
const char *value;
|
|
|
|
if (skip_prefix(buf.buf, ":object-format ", &value)) {
|
|
|
|
int algo = hash_algo_by_name(value);
|
|
|
|
if (algo == GIT_HASH_UNKNOWN)
|
|
|
|
die(_("unsupported object format '%s'"),
|
|
|
|
value);
|
|
|
|
transport->hash_algo = &hash_algos[algo];
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2009-08-05 13:01:53 +08:00
|
|
|
|
|
|
|
eov = strchr(buf.buf, ' ');
|
|
|
|
if (!eov)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("malformed response in ref list: %s"), buf.buf);
|
2009-08-05 13:01:53 +08:00
|
|
|
eon = strchr(eov + 1, ' ');
|
|
|
|
*eov = '\0';
|
|
|
|
if (eon)
|
|
|
|
*eon = '\0';
|
|
|
|
*tail = alloc_ref(eov + 1);
|
|
|
|
if (buf.buf[0] == '@')
|
|
|
|
(*tail)->symref = xstrdup(buf.buf + 1);
|
|
|
|
else if (buf.buf[0] != '?')
|
2020-05-26 03:59:03 +08:00
|
|
|
get_oid_hex_algop(buf.buf, &(*tail)->old_oid, transport->hash_algo);
|
2009-11-18 09:42:30 +08:00
|
|
|
if (eon) {
|
|
|
|
if (has_attribute(eon + 1, "unchanged")) {
|
|
|
|
(*tail)->status |= REF_STATUS_UPTODATE;
|
2017-10-16 06:06:56 +08:00
|
|
|
if (read_ref((*tail)->name, &(*tail)->old_oid) < 0)
|
2018-07-21 15:49:19 +08:00
|
|
|
die(_("could not read ref %s"),
|
2015-08-01 07:57:57 +08:00
|
|
|
(*tail)->name);
|
2009-11-18 09:42:30 +08:00
|
|
|
}
|
|
|
|
}
|
2009-08-05 13:01:53 +08:00
|
|
|
tail = &((*tail)->next);
|
|
|
|
}
|
2009-12-09 23:26:27 +08:00
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, "Debug: Read ref listing.\n");
|
2009-08-05 13:01:53 +08:00
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
for (posn = ret; posn; posn = posn->next)
|
|
|
|
resolve_remote_symref(posn, ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-12-15 05:44:45 +08:00
|
|
|
static struct transport_vtable vtable = {
|
|
|
|
set_helper_option,
|
|
|
|
get_refs_list,
|
|
|
|
fetch,
|
|
|
|
push_refs,
|
|
|
|
connect_helper,
|
|
|
|
release_helper
|
|
|
|
};
|
|
|
|
|
2009-09-04 10:13:49 +08:00
|
|
|
int transport_helper_init(struct transport *transport, const char *name)
|
2009-08-05 13:01:53 +08:00
|
|
|
{
|
2014-05-26 23:33:56 +08:00
|
|
|
struct helper_data *data = xcalloc(1, sizeof(*data));
|
2009-09-04 10:13:49 +08:00
|
|
|
data->name = name;
|
2009-08-05 13:01:53 +08:00
|
|
|
|
transport: add a protocol-whitelist environment variable
If we are cloning an untrusted remote repository into a
sandbox, we may also want to fetch remote submodules in
order to get the complete view as intended by the other
side. However, that opens us up to attacks where a malicious
user gets us to clone something they would not otherwise
have access to (this is not necessarily a problem by itself,
but we may then act on the cloned contents in a way that
exposes them to the attacker).
Ideally such a setup would sandbox git entirely away from
high-value items, but this is not always practical or easy
to set up (e.g., OS network controls may block multiple
protocols, and we would want to enable some but not others).
We can help this case by providing a way to restrict
particular protocols. We use a whitelist in the environment.
This is more annoying to set up than a blacklist, but
defaults to safety if the set of protocols git supports
grows). If no whitelist is specified, we continue to default
to allowing all protocols (this is an "unsafe" default, but
since the minority of users will want this sandboxing
effect, it is the only sensible one).
A note on the tests: ideally these would all be in a single
test file, but the git-daemon and httpd test infrastructure
is an all-or-nothing proposition rather than a test-by-test
prerequisite. By putting them all together, we would be
unable to test the file-local code on machines without
apache.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-17 01:12:52 +08:00
|
|
|
transport_check_allowed(name);
|
|
|
|
|
2009-12-09 23:26:27 +08:00
|
|
|
if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
|
|
|
|
debug = 1;
|
|
|
|
|
2009-08-05 13:01:53 +08:00
|
|
|
transport->data = data;
|
2017-12-15 05:44:45 +08:00
|
|
|
transport->vtable = &vtable;
|
2009-12-09 23:26:31 +08:00
|
|
|
transport->smart_options = &(data->transport_options);
|
2009-08-05 13:01:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-10-13 00:39:41 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Linux pipes can buffer 65536 bytes at once (and most platforms can
|
|
|
|
* buffer less), so attempt reads and writes with up to that size.
|
|
|
|
*/
|
|
|
|
#define BUFFERSIZE 65536
|
|
|
|
/* This should be enough to hold debugging message. */
|
|
|
|
#define PBUFFERSIZE 8192
|
|
|
|
|
|
|
|
/* Print bidirectional transfer loop debug message. */
|
2013-07-10 08:18:40 +08:00
|
|
|
__attribute__((format (printf, 1, 2)))
|
2010-10-13 00:39:41 +08:00
|
|
|
static void transfer_debug(const char *fmt, ...)
|
|
|
|
{
|
2017-08-22 01:43:48 +08:00
|
|
|
/*
|
|
|
|
* NEEDSWORK: This function is sometimes used from multiple threads, and
|
|
|
|
* we end up using debug_enabled racily. That "should not matter" since
|
|
|
|
* we always write the same value, but it's still wrong. This function
|
|
|
|
* is listed in .tsan-suppressions for the time being.
|
|
|
|
*/
|
|
|
|
|
2010-10-13 00:39:41 +08:00
|
|
|
va_list args;
|
|
|
|
char msgbuf[PBUFFERSIZE];
|
|
|
|
static int debug_enabled = -1;
|
|
|
|
|
|
|
|
if (debug_enabled < 0)
|
|
|
|
debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
|
|
|
|
if (!debug_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
vsnprintf(msgbuf, PBUFFERSIZE, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
fprintf(stderr, "Transfer loop debugging: %s\n", msgbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stream state: More data may be coming in this direction. */
|
2016-08-09 16:53:38 +08:00
|
|
|
#define SSTATE_TRANSFERRING 0
|
2010-10-13 00:39:41 +08:00
|
|
|
/*
|
|
|
|
* Stream state: No more data coming in this direction, flushing rest of
|
|
|
|
* data.
|
|
|
|
*/
|
|
|
|
#define SSTATE_FLUSHING 1
|
|
|
|
/* Stream state: Transfer in this direction finished. */
|
|
|
|
#define SSTATE_FINISHED 2
|
|
|
|
|
2016-08-09 16:53:38 +08:00
|
|
|
#define STATE_NEEDS_READING(state) ((state) <= SSTATE_TRANSFERRING)
|
2010-10-13 00:39:41 +08:00
|
|
|
#define STATE_NEEDS_WRITING(state) ((state) <= SSTATE_FLUSHING)
|
|
|
|
#define STATE_NEEDS_CLOSING(state) ((state) == SSTATE_FLUSHING)
|
|
|
|
|
|
|
|
/* Unidirectional transfer. */
|
|
|
|
struct unidirectional_transfer {
|
|
|
|
/* Source */
|
|
|
|
int src;
|
|
|
|
/* Destination */
|
|
|
|
int dest;
|
|
|
|
/* Is source socket? */
|
|
|
|
int src_is_sock;
|
|
|
|
/* Is destination socket? */
|
|
|
|
int dest_is_sock;
|
2013-04-12 06:36:10 +08:00
|
|
|
/* Transfer state (TRANSFERRING/FLUSHING/FINISHED) */
|
2010-10-13 00:39:41 +08:00
|
|
|
int state;
|
|
|
|
/* Buffer. */
|
|
|
|
char buf[BUFFERSIZE];
|
|
|
|
/* Buffer used. */
|
|
|
|
size_t bufuse;
|
|
|
|
/* Name of source. */
|
|
|
|
const char *src_name;
|
|
|
|
/* Name of destination. */
|
|
|
|
const char *dest_name;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Closes the target (for writing) if transfer has finished. */
|
|
|
|
static void udt_close_if_finished(struct unidirectional_transfer *t)
|
|
|
|
{
|
|
|
|
if (STATE_NEEDS_CLOSING(t->state) && !t->bufuse) {
|
|
|
|
t->state = SSTATE_FINISHED;
|
|
|
|
if (t->dest_is_sock)
|
|
|
|
shutdown(t->dest, SHUT_WR);
|
|
|
|
else
|
|
|
|
close(t->dest);
|
|
|
|
transfer_debug("Closed %s.", t->dest_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-05-06 20:36:46 +08:00
|
|
|
* Tries to read data from source into buffer. If buffer is full,
|
2010-10-13 00:39:41 +08:00
|
|
|
* no data is read. Returns 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
static int udt_do_read(struct unidirectional_transfer *t)
|
|
|
|
{
|
|
|
|
ssize_t bytes;
|
|
|
|
|
|
|
|
if (t->bufuse == BUFFERSIZE)
|
|
|
|
return 0; /* No space for more. */
|
|
|
|
|
|
|
|
transfer_debug("%s is readable", t->src_name);
|
2019-01-04 05:03:48 +08:00
|
|
|
bytes = xread(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
|
2018-01-11 14:31:10 +08:00
|
|
|
if (bytes < 0) {
|
2018-07-21 15:49:41 +08:00
|
|
|
error_errno(_("read(%s) failed"), t->src_name);
|
2010-10-13 00:39:41 +08:00
|
|
|
return -1;
|
|
|
|
} else if (bytes == 0) {
|
|
|
|
transfer_debug("%s EOF (with %i bytes in buffer)",
|
2013-07-10 08:18:40 +08:00
|
|
|
t->src_name, (int)t->bufuse);
|
2010-10-13 00:39:41 +08:00
|
|
|
t->state = SSTATE_FLUSHING;
|
|
|
|
} else if (bytes > 0) {
|
|
|
|
t->bufuse += bytes;
|
|
|
|
transfer_debug("Read %i bytes from %s (buffer now at %i)",
|
|
|
|
(int)bytes, t->src_name, (int)t->bufuse);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tries to write data from buffer into destination. If buffer is empty,
|
|
|
|
* no data is written. Returns 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
static int udt_do_write(struct unidirectional_transfer *t)
|
|
|
|
{
|
2011-03-05 07:16:26 +08:00
|
|
|
ssize_t bytes;
|
2010-10-13 00:39:41 +08:00
|
|
|
|
|
|
|
if (t->bufuse == 0)
|
|
|
|
return 0; /* Nothing to write. */
|
|
|
|
|
|
|
|
transfer_debug("%s is writable", t->dest_name);
|
2014-01-17 22:17:09 +08:00
|
|
|
bytes = xwrite(t->dest, t->buf, t->bufuse);
|
2018-01-11 14:31:10 +08:00
|
|
|
if (bytes < 0) {
|
2018-07-21 15:49:41 +08:00
|
|
|
error_errno(_("write(%s) failed"), t->dest_name);
|
2010-10-13 00:39:41 +08:00
|
|
|
return -1;
|
|
|
|
} else if (bytes > 0) {
|
|
|
|
t->bufuse -= bytes;
|
|
|
|
if (t->bufuse)
|
|
|
|
memmove(t->buf, t->buf + bytes, t->bufuse);
|
|
|
|
transfer_debug("Wrote %i bytes to %s (buffer now at %i)",
|
|
|
|
(int)bytes, t->dest_name, (int)t->bufuse);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* State of bidirectional transfer loop. */
|
|
|
|
struct bidirectional_transfer_state {
|
|
|
|
/* Direction from program to git. */
|
|
|
|
struct unidirectional_transfer ptg;
|
|
|
|
/* Direction from git to program. */
|
|
|
|
struct unidirectional_transfer gtp;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void *udt_copy_task_routine(void *udt)
|
|
|
|
{
|
|
|
|
struct unidirectional_transfer *t = (struct unidirectional_transfer *)udt;
|
|
|
|
while (t->state != SSTATE_FINISHED) {
|
|
|
|
if (STATE_NEEDS_READING(t->state))
|
|
|
|
if (udt_do_read(t))
|
|
|
|
return NULL;
|
|
|
|
if (STATE_NEEDS_WRITING(t->state))
|
|
|
|
if (udt_do_write(t))
|
|
|
|
return NULL;
|
|
|
|
if (STATE_NEEDS_CLOSING(t->state))
|
|
|
|
udt_close_if_finished(t);
|
|
|
|
}
|
|
|
|
return udt; /* Just some non-NULL value. */
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NO_PTHREADS
|
|
|
|
|
|
|
|
/*
|
2013-07-29 16:18:21 +08:00
|
|
|
* Join thread, with appropriate errors on failure. Name is name for the
|
2010-10-13 00:39:41 +08:00
|
|
|
* thread (for error messages). Returns 0 on success, 1 on failure.
|
|
|
|
*/
|
|
|
|
static int tloop_join(pthread_t thread, const char *name)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
void *tret;
|
|
|
|
err = pthread_join(thread, &tret);
|
|
|
|
if (!tret) {
|
2018-07-21 15:49:41 +08:00
|
|
|
error(_("%s thread failed"), name);
|
2010-10-13 00:39:41 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (err) {
|
2018-07-21 15:49:41 +08:00
|
|
|
error(_("%s thread failed to join: %s"), name, strerror(err));
|
2010-10-13 00:39:41 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spawn the transfer tasks and then wait for them. Returns 0 on success,
|
|
|
|
* -1 on failure.
|
|
|
|
*/
|
|
|
|
static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
|
|
|
|
{
|
|
|
|
pthread_t gtp_thread;
|
|
|
|
pthread_t ptg_thread;
|
|
|
|
int err;
|
|
|
|
int ret = 0;
|
|
|
|
err = pthread_create(>p_thread, NULL, udt_copy_task_routine,
|
|
|
|
&s->gtp);
|
|
|
|
if (err)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("can't start thread for copying data: %s"), strerror(err));
|
2010-10-13 00:39:41 +08:00
|
|
|
err = pthread_create(&ptg_thread, NULL, udt_copy_task_routine,
|
|
|
|
&s->ptg);
|
|
|
|
if (err)
|
2018-07-21 15:49:41 +08:00
|
|
|
die(_("can't start thread for copying data: %s"), strerror(err));
|
2010-10-13 00:39:41 +08:00
|
|
|
|
|
|
|
ret |= tloop_join(gtp_thread, "Git to program copy");
|
|
|
|
ret |= tloop_join(ptg_thread, "Program to git copy");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
/* Close the source and target (for writing) for transfer. */
|
|
|
|
static void udt_kill_transfer(struct unidirectional_transfer *t)
|
|
|
|
{
|
|
|
|
t->state = SSTATE_FINISHED;
|
|
|
|
/*
|
|
|
|
* Socket read end left open isn't a disaster if nobody
|
|
|
|
* attempts to read from it (mingw compat headers do not
|
|
|
|
* have SHUT_RD)...
|
|
|
|
*
|
|
|
|
* We can't fully close the socket since otherwise gtp
|
|
|
|
* task would first close the socket it sends data to
|
|
|
|
* while closing the ptg file descriptors.
|
|
|
|
*/
|
|
|
|
if (!t->src_is_sock)
|
|
|
|
close(t->src);
|
|
|
|
if (t->dest_is_sock)
|
|
|
|
shutdown(t->dest, SHUT_WR);
|
|
|
|
else
|
|
|
|
close(t->dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-07-29 16:18:21 +08:00
|
|
|
* Join process, with appropriate errors on failure. Name is name for the
|
2010-10-13 00:39:41 +08:00
|
|
|
* process (for error messages). Returns 0 on success, 1 on failure.
|
|
|
|
*/
|
|
|
|
static int tloop_join(pid_t pid, const char *name)
|
|
|
|
{
|
|
|
|
int tret;
|
|
|
|
if (waitpid(pid, &tret, 0) < 0) {
|
2018-07-21 15:49:41 +08:00
|
|
|
error_errno(_("%s process failed to wait"), name);
|
2010-10-13 00:39:41 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
|
2018-07-21 15:49:41 +08:00
|
|
|
error(_("%s process failed"), name);
|
2010-10-13 00:39:41 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spawn the transfer tasks and then wait for them. Returns 0 on success,
|
|
|
|
* -1 on failure.
|
|
|
|
*/
|
|
|
|
static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
|
|
|
|
{
|
|
|
|
pid_t pid1, pid2;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Fork thread #1: git to program. */
|
|
|
|
pid1 = fork();
|
|
|
|
if (pid1 < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die_errno(_("can't start thread for copying data"));
|
2010-10-13 00:39:41 +08:00
|
|
|
else if (pid1 == 0) {
|
|
|
|
udt_kill_transfer(&s->ptg);
|
|
|
|
exit(udt_copy_task_routine(&s->gtp) ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fork thread #2: program to git. */
|
|
|
|
pid2 = fork();
|
|
|
|
if (pid2 < 0)
|
2018-07-21 15:49:41 +08:00
|
|
|
die_errno(_("can't start thread for copying data"));
|
2010-10-13 00:39:41 +08:00
|
|
|
else if (pid2 == 0) {
|
|
|
|
udt_kill_transfer(&s->gtp);
|
|
|
|
exit(udt_copy_task_routine(&s->ptg) ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close both streams in parent as to not interfere with
|
|
|
|
* end of file detection and wait for both tasks to finish.
|
|
|
|
*/
|
|
|
|
udt_kill_transfer(&s->gtp);
|
|
|
|
udt_kill_transfer(&s->ptg);
|
|
|
|
ret |= tloop_join(pid1, "Git to program copy");
|
|
|
|
ret |= tloop_join(pid2, "Program to git copy");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copies data from stdin to output and from input to stdout simultaneously.
|
|
|
|
* Additionally filtering through given filter. If filter is NULL, uses
|
|
|
|
* identity filter.
|
|
|
|
*/
|
|
|
|
int bidirectional_transfer_loop(int input, int output)
|
|
|
|
{
|
|
|
|
struct bidirectional_transfer_state state;
|
|
|
|
|
|
|
|
/* Fill the state fields. */
|
|
|
|
state.ptg.src = input;
|
|
|
|
state.ptg.dest = 1;
|
|
|
|
state.ptg.src_is_sock = (input == output);
|
|
|
|
state.ptg.dest_is_sock = 0;
|
2016-08-09 16:53:38 +08:00
|
|
|
state.ptg.state = SSTATE_TRANSFERRING;
|
2010-10-13 00:39:41 +08:00
|
|
|
state.ptg.bufuse = 0;
|
|
|
|
state.ptg.src_name = "remote input";
|
|
|
|
state.ptg.dest_name = "stdout";
|
|
|
|
|
|
|
|
state.gtp.src = 0;
|
|
|
|
state.gtp.dest = output;
|
|
|
|
state.gtp.src_is_sock = 0;
|
|
|
|
state.gtp.dest_is_sock = (input == output);
|
2016-08-09 16:53:38 +08:00
|
|
|
state.gtp.state = SSTATE_TRANSFERRING;
|
2010-10-13 00:39:41 +08:00
|
|
|
state.gtp.bufuse = 0;
|
|
|
|
state.gtp.src_name = "stdin";
|
|
|
|
state.gtp.dest_name = "remote output";
|
|
|
|
|
|
|
|
return tloop_spawnwait_tasks(&state);
|
|
|
|
}
|
2020-04-17 17:45:36 +08:00
|
|
|
|
|
|
|
void reject_atomic_push(struct ref *remote_refs, int mirror_mode)
|
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
|
|
|
|
/* Mark other refs as failed */
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
|
|
if (!ref->peer_ref && !mirror_mode)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (ref->status) {
|
|
|
|
case REF_STATUS_NONE:
|
|
|
|
case REF_STATUS_OK:
|
|
|
|
case REF_STATUS_EXPECTING_REPORT:
|
|
|
|
ref->status = REF_STATUS_ATOMIC_PUSH_FAILED;
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
break; /* do nothing */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|