Merge branch 'nd/clone-single-branch'

* nd/clone-single-branch:
  clone: add --single-branch to fetch only one branch
This commit is contained in:
Junio C Hamano 2012-01-29 13:18:50 -08:00
commit 7859f533e2
3 changed files with 129 additions and 6 deletions

View File

@ -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,14 @@ 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::
Clone only the history leading to the tip of a single branch,
either specified by the `--branch` option or the primary
branch remote's `HEAD` points at. When creating a shallow
clone with the `--depth` option, this is the default, unless
`--no-single-branch` is given to fetch the histories near the
tips of all branches.
--recursive::
--recurse-submodules::
After the clone is created, initialize all submodules within,

View File

@ -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,
"clone only one branch, HEAD or --branch"),
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,8 +430,28 @@ 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)
if (option_single_branch) {
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 && option_branch)
warning(_("Could not find remote branch %s to clone."),
option_branch);
else
get_fetch_map(remote_head, refspec, &tail, 0);
} else
get_fetch_map(refs, refspec, &tail, 0);
if (!option_mirror && !option_single_branch)
get_fetch_map(refs, tag_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;
@ -498,6 +535,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
usage_msg_opt(_("You must specify a repository to clone."),
builtin_clone_usage, builtin_clone_options);
if (option_single_branch == -1)
option_single_branch = option_depth ? 1 : 0;
if (option_mirror)
option_bare = 1;
@ -645,6 +685,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_depth)
transport_set_option(transport, TRANS_OPT_DEPTH,
option_depth);
if (option_single_branch)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
transport_set_verbosity(transport, option_verbosity, option_progress);
@ -663,6 +705,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
clear_extra_refs();
write_remote_refs(mapped_refs);
if (option_single_branch)
write_followtags(refs, reflog_msg.buf);
remote_head = find_ref_by_name(refs, "HEAD");
remote_head_points_at =

View File

@ -114,8 +114,19 @@ pull_to_client 2nd "refs/heads/B" $((64*3))
pull_to_client 3rd "refs/heads/A" $((1*3))
test_expect_success 'single branch clone' '
git clone --single-branch "file://$(pwd)/." singlebranch
'
test_expect_success 'single branch object count' '
GIT_DIR=singlebranch/.git git count-objects -v |
grep "^in-pack:" > count.singlebranch &&
echo "in-pack: 198" >expected &&
test_cmp expected count.singlebranch
'
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 +259,63 @@ 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 'clone shallow with --branch' '
git clone --depth 1 --branch A "file://$(pwd)/." shallow3
'
test_expect_success 'clone shallow object count' '
echo "in-pack: 12" > count3.expected &&
GIT_DIR=shallow3/.git git count-objects -v |
grep "^in-pack" > count3.actual &&
test_cmp count3.expected count3.actual
'
test_expect_success 'clone shallow with nonexistent --branch' '
git clone --depth 1 --branch Z "file://$(pwd)/." shallow4 &&
GIT_DIR=shallow4/.git git rev-parse HEAD >actual &&
git rev-parse HEAD >expected &&
test_cmp expected actual
'
test_expect_success 'clone shallow with detached HEAD' '
git checkout HEAD^ &&
git clone --depth 1 "file://$(pwd)/." shallow5 &&
git checkout - &&
GIT_DIR=shallow5/.git git rev-parse HEAD >actual &&
git rev-parse HEAD^ >expected &&
test_cmp expected actual
'
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)/." shallow6 &&
cat >taglist.expected <<\EOF &&
TAGB1
TAGB2
EOF
GIT_DIR=shallow6/.git git tag -l >taglist.actual &&
test_cmp taglist.expected taglist.actual &&
echo "in-pack: 7" > count6.expected &&
GIT_DIR=shallow6/.git git count-objects -v |
grep "^in-pack" > count6.actual &&
test_cmp count6.expected count6.actual
'
test_done