The main purpose of shallow clones is to reduce download by only fetching objects up to a certain depth from the given refs. The number of objects depend on how many refs to follow. So: - Only fetch HEAD or the ref specified by --branch - Only fetch tags that reference to downloaded objects More tags/branches can be fetched later using git-fetch as usual. The old behaviour can still be called with --no-single-branch Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- - add --no-single-branch so t5500 works without big changes. - die() if we cannot find suitable branch to fetch (and suggest --no-single-branch) - and a bit more tests to exercise new code --branch=<tag> (or something similar) has to wait until my other patch gets in a good shape (or gets dropped) There may be something slightly wrong with shallow code. I expect it to fetch only 3 objects (1 commit, 1 tree, 1 blob) with --depth=1 in my new test but it fetches 6 (one more commit). Documentation/git-clone.txt | 12 ++++++++- builtin/clone.c | 54 ++++++++++++++++++++++++++++++++++++++---- t/t5500-fetch-pack.sh | 34 ++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 4b8b26b..58f21d6 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -13,7 +13,8 @@ SYNOPSIS [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>] [--separate-git-dir <git dir>] - [--depth <depth>] [--recursive|--recurse-submodules] [--] <repository> + [--depth <depth> [--[no-]single-branch]] + [--recursive|--recurse-submodules] [--] <repository> [<directory>] DESCRIPTION @@ -179,6 +180,15 @@ objects from the source repository into a pack in the cloned repository. with a long history, and would want to send in fixes as patches. +--single-branch:: +--no-single-branch:: + These options are only valid when --depth is given. + `--single-branch` only fetches one branch (either HEAD or + specified by --branch) and tags that point to the downloaded + history. `--no-single-branch` fetches all branches and tags + like in normal clones. `--single-branch` is implied by + default. + --recursive:: --recurse-submodules:: After the clone is created, initialize all submodules within, diff --git a/builtin/clone.c b/builtin/clone.c index efe8b6c..3424e1c 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -37,7 +37,7 @@ static const char * const builtin_clone_usage[] = { NULL }; -static int option_no_checkout, option_bare, option_mirror; +static int option_no_checkout, option_bare, option_mirror, option_single_branch = 1; static int option_local, option_no_hardlinks, option_shared, option_recursive; static char *option_template, *option_depth; static char *option_origin = NULL; @@ -48,6 +48,7 @@ static int option_verbosity; static int option_progress; static struct string_list option_config; static struct string_list option_reference; +static const char *src_ref_prefix = "refs/heads/"; static int opt_parse_reference(const struct option *opt, const char *arg, int unset) { @@ -92,6 +93,8 @@ static struct option builtin_clone_options[] = { "path to git-upload-pack on the remote"), OPT_STRING(0, "depth", &option_depth, "depth", "create a shallow clone of that depth"), + OPT_BOOL(0, "single-branch", &option_single_branch, + "do not limit fetched refs in shallow clones"), OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir", "separate git dir from working tree"), OPT_STRING_LIST('c', "config", &option_config, "key=value", @@ -427,9 +430,29 @@ static struct ref *wanted_peer_refs(const struct ref *refs, struct ref *local_refs = head; struct ref **tail = head ? &head->next : &local_refs; - get_fetch_map(refs, refspec, &tail, 0); - if (!option_mirror) - get_fetch_map(refs, tag_refspec, &tail, 0); + if (!(option_depth && option_single_branch)) { + get_fetch_map(refs, refspec, &tail, 0); + if (!option_mirror) + get_fetch_map(refs, tag_refspec, &tail, 0); + } else { + struct ref *remote_head = NULL; + + if (!option_branch) + remote_head = guess_remote_head(head, refs, 0); + else { + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, src_ref_prefix); + strbuf_addstr(&sb, option_branch); + remote_head = find_ref_by_name(refs, sb.buf); + strbuf_release(&sb); + } + + if (!remote_head) + die(_("Remote branch \"%s\" not found. Nothing to clone.\n" + "Try --no-single-branch to fetch all refs."), + option_branch ? option_branch : "HEAD"); + get_fetch_map(remote_head, refspec, &tail, 0); + } return local_refs; } @@ -448,6 +471,21 @@ static void write_remote_refs(const struct ref *local_refs) clear_extra_refs(); } +static void write_followtags(const struct ref *refs, const char *msg) +{ + const struct ref *ref; + for (ref = refs; ref; ref = ref->next) { + if (prefixcmp(ref->name, "refs/tags/")) + continue; + if (!suffixcmp(ref->name, "^{}")) + continue; + if (!has_sha1_file(ref->old_sha1)) + continue; + update_ref(msg, ref->name, ref->old_sha1, + NULL, 0, DIE_ON_ERR); + } +} + static int write_one_config(const char *key, const char *value, void *data) { return git_config_set_multivar(key, value ? value : "true", "^$", 0); @@ -478,7 +516,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct strbuf key = STRBUF_INIT, value = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; - char *src_ref_prefix = "refs/heads/"; int err = 0; struct refspec *refspec; @@ -642,9 +679,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_set_option(transport, TRANS_OPT_KEEP, "yes"); - if (option_depth) + if (option_depth) { transport_set_option(transport, TRANS_OPT_DEPTH, option_depth); + transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, + option_single_branch ? "1" : NULL); + } transport_set_verbosity(transport, option_verbosity, option_progress); @@ -663,6 +703,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) clear_extra_refs(); write_remote_refs(mapped_refs); + if (option_depth && option_single_branch) + write_followtags(refs, reflog_msg.buf); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index bafcca7..c76a53b 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -115,7 +115,7 @@ pull_to_client 2nd "B" $((64*3)) pull_to_client 3rd "A" $((1*3)) test_expect_success 'clone shallow' ' - git clone --depth 2 "file://$(pwd)/." shallow + git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow ' test_expect_success 'clone shallow object count' ' @@ -248,4 +248,36 @@ test_expect_success 'clone shallow object count' ' grep "^count: 52" count.shallow ' +test_expect_success 'clone shallow without --no-single-branch' ' + git clone --depth 1 "file://$(pwd)/." shallow2 +' + +test_expect_success 'clone shallow object count' ' + ( + cd shallow2 && + git count-objects -v + ) > count.shallow2 && + grep "^in-pack: 6" count.shallow2 +' + +test_expect_success 'shallow clone pulling tags' ' + git tag -a -m A TAGA1 A && + git tag -a -m B TAGB1 B && + git tag TAGA2 A && + git tag TAGB2 B && + git clone --depth 1 "file://$(pwd)/." shallow3 && + + cat >taglist.expected <<\EOF && +TAGB1 +TAGB2 +EOF + GIT_DIR=shallow3/.git git tag -l >taglist.actual && + test_cmp taglist.expected taglist.actual && + + echo "in-pack: 7" > count3.expected && + GIT_DIR=shallow3/.git git count-objects -v | + grep "^in-pack" > count3.actual && + test_cmp count3.expected count3.actual +' + test_done -- 1.7.3.1.256.g2539c.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html