summaryrefslogtreecommitdiff
path: root/builtin/fetch.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/fetch.c')
-rw-r--r--builtin/fetch.c224
1 files changed, 148 insertions, 76 deletions
diff --git a/builtin/fetch.c b/builtin/fetch.c
index ea5b9669ad..34d2bd123b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -6,6 +6,7 @@
#include "repository.h"
#include "refs.h"
#include "refspec.h"
+#include "object-store.h"
#include "commit.h"
#include "builtin.h"
#include "string-list.h"
@@ -63,6 +64,7 @@ static int shown_url = 0;
static struct refspec refmap = REFSPEC_INIT_FETCH;
static struct list_objects_filter_options filter_options;
static struct string_list server_options = STRING_LIST_INIT_DUP;
+static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
static int git_fetch_config(const char *k, const char *v, void *cb)
{
@@ -93,19 +95,6 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
return git_default_config(k, v, cb);
}
-static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
-{
- if (!strcmp(var, "submodule.fetchjobs")) {
- max_children = parse_submodule_fetchjobs(var, value);
- return 0;
- } else if (!strcmp(var, "fetch.recursesubmodules")) {
- recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
- return 0;
- }
-
- return 0;
-}
-
static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
{
/*
@@ -174,6 +163,8 @@ static struct option builtin_fetch_options[] = {
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
TRANSPORT_FAMILY_IPV6),
+ OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
+ N_("report that we have only objects reachable from this object")),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
@@ -254,9 +245,9 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
return 0;
}
-static void find_non_local_tags(struct transport *transport,
- struct ref **head,
- struct ref ***tail)
+static void find_non_local_tags(const struct ref *refs,
+ struct ref **head,
+ struct ref ***tail)
{
struct string_list existing_refs = STRING_LIST_INIT_DUP;
struct string_list remote_refs = STRING_LIST_INIT_NODUP;
@@ -264,7 +255,7 @@ static void find_non_local_tags(struct transport *transport,
struct string_list_item *item = NULL;
for_each_ref(add_existing, &existing_refs);
- for (ref = transport_get_remote_refs(transport, NULL); ref; ref = ref->next) {
+ for (ref = refs; ref; ref = ref->next) {
if (!starts_with(ref->name, "refs/tags/"))
continue;
@@ -338,7 +329,8 @@ static void find_non_local_tags(struct transport *transport,
string_list_clear(&remote_refs, 0);
}
-static struct ref *get_ref_map(struct transport *transport,
+static struct ref *get_ref_map(struct remote *remote,
+ const struct ref *remote_refs,
struct refspec *rs,
int tags, int *autotags)
{
@@ -346,26 +338,11 @@ static struct ref *get_ref_map(struct transport *transport,
struct ref *rm;
struct ref *ref_map = NULL;
struct ref **tail = &ref_map;
- struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
/* opportunistically-updated references: */
struct ref *orefs = NULL, **oref_tail = &orefs;
- const struct ref *remote_refs;
-
- if (rs->nr)
- refspec_ref_prefixes(rs, &ref_prefixes);
- else if (transport->remote && transport->remote->fetch.nr)
- refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
-
- if (ref_prefixes.argc &&
- (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {
- argv_array_push(&ref_prefixes, "refs/tags/");
- }
-
- remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
-
- argv_array_clear(&ref_prefixes);
+ struct string_list existing_refs = STRING_LIST_INIT_DUP;
if (rs->nr) {
struct refspec *fetch_refspec;
@@ -402,7 +379,7 @@ static struct ref *get_ref_map(struct transport *transport,
if (refmap.nr)
fetch_refspec = &refmap;
else
- fetch_refspec = &transport->remote->fetch;
+ fetch_refspec = &remote->fetch;
for (i = 0; i < fetch_refspec->nr; i++)
get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
@@ -410,7 +387,6 @@ static struct ref *get_ref_map(struct transport *transport,
die("--refmap option is only meaningful with command-line refspec(s).");
} else {
/* Use the defaults */
- struct remote *remote = transport->remote;
struct branch *branch = branch_get(NULL);
int has_merge = branch_has_merge_config(branch);
if (remote &&
@@ -449,7 +425,7 @@ static struct ref *get_ref_map(struct transport *transport,
/* also fetch all tags */
get_fetch_map(remote_refs, tag_refspec, &tail, 0);
else if (tags == TAGS_DEFAULT && *autotags)
- find_non_local_tags(transport, &ref_map, &tail);
+ find_non_local_tags(remote_refs, &ref_map, &tail);
/* Now append any refs to be updated opportunistically: */
*tail = orefs;
@@ -458,7 +434,23 @@ static struct ref *get_ref_map(struct transport *transport,
tail = &rm->next;
}
- return ref_remove_duplicates(ref_map);
+ ref_map = ref_remove_duplicates(ref_map);
+
+ for_each_ref(add_existing, &existing_refs);
+ for (rm = ref_map; rm; rm = rm->next) {
+ if (rm->peer_ref) {
+ struct string_list_item *peer_item =
+ string_list_lookup(&existing_refs,
+ rm->peer_ref->name);
+ if (peer_item) {
+ struct object_id *old_oid = peer_item->util;
+ oidcpy(&rm->peer_ref->old_oid, old_oid);
+ }
+ }
+ }
+ string_list_clear(&existing_refs, 1);
+
+ return ref_map;
}
#define STORE_REF_ERROR_OTHER 1
@@ -683,8 +675,10 @@ static int update_local_ref(struct ref *ref,
return r;
}
- current = lookup_commit_reference_gently(&ref->old_oid, 1);
- updated = lookup_commit_reference_gently(&ref->new_oid, 1);
+ current = lookup_commit_reference_gently(the_repository,
+ &ref->old_oid, 1);
+ updated = lookup_commit_reference_gently(the_repository,
+ &ref->new_oid, 1);
if (!current || !updated) {
const char *msg;
const char *what;
@@ -768,7 +762,7 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
}
static int store_updated_refs(const char *raw_url, const char *remote_name,
- struct ref *ref_map)
+ int connectivity_checked, struct ref *ref_map)
{
FILE *fp;
struct commit *commit;
@@ -777,7 +771,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
const char *what, *kind;
struct ref *rm;
char *url;
- const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
+ const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
int want_status;
int summary_width = transport_summary_width(ref_map);
@@ -790,10 +784,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
else
url = xstrdup("foreign");
- rm = ref_map;
- if (check_connected(iterate_ref_map, &rm, NULL)) {
- rc = error(_("%s did not send all necessary objects\n"), url);
- goto abort;
+ if (!connectivity_checked) {
+ rm = ref_map;
+ if (check_connected(iterate_ref_map, &rm, NULL)) {
+ rc = error(_("%s did not send all necessary objects\n"), url);
+ goto abort;
+ }
}
prepare_format_display(ref_map);
@@ -817,7 +813,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
}
- commit = lookup_commit_reference_gently(&rm->old_oid,
+ commit = lookup_commit_reference_gently(the_repository,
+ &rm->old_oid,
1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -945,15 +942,32 @@ static int quickfetch(struct ref *ref_map)
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_refs(struct transport *transport, struct ref *ref_map,
+ struct ref **updated_remote_refs)
{
int ret = quickfetch(ref_map);
if (ret)
- ret = transport_fetch_refs(transport, ref_map);
+ ret = transport_fetch_refs(transport, ref_map,
+ updated_remote_refs);
if (!ret)
- ret |= store_updated_refs(transport->url,
- transport->remote->name,
- ref_map);
+ /*
+ * Keep the new pack's ".keep" file around to allow the caller
+ * time to update refs to reference the new objects.
+ */
+ return 0;
+ transport_unlock_pack(transport);
+ return ret;
+}
+
+/* Update local refs based on the ref values fetched from a remote */
+static int consume_refs(struct transport *transport, struct ref *ref_map)
+{
+ int connectivity_checked = transport->smart_options
+ ? transport->smart_options->connectivity_checked : 0;
+ int ret = store_updated_refs(transport->url,
+ transport->remote->name,
+ connectivity_checked,
+ ref_map);
transport_unlock_pack(transport);
return ret;
}
@@ -1029,7 +1043,7 @@ static void check_not_current_branch(struct ref *ref_map)
static int truncate_fetch_head(void)
{
- const char *filename = git_path_fetch_head();
+ const char *filename = git_path_fetch_head(the_repository);
FILE *fp = fopen_for_writing(filename);
if (!fp)
@@ -1049,6 +1063,40 @@ static void set_option(struct transport *transport, const char *name, const char
name, transport->url);
}
+
+static int add_oid(const char *refname, const struct object_id *oid, int flags,
+ void *cb_data)
+{
+ struct oid_array *oids = cb_data;
+
+ oid_array_append(oids, oid);
+ return 0;
+}
+
+static void add_negotiation_tips(struct git_transport_options *smart_options)
+{
+ struct oid_array *oids = xcalloc(1, sizeof(*oids));
+ int i;
+
+ for (i = 0; i < negotiation_tip.nr; i++) {
+ const char *s = negotiation_tip.items[i].string;
+ int old_nr;
+ if (!has_glob_specials(s)) {
+ struct object_id oid;
+ if (get_oid(s, &oid))
+ die("%s is not a valid object", s);
+ oid_array_append(oids, &oid);
+ continue;
+ }
+ old_nr = oids->nr;
+ for_each_glob_ref(add_oid, s, oids);
+ if (old_nr == oids->nr)
+ warning("Ignoring --negotiation-tip=%s because it does not match any refs",
+ s);
+ }
+ smart_options->negotiation_tips = oids;
+}
+
static struct transport *prepare_transport(struct remote *remote, int deepen)
{
struct transport *transport;
@@ -1075,6 +1123,12 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
filter_options.filter_spec);
set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
}
+ if (negotiation_tip.nr) {
+ if (transport->smart_options)
+ add_negotiation_tips(transport->smart_options);
+ else
+ warning("Ignoring --negotiation-tip because the protocol does not support it.");
+ }
return transport;
}
@@ -1099,7 +1153,8 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- fetch_refs(transport, ref_map);
+ if (!fetch_refs(transport, ref_map, NULL))
+ consume_refs(transport, ref_map);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1110,13 +1165,12 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
static int do_fetch(struct transport *transport,
struct refspec *rs)
{
- struct string_list existing_refs = STRING_LIST_INIT_DUP;
struct ref *ref_map;
- struct ref *rm;
int autotags = (transport->remote->fetch_tags == 1);
int retcode = 0;
-
- for_each_ref(add_existing, &existing_refs);
+ const struct ref *remote_refs;
+ struct ref *updated_remote_refs = NULL;
+ struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
if (tags == TAGS_DEFAULT) {
if (transport->remote->fetch_tags == 2)
@@ -1132,22 +1186,24 @@ static int do_fetch(struct transport *transport,
goto cleanup;
}
- ref_map = get_ref_map(transport, rs, tags, &autotags);
- if (!update_head_ok)
- check_not_current_branch(ref_map);
+ if (rs->nr)
+ refspec_ref_prefixes(rs, &ref_prefixes);
+ else if (transport->remote && transport->remote->fetch.nr)
+ refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
- for (rm = ref_map; rm; rm = rm->next) {
- if (rm->peer_ref) {
- struct string_list_item *peer_item =
- string_list_lookup(&existing_refs,
- rm->peer_ref->name);
- if (peer_item) {
- struct object_id *old_oid = peer_item->util;
- oidcpy(&rm->peer_ref->old_oid, old_oid);
- }
- }
+ if (ref_prefixes.argc &&
+ (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {
+ argv_array_push(&ref_prefixes, "refs/tags/");
}
+ remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+ argv_array_clear(&ref_prefixes);
+
+ ref_map = get_ref_map(transport->remote, remote_refs, rs,
+ tags, &autotags);
+ if (!update_head_ok)
+ check_not_current_branch(ref_map);
+
if (tags == TAGS_DEFAULT && autotags)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
if (prune) {
@@ -1164,7 +1220,24 @@ static int do_fetch(struct transport *transport,
transport->url);
}
}
- if (fetch_refs(transport, ref_map)) {
+
+ if (fetch_refs(transport, ref_map, &updated_remote_refs)) {
+ free_refs(ref_map);
+ retcode = 1;
+ goto cleanup;
+ }
+ if (updated_remote_refs) {
+ /*
+ * Regenerate ref_map using the updated remote refs. This is
+ * to account for additional information which may be provided
+ * by the transport (e.g. shallow info).
+ */
+ free_refs(ref_map);
+ ref_map = get_ref_map(transport->remote, updated_remote_refs, rs,
+ tags, &autotags);
+ free_refs(updated_remote_refs);
+ }
+ if (consume_refs(transport, ref_map)) {
free_refs(ref_map);
retcode = 1;
goto cleanup;
@@ -1176,14 +1249,13 @@ static int do_fetch(struct transport *transport,
if (tags == TAGS_DEFAULT && autotags) {
struct ref **tail = &ref_map;
ref_map = NULL;
- find_non_local_tags(transport, &ref_map, &tail);
+ find_non_local_tags(remote_refs, &ref_map, &tail);
if (ref_map)
backfill_tags(transport, ref_map);
free_refs(ref_map);
}
cleanup:
- string_list_clear(&existing_refs, 1);
return retcode;
}
@@ -1433,7 +1505,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
- config_from_gitmodules(gitmodules_fetch_config, NULL);
+ fetch_config_from_gitmodules(&max_children, &recurse_submodules);
git_config(git_fetch_config, NULL);
argc = parse_options(argc, argv, prefix,
@@ -1449,7 +1521,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (unshallow) {
if (depth)
die(_("--depth and --unshallow cannot be used together"));
- else if (!is_repository_shallow())
+ else if (!is_repository_shallow(the_repository))
die(_("--unshallow on a complete repository does not make sense"));
else
depth = xstrfmt("%d", INFINITE_DEPTH);