summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/fetch-options.txt6
-rw-r--r--builtin/fetch.c6
-rw-r--r--fetch-pack.c27
-rw-r--r--fetch-pack.h1
-rwxr-xr-xt/t5537-fetch-shallow.sh32
-rw-r--r--transport.c4
-rw-r--r--transport.h4
7 files changed, 79 insertions, 1 deletions
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index a83d2b4778..54043e3633 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -21,6 +21,12 @@
If the source repository is shallow, fetch as much as possible so that
the current repository has the same history as the source repository.
+--update-shallow::
+ By default when fetching from a shallow repository,
+ `git fetch` refuses refs that require updating
+ .git/shallow. This option updates .git/shallow and accept such
+ refs.
+
ifndef::git-pull[]
--dry-run::
Show what would be done, without making any changes.
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 7b41a7e388..d2e4fc03d8 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -36,7 +36,7 @@ static int prune = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int tags = TAGS_DEFAULT, unshallow;
+static int tags = TAGS_DEFAULT, unshallow, update_shallow;
static const char *depth;
static const char *upload_pack;
static struct strbuf default_rla = STRBUF_INIT;
@@ -104,6 +104,8 @@ static struct option builtin_fetch_options[] = {
{ OPTION_STRING, 0, "recurse-submodules-default",
&recurse_submodules_default, NULL,
N_("default mode for recursion"), PARSE_OPT_HIDDEN },
+ OPT_BOOL(0, "update-shallow", &update_shallow,
+ N_("accept refs that update .git/shallow")),
OPT_END()
};
@@ -768,6 +770,8 @@ static struct transport *prepare_transport(struct remote *remote)
set_option(transport, TRANS_OPT_KEEP, "yes");
if (depth)
set_option(transport, TRANS_OPT_DEPTH, depth);
+ if (update_shallow)
+ set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
return transport;
}
diff --git a/fetch-pack.c b/fetch-pack.c
index 34c544d0ca..a2d1b4ab28 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -993,6 +993,33 @@ static void update_shallow(struct fetch_pack_args *args,
sha1_array_append(&ref, sought[i]->old_sha1);
si->ref = &ref;
+ if (args->update_shallow) {
+ /*
+ * remote is also shallow, .git/shallow may be updated
+ * so all refs can be accepted. Make sure we only add
+ * shallow roots that are actually reachable from new
+ * refs.
+ */
+ struct sha1_array extra = SHA1_ARRAY_INIT;
+ unsigned char (*sha1)[20] = si->shallow->sha1;
+ assign_shallow_commits_to_refs(si, NULL, NULL);
+ if (!si->nr_ours && !si->nr_theirs) {
+ sha1_array_clear(&ref);
+ return;
+ }
+ for (i = 0; i < si->nr_ours; i++)
+ sha1_array_append(&extra, sha1[si->ours[i]]);
+ for (i = 0; i < si->nr_theirs; i++)
+ sha1_array_append(&extra, sha1[si->theirs[i]]);
+ setup_alternate_shallow(&shallow_lock,
+ &alternate_shallow_file,
+ &extra);
+ commit_lock_file(&shallow_lock);
+ sha1_array_clear(&extra);
+ sha1_array_clear(&ref);
+ return;
+ }
+
/*
* remote is also shallow, check what ref is safe to update
* without updating .git/shallow
diff --git a/fetch-pack.h b/fetch-pack.h
index ce595376b7..ada02f51c1 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -23,6 +23,7 @@ struct fetch_pack_args {
unsigned check_self_contained_and_connected:1;
unsigned self_contained_and_connected:1;
unsigned cloning:1;
+ unsigned update_shallow:1;
};
/*
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 022cb2c990..3ae9092f5c 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -141,4 +141,36 @@ EOF
)
'
+test_expect_success 'fetch --update-shallow' '
+ (
+ cd shallow &&
+ git checkout master &&
+ commit 7 &&
+ git tag -m foo heavy-tag HEAD^ &&
+ git tag light-tag HEAD^:tracked
+ ) &&
+ (
+ cd notshallow &&
+ git fetch --update-shallow ../shallow/.git refs/heads/*:refs/remotes/shallow/* &&
+ git fsck &&
+ git for-each-ref --sort=refname --format="%(refname)" >actual.refs &&
+ cat <<EOF >expect.refs &&
+refs/remotes/shallow/master
+refs/remotes/shallow/no-shallow
+refs/tags/heavy-tag
+refs/tags/light-tag
+EOF
+ test_cmp expect.refs actual.refs &&
+ git log --format=%s shallow/master >actual &&
+ cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/transport.c b/transport.c
index 491360be2c..a09fdb6df2 100644
--- a/transport.c
+++ b/transport.c
@@ -477,6 +477,9 @@ static int set_git_option(struct git_transport_options *opts,
} else if (!strcmp(name, TRANS_OPT_KEEP)) {
opts->keep = !!value;
return 0;
+ } else if (!strcmp(name, TRANS_OPT_UPDATE_SHALLOW)) {
+ opts->update_shallow = !!value;
+ return 0;
} else if (!strcmp(name, TRANS_OPT_DEPTH)) {
if (!value)
opts->depth = 0;
@@ -543,6 +546,7 @@ static int fetch_refs_via_pack(struct transport *transport,
args.check_self_contained_and_connected =
data->options.check_self_contained_and_connected;
args.cloning = transport->cloning;
+ args.update_shallow = data->options.update_shallow;
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
diff --git a/transport.h b/transport.h
index 59842d4994..02ea248db1 100644
--- a/transport.h
+++ b/transport.h
@@ -11,6 +11,7 @@ struct git_transport_options {
unsigned followtags : 1;
unsigned check_self_contained_and_connected : 1;
unsigned self_contained_and_connected : 1;
+ unsigned update_shallow : 1;
int depth;
const char *uploadpack;
const char *receivepack;
@@ -152,6 +153,9 @@ struct transport *transport_get(struct remote *, const char *);
/* Aggressively fetch annotated tags if possible */
#define TRANS_OPT_FOLLOWTAGS "followtags"
+/* Accept refs that may update .git/shallow without --depth */
+#define TRANS_OPT_UPDATE_SHALLOW "updateshallow"
+
/**
* Returns 0 if the option was used, non-zero otherwise. Prints a
* message to stderr if the option is not used.