clone: --dissociate option to mark that reference is only temporary

While use of the --reference option to borrow objects from an
existing local repository of the same project is an effective way to
reduce traffic when cloning a project over the network, it makes the
resulting "borrowing" repository dependent on the "borrowed"
repository.  After running

	git clone --reference=P $URL Q

the resulting repository Q will be broken if the borrowed repository
P disappears.

The way to allow the borrowed repository to be removed is to repack
the borrowing repository (i.e. run "git repack -a -d" in Q); while
power users may know it very well, it is not easily discoverable.

Teach a new "--dissociate" option to "git clone" to run this
repacking for the user.

Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2014-10-14 12:38:52 -07:00
parent 3c2dc76f01
commit fb1d6dabce
3 changed files with 46 additions and 2 deletions

View File

@ -12,7 +12,7 @@ SYNOPSIS
'git clone' [--template=<template_directory>] 'git clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>] [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--separate-git-dir <git dir>] [--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch] [--depth <depth>] [--[no-]single-branch]
[--recursive | --recurse-submodules] [--] <repository> [--recursive | --recurse-submodules] [--] <repository>
[<directory>] [<directory>]
@ -98,7 +98,14 @@ objects from the source repository into a pack in the cloned repository.
require fewer objects to be copied from the repository require fewer objects to be copied from the repository
being cloned, reducing network and local storage costs. being cloned, reducing network and local storage costs.
+ +
*NOTE*: see the NOTE for the `--shared` option. *NOTE*: see the NOTE for the `--shared` option, and also the
`--dissociate` option.
--dissociate::
Borrow the objects from reference repositories specified
with the `--reference` options only to reduce network
transfer and stop borrowing from them after a clone is made
by making necessary local copies of borrowed objects.
--quiet:: --quiet::
-q:: -q::

View File

@ -48,6 +48,7 @@ static int option_verbosity;
static int option_progress = -1; static int option_progress = -1;
static struct string_list option_config; static struct string_list option_config;
static struct string_list option_reference; static struct string_list option_reference;
static int option_dissociate;
static int opt_parse_reference(const struct option *opt, const char *arg, int unset) static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
{ {
@ -93,6 +94,8 @@ static struct option builtin_clone_options[] = {
N_("create a shallow clone of that depth")), N_("create a shallow clone of that depth")),
OPT_BOOL(0, "single-branch", &option_single_branch, OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")), N_("clone only one branch, HEAD or --branch")),
OPT_BOOL(0, "dissociate", &option_dissociate,
N_("use --reference only while cloning")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")), N_("separate git dir from working tree")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
@ -736,6 +739,16 @@ static void write_refspec_config(const char* src_ref_prefix,
strbuf_release(&value); strbuf_release(&value);
} }
static void dissociate_from_references(void)
{
static const char* argv[] = { "repack", "-a", "-d", NULL };
if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
die(_("cannot repack to clean up"));
if (unlink(git_path("objects/info/alternates")) && errno != ENOENT)
die_errno(_("cannot unlink temporary alternates file"));
}
int cmd_clone(int argc, const char **argv, const char *prefix) int cmd_clone(int argc, const char **argv, const char *prefix)
{ {
int is_bundle = 0, is_local; int is_bundle = 0, is_local;
@ -883,6 +896,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_reference.nr) if (option_reference.nr)
setup_reference(); setup_reference();
else if (option_dissociate) {
warning(_("--dissociate given, but there is no --reference"));
option_dissociate = 0;
}
fetch_pattern = value.buf; fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern); refspec = parse_fetch_refspec(1, &fetch_pattern);
@ -996,6 +1013,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_unlock_pack(transport); transport_unlock_pack(transport);
transport_disconnect(transport); transport_disconnect(transport);
if (option_dissociate)
dissociate_from_references();
junk_mode = JUNK_LEAVE_REPO; junk_mode = JUNK_LEAVE_REPO;
err = checkout(); err = checkout();

View File

@ -198,4 +198,21 @@ test_expect_success 'clone using repo pointed at by gitfile as reference' '
test_cmp expected "$base_dir/O/.git/objects/info/alternates" test_cmp expected "$base_dir/O/.git/objects/info/alternates"
' '
test_expect_success 'clone and dissociate from reference' '
git init P &&
(
cd P && test_commit one
) &&
git clone P Q &&
(
cd Q && test_commit two
) &&
git clone --no-local --reference=P Q R &&
git clone --no-local --reference=P --dissociate Q S &&
# removing the reference P would corrupt R but not S
rm -fr P &&
test_must_fail git -C R fsck &&
git -C S fsck
'
test_done test_done