2007-09-11 07:03:04 +04:00
|
|
|
#include "cache.h"
|
2017-06-14 21:07:36 +03:00
|
|
|
#include "config.h"
|
2007-09-11 07:03:04 +04:00
|
|
|
#include "transport.h"
|
|
|
|
#include "run-command.h"
|
2007-09-11 07:03:11 +04:00
|
|
|
#include "pkt-line.h"
|
|
|
|
#include "fetch-pack.h"
|
2013-07-09 00:56:53 +04:00
|
|
|
#include "remote.h"
|
|
|
|
#include "connect.h"
|
2007-10-30 05:03:42 +03:00
|
|
|
#include "send-pack.h"
|
2007-09-11 07:03:11 +04:00
|
|
|
#include "walker.h"
|
2007-09-11 07:03:21 +04:00
|
|
|
#include "bundle.h"
|
2007-10-01 03:59:39 +04:00
|
|
|
#include "dir.h"
|
|
|
|
#include "refs.h"
|
2018-05-17 01:57:48 +03:00
|
|
|
#include "refspec.h"
|
2010-01-17 00:45:31 +03:00
|
|
|
#include "branch.h"
|
2010-05-23 13:17:55 +04:00
|
|
|
#include "url.h"
|
2011-08-20 02:08:47 +04:00
|
|
|
#include "submodule.h"
|
2012-03-29 11:21:23 +04:00
|
|
|
#include "string-list.h"
|
2020-03-30 17:03:46 +03:00
|
|
|
#include "oid-array.h"
|
2015-11-16 11:05:58 +03:00
|
|
|
#include "sigchain.h"
|
2017-12-15 00:44:45 +03:00
|
|
|
#include "transport-internal.h"
|
2018-03-14 21:31:45 +03:00
|
|
|
#include "protocol.h"
|
2018-03-23 20:20:56 +03:00
|
|
|
#include "object-store.h"
|
2018-04-21 13:10:00 +03:00
|
|
|
#include "color.h"
|
|
|
|
|
|
|
|
static int transport_use_color = -1;
|
|
|
|
static char transport_colors[][COLOR_MAXLEN] = {
|
|
|
|
GIT_COLOR_RESET,
|
|
|
|
GIT_COLOR_RED /* REJECTED */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum color_transport {
|
|
|
|
TRANSPORT_COLOR_RESET = 0,
|
|
|
|
TRANSPORT_COLOR_REJECTED = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
static int transport_color_config(void)
|
|
|
|
{
|
|
|
|
const char *keys[] = {
|
|
|
|
"color.transport.reset",
|
|
|
|
"color.transport.rejected"
|
|
|
|
}, *key = "color.transport";
|
|
|
|
char *value;
|
|
|
|
int i;
|
|
|
|
static int initialized;
|
|
|
|
|
|
|
|
if (initialized)
|
|
|
|
return 0;
|
|
|
|
initialized = 1;
|
|
|
|
|
|
|
|
if (!git_config_get_string(key, &value))
|
|
|
|
transport_use_color = git_config_colorbool(key, value);
|
|
|
|
|
|
|
|
if (!want_color_stderr(transport_use_color))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(keys); i++)
|
|
|
|
if (!git_config_get_string(keys[i], &value)) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(keys[i]);
|
|
|
|
if (color_parse(value, transport_colors[i]) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *transport_get_color(enum color_transport ix)
|
|
|
|
{
|
|
|
|
if (want_color_stderr(transport_use_color))
|
|
|
|
return transport_colors[ix];
|
|
|
|
return "";
|
|
|
|
}
|
2007-10-01 03:59:39 +04:00
|
|
|
|
2010-01-17 00:45:31 +03:00
|
|
|
static void set_upstreams(struct transport *transport, struct ref *refs,
|
|
|
|
int pretend)
|
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
for (ref = refs; ref; ref = ref->next) {
|
|
|
|
const char *localname;
|
|
|
|
const char *tmp;
|
|
|
|
const char *remotename;
|
|
|
|
int flag = 0;
|
|
|
|
/*
|
|
|
|
* Check suitability for tracking. Must be successful /
|
|
|
|
* already up-to-date ref create/modify (not delete).
|
|
|
|
*/
|
|
|
|
if (ref->status != REF_STATUS_OK &&
|
|
|
|
ref->status != REF_STATUS_UPTODATE)
|
|
|
|
continue;
|
|
|
|
if (!ref->peer_ref)
|
|
|
|
continue;
|
2015-11-10 05:22:20 +03:00
|
|
|
if (is_null_oid(&ref->new_oid))
|
2010-01-17 00:45:31 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Follow symbolic refs (mainly for HEAD). */
|
|
|
|
localname = ref->peer_ref->name;
|
|
|
|
remotename = ref->name;
|
2014-07-15 23:59:36 +04:00
|
|
|
tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
|
2017-09-23 12:45:04 +03:00
|
|
|
NULL, &flag);
|
2010-01-17 00:45:31 +03:00
|
|
|
if (tmp && flag & REF_ISSYMREF &&
|
2013-12-01 00:55:40 +04:00
|
|
|
starts_with(tmp, "refs/heads/"))
|
2010-01-17 00:45:31 +03:00
|
|
|
localname = tmp;
|
|
|
|
|
|
|
|
/* Both source and destination must be local branches. */
|
2013-12-01 00:55:40 +04:00
|
|
|
if (!localname || !starts_with(localname, "refs/heads/"))
|
2010-01-17 00:45:31 +03:00
|
|
|
continue;
|
2013-12-01 00:55:40 +04:00
|
|
|
if (!remotename || !starts_with(remotename, "refs/heads/"))
|
2010-01-17 00:45:31 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!pretend)
|
|
|
|
install_branch_config(BRANCH_CONFIG_VERBOSE,
|
|
|
|
localname + 11, transport->remote->name,
|
|
|
|
remotename);
|
|
|
|
else
|
2016-06-17 23:20:53 +03:00
|
|
|
printf(_("Would set upstream of '%s' to '%s' of '%s'\n"),
|
2010-01-17 00:45:31 +03:00
|
|
|
localname + 11, remotename + 11,
|
|
|
|
transport->remote->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-11 07:03:21 +04:00
|
|
|
struct bundle_transport_data {
|
|
|
|
int fd;
|
|
|
|
struct bundle_header header;
|
2019-08-22 01:20:10 +03:00
|
|
|
unsigned get_refs_from_bundle_called : 1;
|
2007-09-11 07:03:21 +04:00
|
|
|
};
|
|
|
|
|
2018-03-15 20:31:22 +03:00
|
|
|
static struct ref *get_refs_from_bundle(struct transport *transport,
|
|
|
|
int for_push,
|
2020-07-28 23:25:12 +03:00
|
|
|
const struct strvec *ref_prefixes)
|
2007-09-11 07:03:21 +04:00
|
|
|
{
|
|
|
|
struct bundle_transport_data *data = transport->data;
|
|
|
|
struct ref *result = NULL;
|
|
|
|
int i;
|
|
|
|
|
2009-03-09 04:06:07 +03:00
|
|
|
if (for_push)
|
|
|
|
return NULL;
|
|
|
|
|
2019-08-22 01:20:10 +03:00
|
|
|
data->get_refs_from_bundle_called = 1;
|
|
|
|
|
2007-09-11 07:03:21 +04:00
|
|
|
if (data->fd > 0)
|
|
|
|
close(data->fd);
|
|
|
|
data->fd = read_bundle_header(transport->url, &data->header);
|
|
|
|
if (data->fd < 0)
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("could not read bundle '%s'"), transport->url);
|
2020-06-19 20:56:00 +03:00
|
|
|
|
|
|
|
transport->hash_algo = data->header.hash_algo;
|
|
|
|
|
2007-09-11 07:03:21 +04:00
|
|
|
for (i = 0; i < data->header.references.nr; i++) {
|
|
|
|
struct ref_list_entry *e = data->header.references.list + i;
|
2008-10-18 12:44:18 +04:00
|
|
|
struct ref *ref = alloc_ref(e->name);
|
2017-05-01 05:28:59 +03:00
|
|
|
oidcpy(&ref->old_oid, &e->oid);
|
2007-09-11 07:03:21 +04:00
|
|
|
ref->next = result;
|
|
|
|
result = ref;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-09-14 11:31:23 +04:00
|
|
|
static int fetch_refs_from_bundle(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-01 23:13:20 +03:00
|
|
|
int nr_heads, struct ref **to_fetch)
|
2007-09-11 07:03:21 +04:00
|
|
|
{
|
|
|
|
struct bundle_transport_data *data = transport->data;
|
2020-06-19 20:56:00 +03:00
|
|
|
int ret;
|
2019-08-22 01:20:10 +03:00
|
|
|
|
|
|
|
if (!data->get_refs_from_bundle_called)
|
|
|
|
get_refs_from_bundle(transport, 0, NULL);
|
2020-06-19 20:56:00 +03:00
|
|
|
ret = unbundle(the_repository, &data->header, data->fd,
|
|
|
|
transport->progress ? BUNDLE_VERBOSE : 0);
|
|
|
|
transport->hash_algo = data->header.hash_algo;
|
|
|
|
return ret;
|
2007-09-11 07:03:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int close_bundle(struct transport *transport)
|
|
|
|
{
|
|
|
|
struct bundle_transport_data *data = transport->data;
|
|
|
|
if (data->fd > 0)
|
|
|
|
close(data->fd);
|
2007-09-19 08:49:42 +04:00
|
|
|
free(data);
|
2007-09-11 07:03:21 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-11 07:03:04 +04:00
|
|
|
struct git_transport_data {
|
2009-12-09 18:26:30 +03:00
|
|
|
struct git_transport_options options;
|
2008-02-04 21:26:23 +03:00
|
|
|
struct child_process *conn;
|
|
|
|
int fd[2];
|
2009-12-09 18:26:31 +03:00
|
|
|
unsigned got_remote_heads : 1;
|
2018-03-14 21:31:46 +03:00
|
|
|
enum protocol_version version;
|
2017-03-31 04:40:00 +03:00
|
|
|
struct oid_array extra_have;
|
|
|
|
struct oid_array shallow;
|
2007-09-11 07:03:04 +04:00
|
|
|
};
|
|
|
|
|
2009-12-09 18:26:30 +03:00
|
|
|
static int set_git_option(struct git_transport_options *opts,
|
2007-09-11 07:03:04 +04:00
|
|
|
const char *name, const char *value)
|
|
|
|
{
|
2007-09-11 07:03:11 +04:00
|
|
|
if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
|
2009-12-09 18:26:30 +03:00
|
|
|
opts->uploadpack = value;
|
2007-09-11 07:03:11 +04:00
|
|
|
return 0;
|
|
|
|
} else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
|
2009-12-09 18:26:30 +03:00
|
|
|
opts->receivepack = value;
|
2007-09-11 07:03:04 +04:00
|
|
|
return 0;
|
|
|
|
} else if (!strcmp(name, TRANS_OPT_THIN)) {
|
2009-12-09 18:26:30 +03:00
|
|
|
opts->thin = !!value;
|
2007-09-11 07:03:04 +04:00
|
|
|
return 0;
|
2008-03-04 06:27:40 +03:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
|
2009-12-09 18:26:30 +03:00
|
|
|
opts->followtags = !!value;
|
2008-03-04 06:27:40 +03:00
|
|
|
return 0;
|
2007-09-11 07:03:11 +04:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_KEEP)) {
|
2009-12-09 18:26:30 +03:00
|
|
|
opts->keep = !!value;
|
2007-09-11 07:03:11 +04:00
|
|
|
return 0;
|
2013-12-05 17:02:42 +04:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_UPDATE_SHALLOW)) {
|
|
|
|
opts->update_shallow = !!value;
|
|
|
|
return 0;
|
2007-09-11 07:03:11 +04:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_DEPTH)) {
|
|
|
|
if (!value)
|
2009-12-09 18:26:30 +03:00
|
|
|
opts->depth = 0;
|
2012-01-04 14:01:55 +04:00
|
|
|
else {
|
|
|
|
char *end;
|
|
|
|
opts->depth = strtol(value, &end, 0);
|
|
|
|
if (*end)
|
2016-06-17 23:20:53 +03:00
|
|
|
die(_("transport: invalid depth option '%s'"), value);
|
2012-01-04 14:01:55 +04:00
|
|
|
}
|
2007-09-11 07:03:11 +04:00
|
|
|
return 0;
|
2016-06-12 13:53:59 +03:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {
|
|
|
|
opts->deepen_since = value;
|
|
|
|
return 0;
|
2016-06-12 13:54:04 +03:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {
|
|
|
|
opts->deepen_not = (const struct string_list *)value;
|
|
|
|
return 0;
|
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 13:54:09 +03:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
|
|
|
|
opts->deepen_relative = !!value;
|
|
|
|
return 0;
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 19:58:49 +03:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_FROM_PROMISOR)) {
|
|
|
|
opts->from_promisor = !!value;
|
|
|
|
return 0;
|
2017-12-08 18:58:40 +03:00
|
|
|
} else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) {
|
2019-06-28 01:54:12 +03:00
|
|
|
list_objects_filter_die_if_populated(&opts->filter_options);
|
2017-12-08 18:58:40 +03:00
|
|
|
parse_list_objects_filter(&opts->filter_options, value);
|
|
|
|
return 0;
|
2007-09-11 07:03:04 +04:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-01-29 01:51:23 +03:00
|
|
|
static int connect_setup(struct transport *transport, int for_push)
|
2008-02-04 21:26:23 +03:00
|
|
|
{
|
|
|
|
struct git_transport_data *data = transport->data;
|
2016-01-29 01:51:23 +03:00
|
|
|
int flags = transport->verbose > 0 ? CONNECT_VERBOSE : 0;
|
2009-12-09 18:26:31 +03:00
|
|
|
|
|
|
|
if (data->conn)
|
|
|
|
return 0;
|
|
|
|
|
2016-02-03 07:09:14 +03:00
|
|
|
switch (transport->family) {
|
|
|
|
case TRANSPORT_FAMILY_ALL: break;
|
|
|
|
case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;
|
|
|
|
case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;
|
|
|
|
}
|
|
|
|
|
2011-09-06 22:06:32 +04:00
|
|
|
data->conn = git_connect(data->fd, transport->url,
|
|
|
|
for_push ? data->options.receivepack :
|
|
|
|
data->options.uploadpack,
|
2016-01-29 01:51:23 +03:00
|
|
|
flags);
|
2009-12-09 18:26:31 +03:00
|
|
|
|
2008-02-04 21:26:23 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-12 22:51:21 +03:00
|
|
|
static void die_if_server_options(struct transport *transport)
|
|
|
|
{
|
|
|
|
if (!transport->server_options || !transport->server_options->nr)
|
|
|
|
return;
|
|
|
|
advise(_("see protocol.version in 'git help config' for more details"));
|
|
|
|
die(_("server options require protocol version 2 or later"));
|
|
|
|
}
|
|
|
|
|
2018-09-27 22:24:04 +03:00
|
|
|
/*
|
|
|
|
* Obtains the protocol version from the transport and writes it to
|
|
|
|
* transport->data->version, first connecting if not already connected.
|
|
|
|
*
|
|
|
|
* If the protocol version is one that allows skipping the listing of remote
|
|
|
|
* refs, and must_list_refs is 0, the listing of remote refs is skipped and
|
|
|
|
* this function returns NULL. Otherwise, this function returns the list of
|
|
|
|
* remote refs.
|
|
|
|
*/
|
|
|
|
static struct ref *handshake(struct transport *transport, int for_push,
|
2020-07-28 23:25:12 +03:00
|
|
|
const struct strvec *ref_prefixes,
|
2018-09-27 22:24:04 +03:00
|
|
|
int must_list_refs)
|
2007-09-11 07:03:11 +04:00
|
|
|
{
|
|
|
|
struct git_transport_data *data = transport->data;
|
2018-03-14 21:31:45 +03:00
|
|
|
struct ref *refs = NULL;
|
|
|
|
struct packet_reader reader;
|
2020-11-12 02:29:30 +03:00
|
|
|
int sid_len;
|
|
|
|
const char *server_sid;
|
2007-09-11 07:03:11 +04:00
|
|
|
|
2016-01-29 01:51:23 +03:00
|
|
|
connect_setup(transport, for_push);
|
2018-03-14 21:31:45 +03:00
|
|
|
|
|
|
|
packet_reader_init(&reader, data->fd[0], NULL, 0,
|
|
|
|
PACKET_READ_CHOMP_NEWLINE |
|
pack-protocol.txt: accept error packets in any context
In the Git pack protocol definition, an error packet may appear only in
a certain context. However, servers can face a runtime error (e.g. I/O
error) at an arbitrary timing. This patch changes the protocol to allow
an error packet to be sent instead of any packet.
Without this protocol spec change, when a server cannot process a
request, there's no way to tell that to a client. Since the server
cannot produce a valid response, it would be forced to cut a connection
without telling why. With this protocol spec change, the server can be
more gentle in this situation. An old client may see these error packets
as an unexpected packet, but this is not worse than having an unexpected
EOF.
Following this protocol spec change, the error packet handling code is
moved to pkt-line.c. Implementation wise, this implementation uses
pkt-line to communicate with a subprocess. Since this is not a part of
Git protocol, it's possible that a packet that is not supposed to be an
error packet is mistakenly parsed as an error packet. This error packet
handling is enabled only for the Git pack protocol parsing code
considering this.
Signed-off-by: Masaya Suzuki <masayasuzuki@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-30 00:19:15 +03:00
|
|
|
PACKET_READ_GENTLE_ON_EOF |
|
|
|
|
PACKET_READ_DIE_ON_ERR_PACKET);
|
2018-03-14 21:31:45 +03:00
|
|
|
|
2018-03-14 21:31:46 +03:00
|
|
|
data->version = discover_version(&reader);
|
|
|
|
switch (data->version) {
|
2018-03-14 21:31:47 +03:00
|
|
|
case protocol_v2:
|
2020-11-12 02:29:30 +03:00
|
|
|
if (server_feature_v2("session-id", &server_sid))
|
|
|
|
trace2_data_string("transfer", NULL, "server-sid", server_sid);
|
2018-09-27 22:24:04 +03:00
|
|
|
if (must_list_refs)
|
|
|
|
get_remote_refs(data->fd[1], &reader, &refs, for_push,
|
|
|
|
ref_prefixes,
|
2020-05-19 13:54:00 +03:00
|
|
|
transport->server_options,
|
|
|
|
transport->stateless_rpc);
|
2018-03-14 21:31:47 +03:00
|
|
|
break;
|
2018-03-14 21:31:45 +03:00
|
|
|
case protocol_v1:
|
|
|
|
case protocol_v0:
|
2019-04-12 22:51:21 +03:00
|
|
|
die_if_server_options(transport);
|
2018-03-14 21:31:45 +03:00
|
|
|
get_remote_heads(&reader, &refs,
|
|
|
|
for_push ? REF_NORMAL : 0,
|
|
|
|
&data->extra_have,
|
|
|
|
&data->shallow);
|
2020-11-12 02:29:30 +03:00
|
|
|
server_sid = server_feature_value("session-id", &sid_len);
|
|
|
|
if (server_sid) {
|
|
|
|
char *sid = xstrndup(server_sid, sid_len);
|
|
|
|
trace2_data_string("transfer", NULL, "server-sid", sid);
|
|
|
|
free(sid);
|
|
|
|
}
|
2018-03-14 21:31:45 +03:00
|
|
|
break;
|
|
|
|
case protocol_unknown_version:
|
|
|
|
BUG("unknown protocol version");
|
|
|
|
}
|
2009-12-09 18:26:31 +03:00
|
|
|
data->got_remote_heads = 1;
|
2020-05-25 22:58:55 +03:00
|
|
|
transport->hash_algo = reader.hash_algo;
|
2007-09-11 07:03:11 +04:00
|
|
|
|
2018-09-27 22:24:04 +03:00
|
|
|
if (reader.line_peeked)
|
|
|
|
BUG("buffer must be empty at the end of handshake()");
|
|
|
|
|
2007-09-11 07:03:11 +04:00
|
|
|
return refs;
|
|
|
|
}
|
|
|
|
|
2018-09-27 22:24:04 +03:00
|
|
|
static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
|
2020-07-28 23:25:12 +03:00
|
|
|
const struct strvec *ref_prefixes)
|
2018-09-27 22:24:04 +03:00
|
|
|
{
|
|
|
|
return handshake(transport, for_push, ref_prefixes, 1);
|
|
|
|
}
|
|
|
|
|
2007-09-14 11:31:23 +04:00
|
|
|
static int fetch_refs_via_pack(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-01 23:13:20 +03:00
|
|
|
int nr_heads, struct ref **to_fetch)
|
2007-09-11 07:03:11 +04:00
|
|
|
{
|
2017-02-22 19:02:15 +03:00
|
|
|
int ret = 0;
|
2007-09-11 07:03:11 +04:00
|
|
|
struct git_transport_data *data = transport->data;
|
2018-03-14 21:31:46 +03:00
|
|
|
struct ref *refs = NULL;
|
2007-09-11 07:03:11 +04:00
|
|
|
struct fetch_pack_args args;
|
2008-02-28 19:10:51 +03:00
|
|
|
struct ref *refs_tmp = NULL;
|
2007-09-11 07:03:11 +04:00
|
|
|
|
2007-09-19 08:49:35 +04:00
|
|
|
memset(&args, 0, sizeof(args));
|
2009-12-09 18:26:30 +03:00
|
|
|
args.uploadpack = data->options.uploadpack;
|
|
|
|
args.keep_pack = data->options.keep;
|
2007-09-19 08:49:35 +04:00
|
|
|
args.lock_pack = 1;
|
2009-12-09 18:26:30 +03:00
|
|
|
args.use_thin_pack = data->options.thin;
|
|
|
|
args.include_tag = data->options.followtags;
|
make "git push -v" actually verbose
Providing a single "-v" to "git push" currently does
nothing. Giving two flags ("git push -v -v") turns on the
first level of verbosity.
This is caused by a regression introduced in 8afd8dc (push:
support multiple levels of verbosity, 2010-02-24). Before
the series containing 8afd8dc, the verbosity handling for
fetching and pushing was completely separate. Commit bde873c
refactored the verbosity handling out of the fetch side, and
then 8afd8dc converted push to use the refactored code.
However, the fetch and push sides numbered and passed along
their verbosity levels differently. For both, a verbosity
level of "-1" meant "quiet", and "0" meant "default output".
But from there they differed.
For fetch, a verbosity level of "1" indicated to the "fetch"
program that it should make the status table slightly more
verbose, showing up-to-date entries. A verbosity level of
"2" meant that we should pass a verbose flag to the
transport; in the case of fetch-pack, this displays protocol
debugging information.
As a result, the refactored code in bde873c checks for
"verbosity >= 2", and only then passes it on to the
transport. From the transport code's perspective, a
verbosity of 0 or 1 both meant "0".
Push, on the other hand, does not show its own status table;
that is always handled by the transport layer or below
(originally send-pack itself, but these days it is done by
the transport code). So a verbosity level of 1 meant that we
should pass the verbose flag to send-pack, so that it knows
we want a verbose status table. However, once 8afd8dc
switched it to the refactored fetch code, a verbosity level
of 1 was now being ignored. Thus, you needed to
artificially bump the verbosity to 2 (via "-v -v") to have
any effect.
We can fix this by letting the transport code know about the
true verbosity level (i.e., let it distinguish level 0 or
1).
We then have to also make an adjustment to any transport
methods that assumed "verbose > 0" meant they could spew
lots of debugging information. Before, they could only get
"0" or "2", but now they will also receive "1". They need to
adjust their condition for turning on such spew from
"verbose > 0" to "verbose > 1".
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-17 13:37:15 +04:00
|
|
|
args.verbose = (transport->verbose > 1);
|
2008-10-05 17:53:00 +04:00
|
|
|
args.quiet = (transport->verbose < 0);
|
2010-02-24 15:50:26 +03:00
|
|
|
args.no_progress = !transport->progress;
|
2009-12-09 18:26:30 +03:00
|
|
|
args.depth = data->options.depth;
|
2016-06-12 13:53:59 +03:00
|
|
|
args.deepen_since = data->options.deepen_since;
|
2016-06-12 13:54:04 +03:00
|
|
|
args.deepen_not = data->options.deepen_not;
|
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 13:54:09 +03:00
|
|
|
args.deepen_relative = data->options.deepen_relative;
|
2013-05-26 05:16:17 +04:00
|
|
|
args.check_self_contained_and_connected =
|
|
|
|
data->options.check_self_contained_and_connected;
|
2013-12-05 17:02:39 +04:00
|
|
|
args.cloning = transport->cloning;
|
2013-12-05 17:02:42 +04:00
|
|
|
args.update_shallow = data->options.update_shallow;
|
introduce fetch-object: fetch one promisor object
Introduce fetch-object, providing the ability to fetch one object from a
promisor remote.
This uses fetch-pack. To do this, the transport mechanism has been
updated with 2 flags, "from-promisor" to indicate that the resulting
pack comes from a promisor remote (and thus should be annotated as such
by index-pack), and "no-dependents" to indicate that only the objects
themselves need to be fetched (but fetching additional objects is
nevertheless safe).
Whenever "no-dependents" is used, fetch-pack will refrain from using any
object flags, because it is most likely invoked as part of a dynamic
object fetch by another Git command (which may itself use object flags).
An alternative to this is to leave fetch-pack alone, and instead update
the allocation of flags so that fetch-pack's flags never overlap with
any others, but this will end up shrinking the number of flags available
to nearly every other Git command (that is, every Git command that
accesses objects), so the approach in this commit was used instead.
This will be tested in a subsequent commit.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-05 19:58:49 +03:00
|
|
|
args.from_promisor = data->options.from_promisor;
|
2017-12-08 18:58:40 +03:00
|
|
|
args.filter_options = data->options.filter_options;
|
2018-03-15 20:31:34 +03:00
|
|
|
args.stateless_rpc = transport->stateless_rpc;
|
2018-04-24 01:46:24 +03:00
|
|
|
args.server_options = transport->server_options;
|
2018-07-03 01:39:44 +03:00
|
|
|
args.negotiation_tips = data->options.negotiation_tips;
|
2007-09-11 07:03:11 +04:00
|
|
|
|
2018-09-27 22:24:05 +03:00
|
|
|
if (!data->got_remote_heads) {
|
|
|
|
int i;
|
|
|
|
int must_list_refs = 0;
|
|
|
|
for (i = 0; i < nr_heads; i++) {
|
|
|
|
if (!to_fetch[i]->exact_oid) {
|
|
|
|
must_list_refs = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
refs_tmp = handshake(transport, 0, NULL, must_list_refs);
|
|
|
|
}
|
2008-02-04 21:26:23 +03:00
|
|
|
|
2020-05-19 13:53:56 +03:00
|
|
|
if (data->version == protocol_unknown_version)
|
2018-03-14 21:31:46 +03:00
|
|
|
BUG("unknown protocol version");
|
2020-05-19 13:53:56 +03:00
|
|
|
else if (data->version <= protocol_v1)
|
|
|
|
die_if_server_options(transport);
|
|
|
|
|
|
|
|
refs = fetch_pack(&args, data->fd,
|
|
|
|
refs_tmp ? refs_tmp : transport->remote_refs,
|
|
|
|
to_fetch, nr_heads, &data->shallow,
|
2020-06-25 22:27:47 +03:00
|
|
|
&transport->pack_lockfiles, data->version);
|
2008-02-04 21:26:23 +03:00
|
|
|
|
|
|
|
close(data->fd[0]);
|
|
|
|
close(data->fd[1]);
|
2017-02-22 19:02:15 +03:00
|
|
|
if (finish_connect(data->conn))
|
|
|
|
ret = -1;
|
2008-02-04 21:26:23 +03:00
|
|
|
data->conn = NULL;
|
2009-12-09 18:26:31 +03:00
|
|
|
data->got_remote_heads = 0;
|
2013-05-26 05:16:17 +04:00
|
|
|
data->options.self_contained_and_connected =
|
|
|
|
args.self_contained_and_connected;
|
fetch-pack: write shallow, then check connectivity
When fetching, connectivity is checked after the shallow file is
updated. There are 2 issues with this: (1) the connectivity check is
only performed up to ancestors of existing refs (which is not thorough
enough if we were deepening an existing ref in the first place), and (2)
there is no rollback of the shallow file if the connectivity check
fails.
To solve (1), update the connectivity check to check the ancestry chain
completely in the case of a deepening fetch by refraining from passing
"--not --all" when invoking rev-list in connected.c.
To solve (2), have fetch_pack() perform its own connectivity check
before updating the shallow file. To support existing use cases in which
"git fetch-pack" is used to download objects without much regard as to
the connectivity of the resulting objects with respect to the existing
repository, the connectivity check is only done if necessary (that is,
the fetch is not a clone, and the fetch involves shallow/deepen
functionality). "git fetch" still performs its own connectivity check,
preserving correctness but sometimes performing redundant work. This
redundancy is mitigated by the fact that fetch_pack() reports if it has
performed a connectivity check itself, and if the transport supports
connect or stateless-connect, it will bubble up that report so that "git
fetch" knows not to perform the connectivity check in such a case.
This was noticed when a user tried to deepen an existing repository by
fetching with --no-shallow from a server that did not send all necessary
objects - the connectivity check as run by "git fetch" succeeded, but a
subsequent "git fsck" failed.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-07-03 01:08:43 +03:00
|
|
|
data->options.connectivity_checked = args.connectivity_checked;
|
2007-09-11 07:03:11 +04:00
|
|
|
|
2017-02-22 19:02:15 +03:00
|
|
|
if (refs == NULL)
|
|
|
|
ret = -1;
|
|
|
|
if (report_unmatched_refs(to_fetch, nr_heads))
|
|
|
|
ret = -1;
|
|
|
|
|
2008-02-28 19:10:51 +03:00
|
|
|
free_refs(refs_tmp);
|
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-01 23:13:20 +03:00
|
|
|
free_refs(refs);
|
2017-02-22 19:02:15 +03:00
|
|
|
return ret;
|
2007-09-11 07:03:11 +04:00
|
|
|
}
|
|
|
|
|
2009-08-06 00:23:26 +04:00
|
|
|
static int push_had_errors(struct ref *ref)
|
|
|
|
{
|
|
|
|
for (; ref; ref = ref->next) {
|
|
|
|
switch (ref->status) {
|
|
|
|
case REF_STATUS_NONE:
|
|
|
|
case REF_STATUS_UPTODATE:
|
|
|
|
case REF_STATUS_OK:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-17 02:42:52 +03:00
|
|
|
int transport_refs_pushed(struct ref *ref)
|
2009-03-09 04:06:07 +03:00
|
|
|
{
|
|
|
|
for (; ref; ref = ref->next) {
|
|
|
|
switch(ref->status) {
|
|
|
|
case REF_STATUS_NONE:
|
|
|
|
case REF_STATUS_UPTODATE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:45:50 +03:00
|
|
|
static void update_one_tracking_ref(struct remote *remote, char *refname,
|
|
|
|
struct object_id *new_oid, int deletion,
|
|
|
|
int verbose)
|
2009-03-09 04:06:07 +03:00
|
|
|
{
|
2018-05-17 01:57:49 +03:00
|
|
|
struct refspec_item rs;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2020-08-15 03:25:08 +03:00
|
|
|
memset(&rs, 0, sizeof(rs));
|
2020-08-27 18:45:50 +03:00
|
|
|
rs.src = refname;
|
2009-03-09 04:06:07 +03:00
|
|
|
rs.dst = NULL;
|
|
|
|
|
|
|
|
if (!remote_find_tracking(remote, &rs)) {
|
|
|
|
if (verbose)
|
|
|
|
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
|
2020-08-27 18:45:50 +03:00
|
|
|
if (deletion)
|
2017-02-21 04:10:32 +03:00
|
|
|
delete_ref(NULL, rs.dst, NULL, 0);
|
2020-08-27 18:45:50 +03:00
|
|
|
else
|
|
|
|
update_ref("update by push", rs.dst, new_oid,
|
2017-10-16 01:06:51 +03:00
|
|
|
NULL, 0, 0);
|
2009-03-09 04:06:07 +03:00
|
|
|
free(rs.dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:45:50 +03:00
|
|
|
void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
|
|
|
|
{
|
|
|
|
char *refname;
|
|
|
|
struct object_id *new_oid;
|
|
|
|
struct ref_push_report *report;
|
|
|
|
|
|
|
|
if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
report = ref->report;
|
|
|
|
if (!report)
|
|
|
|
update_one_tracking_ref(remote, ref->name, &ref->new_oid,
|
|
|
|
ref->deletion, verbose);
|
|
|
|
else
|
|
|
|
for (; report; report = report->next) {
|
|
|
|
refname = report->ref_name ? (char *)report->ref_name : ref->name;
|
|
|
|
new_oid = report->new_oid ? report->new_oid : &ref->new_oid;
|
|
|
|
update_one_tracking_ref(remote, refname, new_oid,
|
|
|
|
is_null_oid(new_oid), verbose);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-22 00:39:41 +03:00
|
|
|
static void print_ref_status(char flag, const char *summary,
|
|
|
|
struct ref *to, struct ref *from, const char *msg,
|
2020-08-27 18:45:46 +03:00
|
|
|
struct ref_push_report *report,
|
2016-10-22 00:39:41 +03:00
|
|
|
int porcelain, int summary_width)
|
2009-03-09 04:06:07 +03:00
|
|
|
{
|
2020-08-27 18:45:46 +03:00
|
|
|
const char *to_name;
|
|
|
|
|
|
|
|
if (report && report->ref_name)
|
|
|
|
to_name = report->ref_name;
|
|
|
|
else
|
|
|
|
to_name = to->name;
|
|
|
|
|
2009-06-23 05:10:01 +04:00
|
|
|
if (porcelain) {
|
|
|
|
if (from)
|
2020-08-27 18:45:46 +03:00
|
|
|
fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to_name);
|
2009-06-23 05:10:01 +04:00
|
|
|
else
|
2020-08-27 18:45:46 +03:00
|
|
|
fprintf(stdout, "%c\t:%s\t", flag, to_name);
|
2009-06-23 05:10:01 +04:00
|
|
|
if (msg)
|
|
|
|
fprintf(stdout, "%s (%s)\n", summary, msg);
|
|
|
|
else
|
|
|
|
fprintf(stdout, "%s\n", summary);
|
|
|
|
} else {
|
2018-04-21 13:10:00 +03:00
|
|
|
const char *red = "", *reset = "";
|
|
|
|
if (push_had_errors(to)) {
|
|
|
|
red = transport_get_color(TRANSPORT_COLOR_REJECTED);
|
|
|
|
reset = transport_get_color(TRANSPORT_COLOR_RESET);
|
|
|
|
}
|
|
|
|
fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width,
|
|
|
|
summary, reset);
|
2009-06-23 05:10:01 +04:00
|
|
|
if (from)
|
2020-08-27 18:45:46 +03:00
|
|
|
fprintf(stderr, "%s -> %s",
|
|
|
|
prettify_refname(from->name),
|
|
|
|
prettify_refname(to_name));
|
2009-06-23 05:10:01 +04:00
|
|
|
else
|
2020-08-27 18:45:46 +03:00
|
|
|
fputs(prettify_refname(to_name), stderr);
|
2009-06-23 05:10:01 +04:00
|
|
|
if (msg) {
|
|
|
|
fputs(" (", stderr);
|
|
|
|
fputs(msg, stderr);
|
|
|
|
fputc(')', stderr);
|
|
|
|
}
|
|
|
|
fputc('\n', stderr);
|
2009-03-09 04:06:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:45:46 +03:00
|
|
|
static void print_ok_ref_status(struct ref *ref,
|
|
|
|
struct ref_push_report *report,
|
|
|
|
int porcelain, int summary_width)
|
2009-03-09 04:06:07 +03:00
|
|
|
{
|
2020-08-27 18:45:46 +03:00
|
|
|
struct object_id *old_oid;
|
|
|
|
struct object_id *new_oid;
|
|
|
|
const char *ref_name;
|
|
|
|
int forced_update;
|
|
|
|
|
|
|
|
if (report && report->old_oid)
|
|
|
|
old_oid = report->old_oid;
|
|
|
|
else
|
|
|
|
old_oid = &ref->old_oid;
|
|
|
|
if (report && report->new_oid)
|
|
|
|
new_oid = report->new_oid;
|
|
|
|
else
|
|
|
|
new_oid = &ref->new_oid;
|
|
|
|
if (report && report->forced_update)
|
|
|
|
forced_update = report->forced_update;
|
|
|
|
else
|
|
|
|
forced_update = ref->forced_update;
|
|
|
|
if (report && report->ref_name)
|
|
|
|
ref_name = report->ref_name;
|
|
|
|
else
|
|
|
|
ref_name = ref->name;
|
|
|
|
|
2009-03-09 04:06:07 +03:00
|
|
|
if (ref->deletion)
|
2016-10-22 00:39:41 +03:00
|
|
|
print_ref_status('-', "[deleted]", ref, NULL, NULL,
|
2020-08-27 18:45:46 +03:00
|
|
|
report, porcelain, summary_width);
|
|
|
|
else if (is_null_oid(old_oid))
|
2009-03-09 04:06:07 +03:00
|
|
|
print_ref_status('*',
|
2020-08-27 18:45:46 +03:00
|
|
|
(starts_with(ref_name, "refs/tags/")
|
2020-08-27 18:45:42 +03:00
|
|
|
? "[new tag]"
|
2020-08-27 18:45:46 +03:00
|
|
|
: (starts_with(ref_name, "refs/heads/")
|
2020-08-27 18:45:42 +03:00
|
|
|
? "[new branch]"
|
|
|
|
: "[new reference]")),
|
2020-08-27 18:45:46 +03:00
|
|
|
ref, ref->peer_ref, NULL,
|
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
else {
|
2015-09-25 00:07:40 +03:00
|
|
|
struct strbuf quickref = STRBUF_INIT;
|
2009-03-09 04:06:07 +03:00
|
|
|
char type;
|
|
|
|
const char *msg;
|
|
|
|
|
2020-08-27 18:45:46 +03:00
|
|
|
strbuf_add_unique_abbrev(&quickref, old_oid,
|
2016-08-06 18:41:01 +03:00
|
|
|
DEFAULT_ABBREV);
|
2020-08-27 18:45:46 +03:00
|
|
|
if (forced_update) {
|
2015-09-25 00:07:40 +03:00
|
|
|
strbuf_addstr(&quickref, "...");
|
2009-03-09 04:06:07 +03:00
|
|
|
type = '+';
|
|
|
|
msg = "forced update";
|
|
|
|
} else {
|
2015-09-25 00:07:40 +03:00
|
|
|
strbuf_addstr(&quickref, "..");
|
2009-03-09 04:06:07 +03:00
|
|
|
type = ' ';
|
|
|
|
msg = NULL;
|
|
|
|
}
|
2020-08-27 18:45:46 +03:00
|
|
|
strbuf_add_unique_abbrev(&quickref, new_oid,
|
2016-08-06 18:41:01 +03:00
|
|
|
DEFAULT_ABBREV);
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2016-10-22 00:39:41 +03:00
|
|
|
print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg,
|
2020-08-27 18:45:46 +03:00
|
|
|
report, porcelain, summary_width);
|
2015-09-25 00:07:40 +03:00
|
|
|
strbuf_release(&quickref);
|
2009-03-09 04:06:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:45:46 +03:00
|
|
|
static int print_one_push_report(struct ref *ref, const char *dest, int count,
|
|
|
|
struct ref_push_report *report,
|
2016-10-22 00:39:41 +03:00
|
|
|
int porcelain, int summary_width)
|
2009-03-09 04:06:07 +03:00
|
|
|
{
|
2016-07-14 02:36:53 +03:00
|
|
|
if (!count) {
|
|
|
|
char *url = transport_anonymize_url(dest);
|
|
|
|
fprintf(porcelain ? stdout : stderr, "To %s\n", url);
|
|
|
|
free(url);
|
|
|
|
}
|
2009-03-09 04:06:07 +03:00
|
|
|
|
|
|
|
switch(ref->status) {
|
|
|
|
case REF_STATUS_NONE:
|
2016-10-22 00:39:41 +03:00
|
|
|
print_ref_status('X', "[no match]", ref, NULL, NULL,
|
2020-08-27 18:45:46 +03:00
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
|
|
|
case REF_STATUS_REJECT_NODELETE:
|
|
|
|
print_ref_status('!', "[rejected]", ref, NULL,
|
2016-10-22 00:39:41 +03:00
|
|
|
"remote does not support deleting refs",
|
2020-08-27 18:45:46 +03:00
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
|
|
|
case REF_STATUS_UPTODATE:
|
|
|
|
print_ref_status('=', "[up to date]", ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
ref->peer_ref, NULL,
|
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
|
|
|
case REF_STATUS_REJECT_NONFASTFORWARD:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
"non-fast-forward",
|
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
2012-11-30 05:41:37 +04:00
|
|
|
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
"already exists",
|
|
|
|
report, porcelain, summary_width);
|
2012-11-30 05:41:37 +04:00
|
|
|
break;
|
push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE
When we push to update an existing ref, if:
* the object at the tip of the remote is not a commit; or
* the object we are pushing is not a commit,
it won't be correct to suggest to fetch, integrate and push again,
as the old and new objects will not "merge". We should explain that
the push must be forced when there is a non-committish object is
involved in such a case.
If we do not have the current object at the tip of the remote, we do
not even know that object, when fetched, is something that can be
merged. In such a case, suggesting to pull first just like
non-fast-forward case may not be technically correct, but in
practice, most such failures are seen when you try to push your work
to a branch without knowing that somebody else already pushed to
update the same branch since you forked, so "pull first" would work
as a suggestion most of the time. And if the object at the tip is
not a commit, "pull first" will fail, without making any permanent
damage. As a side effect, it also makes the error message the user
will get during the next "push" attempt easier to understand, now
the user is aware that a non-commit object is involved.
In these cases, the current code already rejects such a push on the
client end, but we used the same error and advice messages as the
ones used when rejecting a non-fast-forward push, i.e. pull from
there and integrate before pushing again.
Introduce new rejection reasons and reword the messages
appropriately.
[jc: with help by Peff on message details]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-24 01:55:30 +04:00
|
|
|
case REF_STATUS_REJECT_FETCH_FIRST:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
"fetch first",
|
|
|
|
report, porcelain, summary_width);
|
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 01:55:30 +04:00
|
|
|
break;
|
|
|
|
case REF_STATUS_REJECT_NEEDS_FORCE:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
"needs force",
|
|
|
|
report, porcelain, summary_width);
|
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 01:55:30 +04:00
|
|
|
break;
|
2013-07-09 01:42:40 +04:00
|
|
|
case REF_STATUS_REJECT_STALE:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
"stale info",
|
|
|
|
report, porcelain, summary_width);
|
2013-07-09 01:42:40 +04:00
|
|
|
break;
|
2020-10-03 15:10:44 +03:00
|
|
|
case REF_STATUS_REJECT_REMOTE_UPDATED:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
|
|
|
"remote ref updated since checkout",
|
|
|
|
report, porcelain, summary_width);
|
|
|
|
break;
|
2013-12-05 17:02:40 +04:00
|
|
|
case REF_STATUS_REJECT_SHALLOW:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2016-10-22 00:39:41 +03:00
|
|
|
"new shallow roots not allowed",
|
2020-08-27 18:45:46 +03:00
|
|
|
report, porcelain, summary_width);
|
2013-12-05 17:02:40 +04:00
|
|
|
break;
|
2009-03-09 04:06:07 +03:00
|
|
|
case REF_STATUS_REMOTE_REJECT:
|
|
|
|
print_ref_status('!', "[remote rejected]", ref,
|
2016-10-22 00:39:41 +03:00
|
|
|
ref->deletion ? NULL : ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
ref->remote_status,
|
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
|
|
|
case REF_STATUS_EXPECTING_REPORT:
|
|
|
|
print_ref_status('!', "[remote failure]", ref,
|
2016-10-22 00:39:41 +03:00
|
|
|
ref->deletion ? NULL : ref->peer_ref,
|
|
|
|
"remote failed to report status",
|
2020-08-27 18:45:46 +03:00
|
|
|
report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
2015-01-08 06:23:22 +03:00
|
|
|
case REF_STATUS_ATOMIC_PUSH_FAILED:
|
|
|
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
2020-08-27 18:45:46 +03:00
|
|
|
"atomic push failed",
|
|
|
|
report, porcelain, summary_width);
|
2015-01-08 06:23:22 +03:00
|
|
|
break;
|
2009-03-09 04:06:07 +03:00
|
|
|
case REF_STATUS_OK:
|
2020-08-27 18:45:46 +03:00
|
|
|
print_ok_ref_status(ref, report, porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-08-27 18:45:46 +03:00
|
|
|
static int print_one_push_status(struct ref *ref, const char *dest, int count,
|
|
|
|
int porcelain, int summary_width)
|
|
|
|
{
|
|
|
|
struct ref_push_report *report;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
if (!ref->report)
|
|
|
|
return print_one_push_report(ref, dest, count,
|
|
|
|
NULL, porcelain, summary_width);
|
|
|
|
|
|
|
|
for (report = ref->report; report; report = report->next)
|
|
|
|
print_one_push_report(ref, dest, count + n++,
|
|
|
|
report, porcelain, summary_width);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2016-10-22 07:33:06 +03:00
|
|
|
static int measure_abbrev(const struct object_id *oid, int sofar)
|
|
|
|
{
|
2017-03-26 19:01:24 +03:00
|
|
|
char hex[GIT_MAX_HEXSZ + 1];
|
2018-03-12 05:27:30 +03:00
|
|
|
int w = find_unique_abbrev_r(hex, oid, DEFAULT_ABBREV);
|
2016-10-22 07:33:06 +03:00
|
|
|
|
|
|
|
return (w < sofar) ? sofar : w;
|
|
|
|
}
|
|
|
|
|
2016-10-22 01:28:07 +03:00
|
|
|
int transport_summary_width(const struct ref *refs)
|
|
|
|
{
|
2016-10-22 07:33:06 +03:00
|
|
|
int maxw = -1;
|
|
|
|
|
|
|
|
for (; refs; refs = refs->next) {
|
|
|
|
maxw = measure_abbrev(&refs->old_oid, maxw);
|
|
|
|
maxw = measure_abbrev(&refs->new_oid, maxw);
|
|
|
|
}
|
|
|
|
if (maxw < 0)
|
|
|
|
maxw = FALLBACK_DEFAULT_ABBREV;
|
|
|
|
return (2 * maxw + 3);
|
2016-10-22 01:28:07 +03:00
|
|
|
}
|
|
|
|
|
2010-02-17 02:42:52 +03:00
|
|
|
void transport_print_push_status(const char *dest, struct ref *refs,
|
2012-11-30 05:41:33 +04:00
|
|
|
int verbose, int porcelain, unsigned int *reject_reasons)
|
2009-03-09 04:06:07 +03:00
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
int n = 0;
|
push: Provide situational hints for non-fast-forward errors
Pushing a non-fast-forward update to a remote repository will result in
an error, but the hint text doesn't provide the correct resolution in
every case. Give better resolution advice in three push scenarios:
1) If you push your current branch and it triggers a non-fast-forward
error, you should merge remote changes with 'git pull' before pushing
again.
2) If you push to a shared repository others push to, and your local
tracking branches are not kept up to date, the 'matching refs' default
will generate non-fast-forward errors on outdated branches. If this is
your workflow, the 'matching refs' default is not for you. Consider
setting the 'push.default' configuration variable to 'current' or
'upstream' to ensure only your current branch is pushed.
3) If you explicitly specify a ref that is not your current branch or
push matching branches with ':', you will generate a non-fast-forward
error if any pushed branch tip is out of date. You should checkout the
offending branch and merge remote changes before pushing again.
Teach transport.c to recognize these scenarios and configure push.c
to hint for them. If 'git push's default behavior changes or we
discover more scenarios, extension is easy. Standardize on the
advice API and add three new advice variables, 'pushNonFFCurrent',
'pushNonFFDefault', and 'pushNonFFMatching'. Setting any of these
to 'false' will disable their affiliated advice. Setting
'pushNonFastForward' to false will disable all three, thus preserving the
config option for users who already set it, but guaranteeing new
users won't disable push advice accidentally.
Based-on-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Christopher Tiwald <christiwald@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-03-20 08:31:33 +04:00
|
|
|
char *head;
|
2016-10-22 01:28:07 +03:00
|
|
|
int summary_width = transport_summary_width(refs);
|
push: Provide situational hints for non-fast-forward errors
Pushing a non-fast-forward update to a remote repository will result in
an error, but the hint text doesn't provide the correct resolution in
every case. Give better resolution advice in three push scenarios:
1) If you push your current branch and it triggers a non-fast-forward
error, you should merge remote changes with 'git pull' before pushing
again.
2) If you push to a shared repository others push to, and your local
tracking branches are not kept up to date, the 'matching refs' default
will generate non-fast-forward errors on outdated branches. If this is
your workflow, the 'matching refs' default is not for you. Consider
setting the 'push.default' configuration variable to 'current' or
'upstream' to ensure only your current branch is pushed.
3) If you explicitly specify a ref that is not your current branch or
push matching branches with ':', you will generate a non-fast-forward
error if any pushed branch tip is out of date. You should checkout the
offending branch and merge remote changes before pushing again.
Teach transport.c to recognize these scenarios and configure push.c
to hint for them. If 'git push's default behavior changes or we
discover more scenarios, extension is easy. Standardize on the
advice API and add three new advice variables, 'pushNonFFCurrent',
'pushNonFFDefault', and 'pushNonFFMatching'. Setting any of these
to 'false' will disable their affiliated advice. Setting
'pushNonFastForward' to false will disable all three, thus preserving the
config option for users who already set it, but guaranteeing new
users won't disable push advice accidentally.
Based-on-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Christopher Tiwald <christiwald@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-03-20 08:31:33 +04:00
|
|
|
|
2018-04-21 13:10:00 +03:00
|
|
|
if (transport_color_config() < 0)
|
|
|
|
warning(_("could not parse transport.color.* config"));
|
|
|
|
|
2017-10-01 10:29:03 +03:00
|
|
|
head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
|
2009-03-09 04:06:07 +03:00
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
for (ref = refs; ref; ref = ref->next)
|
|
|
|
if (ref->status == REF_STATUS_UPTODATE)
|
2016-10-22 00:39:41 +03:00
|
|
|
n += print_one_push_status(ref, dest, n,
|
|
|
|
porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (ref = refs; ref; ref = ref->next)
|
|
|
|
if (ref->status == REF_STATUS_OK)
|
2016-10-22 00:39:41 +03:00
|
|
|
n += print_one_push_status(ref, dest, n,
|
|
|
|
porcelain, summary_width);
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2012-11-30 05:41:33 +04:00
|
|
|
*reject_reasons = 0;
|
2009-03-09 04:06:07 +03:00
|
|
|
for (ref = refs; ref; ref = ref->next) {
|
|
|
|
if (ref->status != REF_STATUS_NONE &&
|
|
|
|
ref->status != REF_STATUS_UPTODATE &&
|
|
|
|
ref->status != REF_STATUS_OK)
|
2016-10-22 00:39:41 +03:00
|
|
|
n += print_one_push_status(ref, dest, n,
|
|
|
|
porcelain, summary_width);
|
2012-11-30 05:41:33 +04:00
|
|
|
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
|
2013-01-31 16:22:51 +04:00
|
|
|
if (head != NULL && !strcmp(head, ref->name))
|
2012-11-30 05:41:33 +04:00
|
|
|
*reject_reasons |= REJECT_NON_FF_HEAD;
|
push: Provide situational hints for non-fast-forward errors
Pushing a non-fast-forward update to a remote repository will result in
an error, but the hint text doesn't provide the correct resolution in
every case. Give better resolution advice in three push scenarios:
1) If you push your current branch and it triggers a non-fast-forward
error, you should merge remote changes with 'git pull' before pushing
again.
2) If you push to a shared repository others push to, and your local
tracking branches are not kept up to date, the 'matching refs' default
will generate non-fast-forward errors on outdated branches. If this is
your workflow, the 'matching refs' default is not for you. Consider
setting the 'push.default' configuration variable to 'current' or
'upstream' to ensure only your current branch is pushed.
3) If you explicitly specify a ref that is not your current branch or
push matching branches with ':', you will generate a non-fast-forward
error if any pushed branch tip is out of date. You should checkout the
offending branch and merge remote changes before pushing again.
Teach transport.c to recognize these scenarios and configure push.c
to hint for them. If 'git push's default behavior changes or we
discover more scenarios, extension is easy. Standardize on the
advice API and add three new advice variables, 'pushNonFFCurrent',
'pushNonFFDefault', and 'pushNonFFMatching'. Setting any of these
to 'false' will disable their affiliated advice. Setting
'pushNonFastForward' to false will disable all three, thus preserving the
config option for users who already set it, but guaranteeing new
users won't disable push advice accidentally.
Based-on-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Christopher Tiwald <christiwald@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-03-20 08:31:33 +04:00
|
|
|
else
|
2012-11-30 05:41:33 +04:00
|
|
|
*reject_reasons |= REJECT_NON_FF_OTHER;
|
2012-11-30 05:41:37 +04:00
|
|
|
} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
|
|
|
|
*reject_reasons |= REJECT_ALREADY_EXISTS;
|
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 01:55:30 +04:00
|
|
|
} else if (ref->status == REF_STATUS_REJECT_FETCH_FIRST) {
|
|
|
|
*reject_reasons |= REJECT_FETCH_FIRST;
|
|
|
|
} else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) {
|
|
|
|
*reject_reasons |= REJECT_NEEDS_FORCE;
|
2020-10-03 15:10:45 +03:00
|
|
|
} else if (ref->status == REF_STATUS_REJECT_REMOTE_UPDATED) {
|
|
|
|
*reject_reasons |= REJECT_REF_NEEDS_UPDATE;
|
push: Provide situational hints for non-fast-forward errors
Pushing a non-fast-forward update to a remote repository will result in
an error, but the hint text doesn't provide the correct resolution in
every case. Give better resolution advice in three push scenarios:
1) If you push your current branch and it triggers a non-fast-forward
error, you should merge remote changes with 'git pull' before pushing
again.
2) If you push to a shared repository others push to, and your local
tracking branches are not kept up to date, the 'matching refs' default
will generate non-fast-forward errors on outdated branches. If this is
your workflow, the 'matching refs' default is not for you. Consider
setting the 'push.default' configuration variable to 'current' or
'upstream' to ensure only your current branch is pushed.
3) If you explicitly specify a ref that is not your current branch or
push matching branches with ':', you will generate a non-fast-forward
error if any pushed branch tip is out of date. You should checkout the
offending branch and merge remote changes before pushing again.
Teach transport.c to recognize these scenarios and configure push.c
to hint for them. If 'git push's default behavior changes or we
discover more scenarios, extension is easy. Standardize on the
advice API and add three new advice variables, 'pushNonFFCurrent',
'pushNonFFDefault', and 'pushNonFFMatching'. Setting any of these
to 'false' will disable their affiliated advice. Setting
'pushNonFastForward' to false will disable all three, thus preserving the
config option for users who already set it, but guaranteeing new
users won't disable push advice accidentally.
Based-on-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Christopher Tiwald <christiwald@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-03-20 08:31:33 +04:00
|
|
|
}
|
2009-03-09 04:06:07 +03:00
|
|
|
}
|
2014-10-21 05:50:44 +04:00
|
|
|
free(head);
|
2009-03-09 04:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)
|
2007-11-09 02:35:32 +03:00
|
|
|
{
|
2007-09-11 07:03:04 +04:00
|
|
|
struct git_transport_data *data = transport->data;
|
2007-10-30 05:03:42 +03:00
|
|
|
struct send_pack_args args;
|
2018-03-14 21:31:46 +03:00
|
|
|
int ret = 0;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2018-04-21 13:10:00 +03:00
|
|
|
if (transport_color_config() < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-03-14 21:31:43 +03:00
|
|
|
if (!data->got_remote_heads)
|
2018-03-15 20:31:22 +03:00
|
|
|
get_refs_via_connect(transport, 1, NULL);
|
2007-09-11 07:03:04 +04:00
|
|
|
|
2009-10-31 03:47:41 +03:00
|
|
|
memset(&args, 0, sizeof(args));
|
2007-11-10 02:32:25 +03:00
|
|
|
args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
|
2007-10-30 05:03:42 +03:00
|
|
|
args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
|
2009-12-09 18:26:30 +03:00
|
|
|
args.use_thin_pack = data->options.thin;
|
2010-02-24 15:50:24 +03:00
|
|
|
args.verbose = (transport->verbose > 0);
|
|
|
|
args.quiet = (transport->verbose < 0);
|
2010-10-16 22:37:03 +04:00
|
|
|
args.progress = transport->progress;
|
2007-10-30 05:03:42 +03:00
|
|
|
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
|
2010-02-27 07:52:15 +03:00
|
|
|
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
|
2015-01-08 06:23:23 +03:00
|
|
|
args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
|
2016-07-15 00:49:47 +03:00
|
|
|
args.push_options = transport->push_options;
|
2014-08-23 05:15:24 +04:00
|
|
|
args.url = transport->url;
|
2007-10-30 05:03:42 +03:00
|
|
|
|
2015-08-19 18:26:46 +03:00
|
|
|
if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
|
|
|
|
args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
|
|
|
|
else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED)
|
|
|
|
args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
|
|
|
|
else
|
|
|
|
args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
|
|
|
|
|
2018-03-14 21:31:46 +03:00
|
|
|
switch (data->version) {
|
2018-03-14 21:31:47 +03:00
|
|
|
case protocol_v2:
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("support for protocol v2 not implemented yet"));
|
2018-03-14 21:31:47 +03:00
|
|
|
break;
|
2018-03-14 21:31:46 +03:00
|
|
|
case protocol_v1:
|
|
|
|
case protocol_v0:
|
|
|
|
ret = send_pack(&args, data->fd, data->conn, remote_refs,
|
|
|
|
&data->extra_have);
|
|
|
|
break;
|
|
|
|
case protocol_unknown_version:
|
|
|
|
BUG("unknown protocol version");
|
|
|
|
}
|
2009-03-09 04:06:07 +03:00
|
|
|
|
|
|
|
close(data->fd[1]);
|
|
|
|
close(data->fd[0]);
|
2020-04-17 12:45:32 +03:00
|
|
|
/*
|
|
|
|
* Atomic push may abort the connection early and close the pipe,
|
|
|
|
* which may cause an error for `finish_connect()`. Ignore this error
|
|
|
|
* for atomic git-push.
|
|
|
|
*/
|
|
|
|
if (ret || args.atomic)
|
|
|
|
finish_connect(data->conn);
|
|
|
|
else
|
|
|
|
ret = finish_connect(data->conn);
|
2009-03-09 04:06:07 +03:00
|
|
|
data->conn = NULL;
|
2009-12-09 18:26:31 +03:00
|
|
|
data->got_remote_heads = 0;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
|
|
|
return ret;
|
2007-09-11 07:03:04 +04:00
|
|
|
}
|
|
|
|
|
2009-12-09 18:26:33 +03:00
|
|
|
static int connect_git(struct transport *transport, const char *name,
|
|
|
|
const char *executable, int fd[2])
|
|
|
|
{
|
|
|
|
struct git_transport_data *data = transport->data;
|
|
|
|
data->conn = git_connect(data->fd, transport->url,
|
|
|
|
executable, 0);
|
|
|
|
fd[0] = data->fd[0];
|
|
|
|
fd[1] = data->fd[1];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-19 08:49:42 +04:00
|
|
|
static int disconnect_git(struct transport *transport)
|
|
|
|
{
|
2008-02-04 21:26:23 +03:00
|
|
|
struct git_transport_data *data = transport->data;
|
|
|
|
if (data->conn) {
|
transport: don't flush when disconnecting stateless-rpc helper
Since ba227857d2 (Reduce the number of connects when fetching,
2008-02-04), when we disconnect a git transport, we send a final flush
packet. This cleanly tells the other side that we're done, and avoids
the other side complaining "the remote end hung up unexpectedly" (though
we'd only see that for transports that pass along the server stderr,
like ssh or local-host).
But when we've initiated a v2 stateless-connect session over a transport
helper, there's no point in sending this flush packet. Each operation
we've performed is self-contained, and the other side is fine with us
hanging up between operations.
But much worse, by sending the flush packet we may cause the helper to
issue an entirely new request _just_ to send the flush packet. So we can
incur an extra network request just to say "by the way, we have nothing
more to send".
Let's drop this extra flush packet. As the test shows, this reduces the
number of POSTs required for a v2 ls-remote over http from 2 to 1.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-01-08 10:10:09 +03:00
|
|
|
if (data->got_remote_heads && !transport->stateless_rpc)
|
2009-12-09 18:26:31 +03:00
|
|
|
packet_flush(data->fd[1]);
|
2008-02-04 21:26:23 +03:00
|
|
|
close(data->fd[0]);
|
|
|
|
close(data->fd[1]);
|
|
|
|
finish_connect(data->conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(data);
|
2007-09-19 08:49:42 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-15 00:44:45 +03:00
|
|
|
static struct transport_vtable taken_over_vtable = {
|
|
|
|
NULL,
|
|
|
|
get_refs_via_connect,
|
|
|
|
fetch_refs_via_pack,
|
|
|
|
git_transport_push,
|
|
|
|
NULL,
|
|
|
|
disconnect_git
|
|
|
|
};
|
|
|
|
|
2009-12-09 18:26:31 +03:00
|
|
|
void transport_take_over(struct transport *transport,
|
|
|
|
struct child_process *child)
|
|
|
|
{
|
|
|
|
struct git_transport_data *data;
|
|
|
|
|
|
|
|
if (!transport->smart_options)
|
2018-05-02 12:38:39 +03:00
|
|
|
BUG("taking over transport requires non-NULL "
|
2009-12-09 18:26:31 +03:00
|
|
|
"smart_options field.");
|
|
|
|
|
|
|
|
data = xcalloc(1, sizeof(*data));
|
|
|
|
data->options = *transport->smart_options;
|
|
|
|
data->conn = child;
|
|
|
|
data->fd[0] = data->conn->out;
|
|
|
|
data->fd[1] = data->conn->in;
|
|
|
|
data->got_remote_heads = 0;
|
|
|
|
transport->data = data;
|
|
|
|
|
2017-12-15 00:44:45 +03:00
|
|
|
transport->vtable = &taken_over_vtable;
|
2009-12-09 18:26:31 +03:00
|
|
|
transport->smart_options = &(data->options);
|
fetch: work around "transport-take-over" hack
A Git-aware "connect" transport allows the "transport_take_over" to
redirect generic transport requests like fetch(), push_refs() and
get_refs_list() to the native Git transport handling methods. The
take-over process replaces transport->data with a fake data that
these method implementations understand.
While this hack works OK for a single request, it breaks when the
transport needs to make more than one requests. transport->data
that used to hold necessary information for the specific helper to
work correctly is destroyed during the take-over process.
One codepath that this matters is "git fetch" in auto-follow mode;
when it does not get all the tags that ought to point at the history
it got (which can be determined by looking at the peeled tags in the
initial advertisement) from the primary transfer, it internally
makes a second request to complete the fetch. Because "take-over"
hack has already destroyed the data necessary to talk to the
transport helper by the time this happens, the second request cannot
make a request to the helper to make another connection to fetch
these additional tags.
Mark such a transport as "cannot_reuse", and use a separate
transport to perform the backfill fetch in order to work around
this breakage.
Note that this problem does not manifest itself when running t5802,
because our upload-pack gives you all the necessary auto-followed
tags during the primary transfer. You would need to step through
"git fetch" in a debugger, stop immediately after the primary
transfer finishes and writes these auto-followed tags, remove the
tag references and repack/prune the repository to convince the
"find-non-local-tags" procedure that the primary transfer failed to
give us all the necessary tags, and then let it continue, in order
to trigger the bug in the secondary transfer this patch fixes.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-08 02:47:18 +04:00
|
|
|
|
|
|
|
transport->cannot_reuse = 1;
|
2009-12-09 18:26:31 +03:00
|
|
|
}
|
|
|
|
|
2007-09-11 07:03:04 +04:00
|
|
|
static int is_file(const char *url)
|
|
|
|
{
|
|
|
|
struct stat buf;
|
|
|
|
if (stat(url, &buf))
|
|
|
|
return 0;
|
|
|
|
return S_ISREG(buf.st_mode);
|
|
|
|
}
|
|
|
|
|
2009-12-09 18:26:29 +03:00
|
|
|
static int external_specification_len(const char *url)
|
|
|
|
{
|
|
|
|
return strchr(url, ':') - url;
|
|
|
|
}
|
|
|
|
|
2015-09-23 01:03:49 +03:00
|
|
|
static const struct string_list *protocol_whitelist(void)
|
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-16 20:12:52 +03:00
|
|
|
{
|
2015-09-23 01:03:49 +03:00
|
|
|
static int enabled = -1;
|
|
|
|
static struct string_list allowed = STRING_LIST_INIT_DUP;
|
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-16 20:12:52 +03:00
|
|
|
|
2015-09-23 01:03:49 +03:00
|
|
|
if (enabled < 0) {
|
|
|
|
const char *v = getenv("GIT_ALLOW_PROTOCOL");
|
|
|
|
if (v) {
|
|
|
|
string_list_split(&allowed, v, ':', -1);
|
|
|
|
string_list_sort(&allowed);
|
|
|
|
enabled = 1;
|
|
|
|
} else {
|
|
|
|
enabled = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return enabled ? &allowed : NULL;
|
|
|
|
}
|
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-16 20:12:52 +03:00
|
|
|
|
2016-12-15 01:39:52 +03:00
|
|
|
enum protocol_allow_config {
|
|
|
|
PROTOCOL_ALLOW_NEVER = 0,
|
|
|
|
PROTOCOL_ALLOW_USER_ONLY,
|
|
|
|
PROTOCOL_ALLOW_ALWAYS
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum protocol_allow_config parse_protocol_config(const char *key,
|
|
|
|
const char *value)
|
2015-09-23 01:03:49 +03:00
|
|
|
{
|
2016-12-15 01:39:52 +03:00
|
|
|
if (!strcasecmp(value, "always"))
|
|
|
|
return PROTOCOL_ALLOW_ALWAYS;
|
|
|
|
else if (!strcasecmp(value, "never"))
|
|
|
|
return PROTOCOL_ALLOW_NEVER;
|
|
|
|
else if (!strcasecmp(value, "user"))
|
|
|
|
return PROTOCOL_ALLOW_USER_ONLY;
|
|
|
|
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("unknown value for config '%s': %s"), key, value);
|
2015-09-23 01:03:49 +03:00
|
|
|
}
|
|
|
|
|
2016-12-15 01:39:52 +03:00
|
|
|
static enum protocol_allow_config get_protocol_config(const char *type)
|
2015-09-23 01:03:49 +03:00
|
|
|
{
|
2016-12-15 01:39:52 +03:00
|
|
|
char *key = xstrfmt("protocol.%s.allow", type);
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
/* first check the per-protocol config */
|
|
|
|
if (!git_config_get_string(key, &value)) {
|
|
|
|
enum protocol_allow_config ret =
|
|
|
|
parse_protocol_config(key, value);
|
|
|
|
free(key);
|
|
|
|
free(value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
free(key);
|
|
|
|
|
|
|
|
/* if defined, fallback to user-defined default for unknown protocols */
|
|
|
|
if (!git_config_get_string("protocol.allow", &value)) {
|
|
|
|
enum protocol_allow_config ret =
|
|
|
|
parse_protocol_config("protocol.allow", value);
|
|
|
|
free(value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fallback to built-in defaults */
|
|
|
|
/* known safe */
|
|
|
|
if (!strcmp(type, "http") ||
|
|
|
|
!strcmp(type, "https") ||
|
|
|
|
!strcmp(type, "git") ||
|
|
|
|
!strcmp(type, "ssh") ||
|
|
|
|
!strcmp(type, "file"))
|
|
|
|
return PROTOCOL_ALLOW_ALWAYS;
|
|
|
|
|
|
|
|
/* known scary; err on the side of caution */
|
|
|
|
if (!strcmp(type, "ext"))
|
|
|
|
return PROTOCOL_ALLOW_NEVER;
|
|
|
|
|
|
|
|
/* unknown; by default let them be used only directly by the user */
|
|
|
|
return PROTOCOL_ALLOW_USER_ONLY;
|
2015-09-23 01:03:49 +03:00
|
|
|
}
|
|
|
|
|
2016-12-15 01:39:54 +03:00
|
|
|
int is_transport_allowed(const char *type, int from_user)
|
2015-09-23 01:03:49 +03:00
|
|
|
{
|
2016-12-15 01:39:52 +03:00
|
|
|
const struct string_list *whitelist = protocol_whitelist();
|
|
|
|
if (whitelist)
|
|
|
|
return string_list_has_string(whitelist, type);
|
|
|
|
|
|
|
|
switch (get_protocol_config(type)) {
|
|
|
|
case PROTOCOL_ALLOW_ALWAYS:
|
|
|
|
return 1;
|
|
|
|
case PROTOCOL_ALLOW_NEVER:
|
|
|
|
return 0;
|
|
|
|
case PROTOCOL_ALLOW_USER_ONLY:
|
2016-12-15 01:39:54 +03:00
|
|
|
if (from_user < 0)
|
|
|
|
from_user = git_env_bool("GIT_PROTOCOL_FROM_USER", 1);
|
|
|
|
return from_user;
|
2016-12-15 01:39:52 +03:00
|
|
|
}
|
|
|
|
|
2018-05-02 12:38:39 +03:00
|
|
|
BUG("invalid protocol_allow_config type");
|
2015-09-23 01:03:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void transport_check_allowed(const char *type)
|
|
|
|
{
|
2016-12-15 01:39:54 +03:00
|
|
|
if (!is_transport_allowed(type, -1))
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("transport '%s' not allowed"), type);
|
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-16 20:12:52 +03:00
|
|
|
}
|
|
|
|
|
2017-12-15 00:44:45 +03:00
|
|
|
static struct transport_vtable bundle_vtable = {
|
|
|
|
NULL,
|
|
|
|
get_refs_from_bundle,
|
|
|
|
fetch_refs_from_bundle,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
close_bundle
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct transport_vtable builtin_smart_vtable = {
|
|
|
|
NULL,
|
|
|
|
get_refs_via_connect,
|
|
|
|
fetch_refs_via_pack,
|
|
|
|
git_transport_push,
|
|
|
|
connect_git,
|
|
|
|
disconnect_git
|
|
|
|
};
|
|
|
|
|
2007-09-15 11:23:14 +04:00
|
|
|
struct transport *transport_get(struct remote *remote, const char *url)
|
2007-09-11 07:03:04 +04:00
|
|
|
{
|
2010-01-27 20:53:17 +03:00
|
|
|
const char *helper;
|
2007-09-15 11:23:07 +04:00
|
|
|
struct transport *ret = xcalloc(1, sizeof(*ret));
|
|
|
|
|
2010-02-24 15:50:26 +03:00
|
|
|
ret->progress = isatty(2);
|
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-10 23:57:22 +03:00
|
|
|
string_list_init(&ret->pack_lockfiles, 1);
|
2010-02-24 15:50:26 +03:00
|
|
|
|
2009-11-04 05:38:51 +03:00
|
|
|
if (!remote)
|
2018-07-21 10:49:19 +03:00
|
|
|
BUG("No remote provided to transport_get()");
|
2009-11-04 05:38:51 +03:00
|
|
|
|
2010-02-16 10:18:21 +03:00
|
|
|
ret->got_remote_refs = 0;
|
2007-09-15 11:23:07 +04:00
|
|
|
ret->remote = remote;
|
2010-01-27 20:53:17 +03:00
|
|
|
helper = remote->foreign_vcs;
|
2009-11-18 04:42:22 +03:00
|
|
|
|
2010-01-27 23:22:37 +03:00
|
|
|
if (!url && remote->url)
|
2009-11-18 04:42:22 +03:00
|
|
|
url = remote->url[0];
|
2007-09-15 11:23:07 +04:00
|
|
|
ret->url = url;
|
|
|
|
|
2009-11-18 04:42:26 +03:00
|
|
|
/* maybe it is a foreign URL? */
|
|
|
|
if (url) {
|
|
|
|
const char *p = url;
|
|
|
|
|
2010-05-23 13:17:55 +04:00
|
|
|
while (is_urlschemechar(p == url, *p))
|
2009-11-18 04:42:26 +03:00
|
|
|
p++;
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(p, "::"))
|
2010-01-27 20:53:17 +03:00
|
|
|
helper = xstrndup(url, p - url);
|
2009-11-18 04:42:26 +03:00
|
|
|
}
|
|
|
|
|
2010-01-27 20:53:17 +03:00
|
|
|
if (helper) {
|
|
|
|
transport_helper_init(ret, helper);
|
2013-12-01 00:55:40 +04:00
|
|
|
} else if (starts_with(url, "rsync:")) {
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("git-over-rsync is no longer supported"));
|
2013-11-28 23:50:03 +04:00
|
|
|
} else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
|
2007-09-11 07:03:21 +04:00
|
|
|
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
|
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-16 20:12:52 +03:00
|
|
|
transport_check_allowed("file");
|
2007-09-11 07:03:21 +04:00
|
|
|
ret->data = data;
|
2017-12-15 00:44:45 +03:00
|
|
|
ret->vtable = &bundle_vtable;
|
2009-12-09 18:26:30 +03:00
|
|
|
ret->smart_options = NULL;
|
2009-12-09 18:26:29 +03:00
|
|
|
} else if (!is_url(url)
|
2013-12-01 00:55:40 +04:00
|
|
|
|| starts_with(url, "file://")
|
|
|
|
|| starts_with(url, "git://")
|
|
|
|
|| starts_with(url, "ssh://")
|
2016-02-15 17:29:06 +03:00
|
|
|
|| starts_with(url, "git+ssh://") /* deprecated - do not use */
|
|
|
|
|| starts_with(url, "ssh+git://") /* deprecated - do not use */
|
|
|
|
) {
|
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-16 20:12:52 +03:00
|
|
|
/*
|
|
|
|
* These are builtin smart transports; "allowed" transports
|
|
|
|
* will be checked individually in git_connect.
|
|
|
|
*/
|
2007-09-11 07:03:04 +04:00
|
|
|
struct git_transport_data *data = xcalloc(1, sizeof(*data));
|
|
|
|
ret->data = data;
|
2017-12-15 00:44:45 +03:00
|
|
|
ret->vtable = &builtin_smart_vtable;
|
2009-12-09 18:26:30 +03:00
|
|
|
ret->smart_options = &(data->options);
|
2007-09-19 08:49:31 +04:00
|
|
|
|
2008-02-04 21:26:23 +03:00
|
|
|
data->conn = NULL;
|
2009-12-09 18:26:31 +03:00
|
|
|
data->got_remote_heads = 0;
|
2009-12-09 18:26:29 +03:00
|
|
|
} else {
|
|
|
|
/* Unknown protocol in URL. Pass to external handler. */
|
|
|
|
int len = external_specification_len(url);
|
2014-12-24 03:18:31 +03:00
|
|
|
char *handler = xmemdupz(url, len);
|
2009-12-09 18:26:29 +03:00
|
|
|
transport_helper_init(ret, handler);
|
2007-09-11 07:03:04 +04:00
|
|
|
}
|
2007-09-15 11:23:07 +04:00
|
|
|
|
2009-12-09 18:26:30 +03:00
|
|
|
if (ret->smart_options) {
|
|
|
|
ret->smart_options->thin = 1;
|
|
|
|
ret->smart_options->uploadpack = "git-upload-pack";
|
|
|
|
if (remote->uploadpack)
|
|
|
|
ret->smart_options->uploadpack = remote->uploadpack;
|
|
|
|
ret->smart_options->receivepack = "git-receive-pack";
|
|
|
|
if (remote->receivepack)
|
|
|
|
ret->smart_options->receivepack = remote->receivepack;
|
|
|
|
}
|
|
|
|
|
2020-05-25 22:58:55 +03:00
|
|
|
ret->hash_algo = &hash_algos[GIT_HASH_SHA1];
|
|
|
|
|
2007-09-11 07:03:04 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-25 22:58:55 +03:00
|
|
|
const struct git_hash_algo *transport_get_hash_algo(struct transport *transport)
|
|
|
|
{
|
|
|
|
return transport->hash_algo;
|
|
|
|
}
|
|
|
|
|
2007-09-11 07:03:04 +04:00
|
|
|
int transport_set_option(struct transport *transport,
|
|
|
|
const char *name, const char *value)
|
|
|
|
{
|
2009-12-09 18:26:30 +03:00
|
|
|
int git_reports = 1, protocol_reports = 1;
|
|
|
|
|
|
|
|
if (transport->smart_options)
|
|
|
|
git_reports = set_git_option(transport->smart_options,
|
|
|
|
name, value);
|
|
|
|
|
2017-12-15 00:44:45 +03:00
|
|
|
if (transport->vtable->set_option)
|
|
|
|
protocol_reports = transport->vtable->set_option(transport,
|
|
|
|
name, value);
|
2009-12-09 18:26:30 +03:00
|
|
|
|
|
|
|
/* If either report is 0, report 0 (success). */
|
|
|
|
if (!git_reports || !protocol_reports)
|
|
|
|
return 0;
|
|
|
|
/* If either reports -1 (invalid value), report -1. */
|
|
|
|
if ((git_reports == -1) || (protocol_reports == -1))
|
|
|
|
return -1;
|
|
|
|
/* Otherwise if both report unknown, report unknown. */
|
2007-09-18 12:54:57 +04:00
|
|
|
return 1;
|
2007-09-11 07:03:04 +04:00
|
|
|
}
|
|
|
|
|
2010-02-24 15:50:26 +03:00
|
|
|
void transport_set_verbosity(struct transport *transport, int verbosity,
|
|
|
|
int force_progress)
|
2010-02-24 15:50:23 +03:00
|
|
|
{
|
make "git push -v" actually verbose
Providing a single "-v" to "git push" currently does
nothing. Giving two flags ("git push -v -v") turns on the
first level of verbosity.
This is caused by a regression introduced in 8afd8dc (push:
support multiple levels of verbosity, 2010-02-24). Before
the series containing 8afd8dc, the verbosity handling for
fetching and pushing was completely separate. Commit bde873c
refactored the verbosity handling out of the fetch side, and
then 8afd8dc converted push to use the refactored code.
However, the fetch and push sides numbered and passed along
their verbosity levels differently. For both, a verbosity
level of "-1" meant "quiet", and "0" meant "default output".
But from there they differed.
For fetch, a verbosity level of "1" indicated to the "fetch"
program that it should make the status table slightly more
verbose, showing up-to-date entries. A verbosity level of
"2" meant that we should pass a verbose flag to the
transport; in the case of fetch-pack, this displays protocol
debugging information.
As a result, the refactored code in bde873c checks for
"verbosity >= 2", and only then passes it on to the
transport. From the transport code's perspective, a
verbosity of 0 or 1 both meant "0".
Push, on the other hand, does not show its own status table;
that is always handled by the transport layer or below
(originally send-pack itself, but these days it is done by
the transport code). So a verbosity level of 1 meant that we
should pass the verbose flag to send-pack, so that it knows
we want a verbose status table. However, once 8afd8dc
switched it to the refactored fetch code, a verbosity level
of 1 was now being ignored. Thus, you needed to
artificially bump the verbosity to 2 (via "-v -v") to have
any effect.
We can fix this by letting the transport code know about the
true verbosity level (i.e., let it distinguish level 0 or
1).
We then have to also make an adjustment to any transport
methods that assumed "verbose > 0" meant they could spew
lots of debugging information. Before, they could only get
"0" or "2", but now they will also receive "1". They need to
adjust their condition for turning on such spew from
"verbose > 0" to "verbose > 1".
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-17 13:37:15 +04:00
|
|
|
if (verbosity >= 1)
|
2010-02-24 15:50:23 +03:00
|
|
|
transport->verbose = verbosity <= 3 ? verbosity : 3;
|
|
|
|
if (verbosity < 0)
|
|
|
|
transport->verbose = -1;
|
2010-02-24 15:50:26 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Rules used to determine whether to report progress (processing aborts
|
|
|
|
* when a rule is satisfied):
|
|
|
|
*
|
2012-02-14 00:17:15 +04:00
|
|
|
* . Report progress, if force_progress is 1 (ie. --progress).
|
|
|
|
* . Don't report progress, if force_progress is 0 (ie. --no-progress).
|
|
|
|
* . Don't report progress, if verbosity < 0 (ie. -q/--quiet ).
|
|
|
|
* . Report progress if isatty(2) is 1.
|
2010-02-24 15:50:26 +03:00
|
|
|
**/
|
2012-02-14 00:17:15 +04:00
|
|
|
if (force_progress >= 0)
|
|
|
|
transport->progress = !!force_progress;
|
|
|
|
else
|
|
|
|
transport->progress = verbosity >= 0 && isatty(2);
|
2010-02-24 15:50:23 +03:00
|
|
|
}
|
|
|
|
|
2012-03-29 11:21:23 +04:00
|
|
|
static void die_with_unpushed_submodules(struct string_list *needs_pushing)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-06-17 23:20:53 +03:00
|
|
|
fprintf(stderr, _("The following submodule paths contain changes that can\n"
|
|
|
|
"not be found on any remote:\n"));
|
2012-03-29 11:21:23 +04:00
|
|
|
for (i = 0; i < needs_pushing->nr; i++)
|
2016-08-24 00:40:08 +03:00
|
|
|
fprintf(stderr, " %s\n", needs_pushing->items[i].string);
|
2016-06-17 23:20:53 +03:00
|
|
|
fprintf(stderr, _("\nPlease try\n\n"
|
|
|
|
" git push --recurse-submodules=on-demand\n\n"
|
|
|
|
"or cd to the path and use\n\n"
|
|
|
|
" git push\n\n"
|
|
|
|
"to push them to a remote.\n\n"));
|
2012-03-29 11:21:23 +04:00
|
|
|
|
|
|
|
string_list_clear(needs_pushing, 0);
|
|
|
|
|
2016-06-17 23:20:53 +03:00
|
|
|
die(_("Aborting."));
|
2012-03-29 11:21:23 +04:00
|
|
|
}
|
|
|
|
|
2013-01-13 09:17:03 +04:00
|
|
|
static int run_pre_push_hook(struct transport *transport,
|
|
|
|
struct ref *remote_refs)
|
|
|
|
{
|
|
|
|
int ret = 0, x;
|
|
|
|
struct ref *r;
|
2014-08-19 23:09:35 +04:00
|
|
|
struct child_process proc = CHILD_PROCESS_INIT;
|
2013-01-13 09:17:03 +04:00
|
|
|
struct strbuf buf;
|
|
|
|
const char *argv[4];
|
|
|
|
|
|
|
|
if (!(argv[0] = find_hook("pre-push")))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
argv[1] = transport->remote->name;
|
|
|
|
argv[2] = transport->url;
|
|
|
|
argv[3] = NULL;
|
|
|
|
|
|
|
|
proc.argv = argv;
|
|
|
|
proc.in = -1;
|
2019-02-23 01:25:06 +03:00
|
|
|
proc.trace2_hook_name = "pre-push";
|
2013-01-13 09:17:03 +04:00
|
|
|
|
|
|
|
if (start_command(&proc)) {
|
|
|
|
finish_command(&proc);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-11-16 11:05:58 +03:00
|
|
|
sigchain_push(SIGPIPE, SIG_IGN);
|
|
|
|
|
2013-01-13 09:17:03 +04:00
|
|
|
strbuf_init(&buf, 256);
|
|
|
|
|
|
|
|
for (r = remote_refs; r; r = r->next) {
|
|
|
|
if (!r->peer_ref) continue;
|
|
|
|
if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
|
2013-07-09 01:42:40 +04:00
|
|
|
if (r->status == REF_STATUS_REJECT_STALE) continue;
|
2020-10-03 15:10:44 +03:00
|
|
|
if (r->status == REF_STATUS_REJECT_REMOTE_UPDATED) continue;
|
2013-01-13 09:17:03 +04:00
|
|
|
if (r->status == REF_STATUS_UPTODATE) continue;
|
|
|
|
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf( &buf, "%s %s %s %s\n",
|
2015-11-10 05:22:20 +03:00
|
|
|
r->peer_ref->name, oid_to_hex(&r->new_oid),
|
|
|
|
r->name, oid_to_hex(&r->old_oid));
|
2013-01-13 09:17:03 +04:00
|
|
|
|
2015-11-16 11:05:58 +03:00
|
|
|
if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
|
|
|
|
/* We do not mind if a hook does not read all refs. */
|
|
|
|
if (errno != EPIPE)
|
|
|
|
ret = -1;
|
2013-01-13 09:17:03 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
x = close(proc.in);
|
|
|
|
if (!ret)
|
|
|
|
ret = x;
|
|
|
|
|
2015-11-16 11:05:58 +03:00
|
|
|
sigchain_pop(SIGPIPE);
|
|
|
|
|
2013-01-13 09:17:03 +04:00
|
|
|
x = finish_command(&proc);
|
|
|
|
if (!ret)
|
|
|
|
ret = x;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-11-10 08:48:55 +03:00
|
|
|
int transport_push(struct repository *r,
|
|
|
|
struct transport *transport,
|
2018-05-17 01:58:17 +03:00
|
|
|
struct refspec *rs, int flags,
|
2012-11-30 05:41:33 +04:00
|
|
|
unsigned int *reject_reasons)
|
2007-09-11 07:03:04 +04:00
|
|
|
{
|
2012-11-30 05:41:33 +04:00
|
|
|
*reject_reasons = 0;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2018-04-21 13:10:00 +03:00
|
|
|
if (transport_color_config() < 0)
|
|
|
|
return -1;
|
|
|
|
|
2017-12-15 00:44:45 +03:00
|
|
|
if (transport->vtable->push_refs) {
|
2014-03-05 23:04:54 +04:00
|
|
|
struct ref *remote_refs;
|
2009-03-09 04:06:07 +03:00
|
|
|
struct ref *local_refs = get_local_heads();
|
|
|
|
int match_flags = MATCH_REFS_NONE;
|
2010-02-24 15:50:24 +03:00
|
|
|
int verbose = (transport->verbose > 0);
|
|
|
|
int quiet = (transport->verbose < 0);
|
2009-06-23 05:10:01 +04:00
|
|
|
int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
|
2010-01-17 00:45:31 +03:00
|
|
|
int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
|
2010-02-27 07:52:15 +03:00
|
|
|
int push_ret, ret, err;
|
2020-07-28 23:25:12 +03:00
|
|
|
struct strvec ref_prefixes = STRVEC_INIT;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2018-05-17 01:58:22 +03:00
|
|
|
if (check_push_refs(local_refs, rs) < 0)
|
2014-03-05 23:04:54 +04:00
|
|
|
return -1;
|
|
|
|
|
2018-05-17 02:48:21 +03:00
|
|
|
refspec_ref_prefixes(rs, &ref_prefixes);
|
2018-03-15 20:31:26 +03:00
|
|
|
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_enter("transport_push", "get_refs_list", r);
|
2018-03-15 20:31:26 +03:00
|
|
|
remote_refs = transport->vtable->get_refs_list(transport, 1,
|
|
|
|
&ref_prefixes);
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_leave("transport_push", "get_refs_list", r);
|
2018-03-15 20:31:26 +03:00
|
|
|
|
2020-07-28 23:25:12 +03:00
|
|
|
strvec_clear(&ref_prefixes);
|
2014-03-05 23:04:54 +04:00
|
|
|
|
2009-03-09 04:06:07 +03:00
|
|
|
if (flags & TRANSPORT_PUSH_ALL)
|
|
|
|
match_flags |= MATCH_REFS_ALL;
|
|
|
|
if (flags & TRANSPORT_PUSH_MIRROR)
|
|
|
|
match_flags |= MATCH_REFS_MIRROR;
|
2012-02-23 02:43:41 +04:00
|
|
|
if (flags & TRANSPORT_PUSH_PRUNE)
|
|
|
|
match_flags |= MATCH_REFS_PRUNE;
|
2013-03-05 00:09:50 +04:00
|
|
|
if (flags & TRANSPORT_PUSH_FOLLOW_TAGS)
|
|
|
|
match_flags |= MATCH_REFS_FOLLOW_TAGS;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2018-05-17 01:58:21 +03:00
|
|
|
if (match_push_refs(local_refs, &remote_refs, rs, match_flags))
|
2009-03-09 04:06:07 +03:00
|
|
|
return -1;
|
|
|
|
|
2013-07-09 22:01:06 +04:00
|
|
|
if (transport->smart_options &&
|
|
|
|
transport->smart_options->cas &&
|
|
|
|
!is_empty_cas(transport->smart_options->cas))
|
|
|
|
apply_push_cas(transport->smart_options->cas,
|
|
|
|
transport->remote, remote_refs);
|
|
|
|
|
2010-01-08 05:12:42 +03:00
|
|
|
set_ref_status_for_push(remote_refs,
|
|
|
|
flags & TRANSPORT_PUSH_MIRROR,
|
|
|
|
flags & TRANSPORT_PUSH_FORCE);
|
|
|
|
|
2013-01-13 09:17:03 +04:00
|
|
|
if (!(flags & TRANSPORT_PUSH_NO_HOOK))
|
|
|
|
if (run_pre_push_hook(transport, remote_refs))
|
|
|
|
return -1;
|
|
|
|
|
2016-12-19 21:25:33 +03:00
|
|
|
if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
|
|
|
|
TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&
|
|
|
|
!is_bare_repository()) {
|
2011-08-20 02:08:47 +04:00
|
|
|
struct ref *ref = remote_refs;
|
2017-03-31 04:40:00 +03:00
|
|
|
struct oid_array commits = OID_ARRAY_INIT;
|
2016-11-16 18:11:05 +03:00
|
|
|
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_enter("transport_push", "push_submodules", r);
|
2011-08-20 02:08:47 +04:00
|
|
|
for (; ref; ref = ref->next)
|
2016-11-16 18:11:05 +03:00
|
|
|
if (!is_null_oid(&ref->new_oid))
|
2017-03-31 04:40:00 +03:00
|
|
|
oid_array_append(&commits,
|
2017-03-31 04:39:56 +03:00
|
|
|
&ref->new_oid);
|
2016-11-16 18:11:05 +03:00
|
|
|
|
2018-11-10 08:48:55 +03:00
|
|
|
if (!push_unpushed_submodules(r,
|
2018-09-21 18:57:35 +03:00
|
|
|
&commits,
|
2017-04-05 20:47:19 +03:00
|
|
|
transport->remote,
|
2018-05-17 01:58:23 +03:00
|
|
|
rs,
|
2017-04-05 20:47:16 +03:00
|
|
|
transport->push_options,
|
2016-11-17 21:46:04 +03:00
|
|
|
pretend)) {
|
2017-03-31 04:40:00 +03:00
|
|
|
oid_array_clear(&commits);
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_leave("transport_push", "push_submodules", r);
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("failed to push all needed submodules"));
|
2016-11-16 18:11:05 +03:00
|
|
|
}
|
2017-03-31 04:40:00 +03:00
|
|
|
oid_array_clear(&commits);
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_leave("transport_push", "push_submodules", r);
|
2012-03-29 11:21:24 +04:00
|
|
|
}
|
|
|
|
|
2016-11-17 21:46:04 +03:00
|
|
|
if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||
|
2016-12-19 21:25:33 +03:00
|
|
|
((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
|
|
|
|
TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&
|
2016-11-17 21:46:04 +03:00
|
|
|
!pretend)) && !is_bare_repository()) {
|
2011-08-20 02:08:47 +04:00
|
|
|
struct ref *ref = remote_refs;
|
2014-07-18 13:19:00 +04:00
|
|
|
struct string_list needs_pushing = STRING_LIST_INIT_DUP;
|
2017-03-31 04:40:00 +03:00
|
|
|
struct oid_array commits = OID_ARRAY_INIT;
|
2012-03-29 11:21:23 +04:00
|
|
|
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_enter("transport_push", "check_submodules", r);
|
2011-08-20 02:08:47 +04:00
|
|
|
for (; ref; ref = ref->next)
|
2016-11-16 18:11:05 +03:00
|
|
|
if (!is_null_oid(&ref->new_oid))
|
2017-03-31 04:40:00 +03:00
|
|
|
oid_array_append(&commits,
|
2017-03-31 04:39:56 +03:00
|
|
|
&ref->new_oid);
|
2016-11-16 18:11:05 +03:00
|
|
|
|
2018-11-10 08:48:55 +03:00
|
|
|
if (find_unpushed_submodules(r,
|
2018-09-21 18:57:35 +03:00
|
|
|
&commits,
|
|
|
|
transport->remote->name,
|
|
|
|
&needs_pushing)) {
|
2017-03-31 04:40:00 +03:00
|
|
|
oid_array_clear(&commits);
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_leave("transport_push", "check_submodules", r);
|
2016-11-16 18:11:05 +03:00
|
|
|
die_with_unpushed_submodules(&needs_pushing);
|
|
|
|
}
|
|
|
|
string_list_clear(&needs_pushing, 0);
|
2017-03-31 04:40:00 +03:00
|
|
|
oid_array_clear(&commits);
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_leave("transport_push", "check_submodules", r);
|
2011-08-20 02:08:47 +04:00
|
|
|
}
|
|
|
|
|
2019-10-03 02:49:29 +03:00
|
|
|
if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY)) {
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_enter("transport_push", "push_refs", r);
|
2017-12-15 00:44:45 +03:00
|
|
|
push_ret = transport->vtable->push_refs(transport, remote_refs, flags);
|
2019-10-08 07:18:24 +03:00
|
|
|
trace2_region_leave("transport_push", "push_refs", r);
|
2019-10-03 02:49:29 +03:00
|
|
|
} else
|
2016-12-19 21:25:33 +03:00
|
|
|
push_ret = 0;
|
2010-01-08 05:12:43 +03:00
|
|
|
err = push_had_errors(remote_refs);
|
2010-02-27 07:52:15 +03:00
|
|
|
ret = push_ret | err;
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2010-01-08 05:12:43 +03:00
|
|
|
if (!quiet || err)
|
2010-02-17 02:42:52 +03:00
|
|
|
transport_print_push_status(transport->url, remote_refs,
|
2009-08-13 03:36:04 +04:00
|
|
|
verbose | porcelain, porcelain,
|
2012-11-30 05:41:33 +04:00
|
|
|
reject_reasons);
|
2009-03-09 04:06:07 +03:00
|
|
|
|
2010-01-17 00:45:31 +03:00
|
|
|
if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
|
|
|
|
set_upstreams(transport, remote_refs, pretend);
|
|
|
|
|
2016-12-19 21:25:33 +03:00
|
|
|
if (!(flags & (TRANSPORT_PUSH_DRY_RUN |
|
|
|
|
TRANSPORT_RECURSE_SUBMODULES_ONLY))) {
|
2009-03-09 04:06:07 +03:00
|
|
|
struct ref *ref;
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next)
|
2010-02-17 02:42:52 +03:00
|
|
|
transport_update_tracking_ref(transport->remote, ref, verbose);
|
2009-03-09 04:06:07 +03:00
|
|
|
}
|
|
|
|
|
2010-02-27 07:52:15 +03:00
|
|
|
if (porcelain && !push_ret)
|
|
|
|
puts("Done");
|
2010-03-15 10:58:24 +03:00
|
|
|
else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
|
2009-03-09 04:06:07 +03:00
|
|
|
fprintf(stderr, "Everything up-to-date\n");
|
2010-02-27 07:52:15 +03:00
|
|
|
|
2009-03-09 04:06:07 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 1;
|
2007-09-11 07:03:04 +04:00
|
|
|
}
|
|
|
|
|
2018-03-15 20:31:23 +03:00
|
|
|
const struct ref *transport_get_remote_refs(struct transport *transport,
|
2020-07-28 23:25:12 +03:00
|
|
|
const struct strvec *ref_prefixes)
|
2007-09-11 07:03:11 +04:00
|
|
|
{
|
2010-02-16 10:18:21 +03:00
|
|
|
if (!transport->got_remote_refs) {
|
2018-03-15 20:31:23 +03:00
|
|
|
transport->remote_refs =
|
|
|
|
transport->vtable->get_refs_list(transport, 0,
|
|
|
|
ref_prefixes);
|
2010-02-16 10:18:21 +03:00
|
|
|
transport->got_remote_refs = 1;
|
|
|
|
}
|
2009-12-09 18:26:31 +03:00
|
|
|
|
2007-09-11 07:03:11 +04:00
|
|
|
return transport->remote_refs;
|
|
|
|
}
|
|
|
|
|
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-01 23:13:20 +03:00
|
|
|
int transport_fetch_refs(struct transport *transport, struct ref *refs)
|
2007-09-11 07:03:11 +04:00
|
|
|
{
|
2007-09-14 11:31:21 +04:00
|
|
|
int rc;
|
2009-08-24 08:04:09 +04:00
|
|
|
int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
|
2009-11-18 04:42:24 +03:00
|
|
|
struct ref **heads = NULL;
|
|
|
|
struct ref *rm;
|
2007-09-11 07:03:11 +04:00
|
|
|
|
|
|
|
for (rm = refs; rm; rm = rm->next) {
|
2009-08-24 08:04:09 +04:00
|
|
|
nr_refs++;
|
2007-09-11 07:03:11 +04:00
|
|
|
if (rm->peer_ref &&
|
2015-11-10 05:22:20 +03:00
|
|
|
!is_null_oid(&rm->old_oid) &&
|
convert "oidcmp() == 0" to oideq()
Using the more restrictive oideq() should, in the long run,
give the compiler more opportunities to optimize these
callsites. For now, this conversion should be a complete
noop with respect to the generated code.
The result is also perhaps a little more readable, as it
avoids the "zero is equal" idiom. Since it's so prevalent in
C, I think seasoned programmers tend not to even notice it
anymore, but it can sometimes make for awkward double
negations (e.g., we can drop a few !!oidcmp() instances
here).
This patch was generated almost entirely by the included
coccinelle patch. This mechanical conversion should be
completely safe, because we check explicitly for cases where
oidcmp() is compared to 0, which is what oideq() is doing
under the hood. Note that we don't have to catch "!oidcmp()"
separately; coccinelle's standard isomorphisms make sure the
two are treated equivalently.
I say "almost" because I did hand-edit the coccinelle output
to fix up a few style violations (it mostly keeps the
original formatting, but sometimes unwraps long lines).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-29 00:22:40 +03:00
|
|
|
oideq(&rm->peer_ref->old_oid, &rm->old_oid))
|
2007-09-11 07:03:11 +04:00
|
|
|
continue;
|
2007-09-14 11:31:18 +04:00
|
|
|
ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
|
2007-09-14 11:31:21 +04:00
|
|
|
heads[nr_heads++] = rm;
|
2007-09-11 07:03:11 +04:00
|
|
|
}
|
|
|
|
|
2009-08-24 08:04:09 +04:00
|
|
|
if (!nr_heads) {
|
|
|
|
/*
|
|
|
|
* When deepening of a shallow repository is requested,
|
|
|
|
* then local and remote refs are likely to still be equal.
|
|
|
|
* Just feed them all to the fetch method in that case.
|
|
|
|
* This condition shouldn't be met in a non-deepening fetch
|
2013-06-18 21:44:58 +04:00
|
|
|
* (see builtin/fetch.c:quickfetch()).
|
2009-08-24 08:04:09 +04:00
|
|
|
*/
|
2016-02-23 01:44:25 +03:00
|
|
|
ALLOC_ARRAY(heads, nr_refs);
|
2009-08-24 08:04:09 +04:00
|
|
|
for (rm = refs; rm; rm = rm->next)
|
|
|
|
heads[nr_heads++] = rm;
|
|
|
|
}
|
|
|
|
|
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-01 23:13:20 +03:00
|
|
|
rc = transport->vtable->fetch(transport, nr_heads, heads);
|
2009-12-09 18:26:31 +03:00
|
|
|
|
2007-09-11 07:03:11 +04:00
|
|
|
free(heads);
|
2007-09-14 11:31:21 +04:00
|
|
|
return rc;
|
2007-09-11 07:03:11 +04:00
|
|
|
}
|
|
|
|
|
2007-09-14 11:31:23 +04:00
|
|
|
void transport_unlock_pack(struct transport *transport)
|
|
|
|
{
|
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-10 23:57:22 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < transport->pack_lockfiles.nr; i++)
|
|
|
|
unlink_or_warn(transport->pack_lockfiles.items[i].string);
|
|
|
|
string_list_clear(&transport->pack_lockfiles, 0);
|
2007-09-14 11:31:23 +04:00
|
|
|
}
|
|
|
|
|
2009-12-09 18:26:33 +03:00
|
|
|
int transport_connect(struct transport *transport, const char *name,
|
|
|
|
const char *exec, int fd[2])
|
|
|
|
{
|
2017-12-15 00:44:45 +03:00
|
|
|
if (transport->vtable->connect)
|
|
|
|
return transport->vtable->connect(transport, name, exec, fd);
|
2009-12-09 18:26:33 +03:00
|
|
|
else
|
2018-07-21 10:49:40 +03:00
|
|
|
die(_("operation not supported by protocol"));
|
2009-12-09 18:26:33 +03:00
|
|
|
}
|
|
|
|
|
2007-09-11 07:03:04 +04:00
|
|
|
int transport_disconnect(struct transport *transport)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2017-12-15 00:44:45 +03:00
|
|
|
if (transport->vtable->disconnect)
|
|
|
|
ret = transport->vtable->disconnect(transport);
|
2007-09-11 07:03:04 +04:00
|
|
|
free(transport);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-04-17 12:20:11 +04:00
|
|
|
|
|
|
|
/*
|
2012-03-28 12:41:54 +04:00
|
|
|
* Strip username (and password) from a URL and return
|
2009-04-17 12:20:11 +04:00
|
|
|
* it in a newly allocated string.
|
|
|
|
*/
|
|
|
|
char *transport_anonymize_url(const char *url)
|
|
|
|
{
|
2016-02-23 01:45:05 +03:00
|
|
|
char *scheme_prefix, *anon_part;
|
2009-04-17 12:20:11 +04:00
|
|
|
size_t anon_len, prefix_len = 0;
|
|
|
|
|
|
|
|
anon_part = strchr(url, '@');
|
2013-11-28 23:50:03 +04:00
|
|
|
if (url_is_local_not_ssh(url) || !anon_part)
|
2009-04-17 12:20:11 +04:00
|
|
|
goto literal_copy;
|
|
|
|
|
|
|
|
anon_len = strlen(++anon_part);
|
|
|
|
scheme_prefix = strstr(url, "://");
|
|
|
|
if (!scheme_prefix) {
|
|
|
|
if (!strchr(anon_part, ':'))
|
|
|
|
/* cannot be "me@there:/path/name" */
|
|
|
|
goto literal_copy;
|
|
|
|
} else {
|
|
|
|
const char *cp;
|
|
|
|
/* make sure scheme is reasonable */
|
|
|
|
for (cp = url; cp < scheme_prefix; cp++) {
|
|
|
|
switch (*cp) {
|
|
|
|
/* RFC 1738 2.1 */
|
|
|
|
case '+': case '.': case '-':
|
|
|
|
break; /* ok */
|
|
|
|
default:
|
|
|
|
if (isalnum(*cp))
|
|
|
|
break;
|
|
|
|
/* it isn't */
|
|
|
|
goto literal_copy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* @ past the first slash does not count */
|
|
|
|
cp = strchr(scheme_prefix + 3, '/');
|
|
|
|
if (cp && cp < anon_part)
|
|
|
|
goto literal_copy;
|
|
|
|
prefix_len = scheme_prefix - url + 3;
|
|
|
|
}
|
2016-02-23 01:45:05 +03:00
|
|
|
return xstrfmt("%.*s%.*s", (int)prefix_len, url,
|
|
|
|
(int)anon_len, anon_part);
|
2009-04-17 12:20:11 +04:00
|
|
|
literal_copy:
|
|
|
|
return xstrdup(url);
|
|
|
|
}
|