Browse Source

revamp git-clone.

This does two things.

 * A new flag --reference can be used to name a local repository
   that is to be used as an alternate.  This is in response to
   an inquiry by James Cloos in the message on the list
   <m3r74ykue7.fsf@lugabout.cloos.reno.nv.us>.

 * A new flag --use-separate-remote stops contaminating local
   branch namespace by upstream branch names.  The upstream
   branch heads are copied in .git/refs/remotes/ instead of
   .git/refs/heads/ and .git/remotes/origin file is set up to
   reflect this as well.  It requires to have fetch/pull update
   to understand .git/refs/remotes by Eric Wong to further
   update the repository cloned this way.

For the former change, git-fetch-pack is taught a new flag --all
to fetch from all the remote heads.  Nobody uses the git-clone-pack
with this change, so we could deprecate the command, but removal
of the command will be left to a separate round.

Signed-off-by: Junio C Hamano <junkio@cox.net>
maint
Junio C Hamano 19 years ago
parent
commit
dfeff66ed9
  1. 18
      fetch-pack.c
  2. 184
      git-clone.sh

18
fetch-pack.c

@ -7,8 +7,9 @@ @@ -7,8 +7,9 @@
static int keep_pack;
static int quiet;
static int verbose;
static int fetch_all;
static const char fetch_pack_usage[] =
"git-fetch-pack [-q] [-v] [-k] [--thin] [--exec=upload-pack] [host:]directory <refs>...";
"git-fetch-pack [--all] [-q] [-v] [-k] [--thin] [--exec=upload-pack] [host:]directory <refs>...";
static const char *exec = "git-upload-pack";

#define COMPLETE (1U << 0)
@ -266,8 +267,9 @@ static void filter_refs(struct ref **refs, int nr_match, char **match) @@ -266,8 +267,9 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
for (prev = NULL, current = *refs; current; current = next) {
next = current->next;
if ((!memcmp(current->name, "refs/", 5) &&
check_ref_format(current->name + 5)) ||
!path_match(current->name, nr_match, match)) {
check_ref_format(current->name + 5)) ||
(!fetch_all &&
!path_match(current->name, nr_match, match))) {
if (prev == NULL)
*refs = next;
else
@ -376,7 +378,11 @@ static int fetch_pack(int fd[2], int nr_match, char **match) @@ -376,7 +378,11 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
goto all_done;
}
if (find_common(fd, sha1, ref) < 0)
fprintf(stderr, "warning: no common commits\n");
if (!keep_pack)
/* When cloning, it is not unusual to have
* no common commit.
*/
fprintf(stderr, "warning: no common commits\n");

if (keep_pack)
status = receive_keep_pack(fd, "git-fetch-pack", quiet);
@ -426,6 +432,10 @@ int main(int argc, char **argv) @@ -426,6 +432,10 @@ int main(int argc, char **argv)
use_thin_pack = 1;
continue;
}
if (!strcmp("--all", arg)) {
fetch_all = 1;
continue;
}
if (!strcmp("-v", arg)) {
verbose = 1;
continue;

184
git-clone.sh

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
unset CDPATH

usage() {
echo >&2 "Usage: $0 [--bare] [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]"
echo >&2 "Usage: $0 [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]"
exit 1
}

@ -40,22 +40,72 @@ Perhaps git-update-server-info needs to be run there?" @@ -40,22 +40,72 @@ Perhaps git-update-server-info needs to be run there?"
do
name=`expr "$refname" : 'refs/\(.*\)'` &&
case "$name" in
*^*) ;;
*)
git-http-fetch -v -a -w "$name" "$name" "$1/" || exit 1
*^*) continue;;
esac
if test -n "$use_separate_remote" &&
branch_name=`expr "$name" : 'heads/\(.*\)'`
then
tname="remotes/$branch_name"
else
tname=$name
fi
git-http-fetch -v -a -w "$tname" "$name" "$1/" || exit 1
done <"$clone_tmp/refs"
rm -fr "$clone_tmp"
http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD"
}

# Read git-fetch-pack -k output and store the remote branches.
copy_refs='
use File::Path qw(mkpath);
use File::Basename qw(dirname);
my $git_dir = $ARGV[0];
my $use_separate_remote = $ARGV[1];

my $branch_top = ($use_separate_remote ? "remotes" : "heads");
my $tag_top = "tags";

sub store {
my ($sha1, $name, $top) = @_;
$name = "$git_dir/refs/$top/$name";
mkpath(dirname($name));
open O, ">", "$name";
print O "$sha1\n";
close O;
}

open FH, "<", "$git_dir/CLONE_HEAD";
while (<FH>) {
my ($sha1, $name) = /^([0-9a-f]{40})\s(.*)$/;
next if ($name =~ /\^\173/);
if ($name eq "HEAD") {
open O, ">", "$git_dir/REMOTE_HEAD";
print O "$sha1\n";
close O;
next;
}
if ($name =~ s/^refs\/heads\///) {
store($sha1, $name, $branch_top);
next;
}
if ($name =~ s/^refs\/tags\///) {
store($sha1, $name, $tag_top);
next;
}
}
close FH;
'

quiet=
use_local=no
local_shared=no
no_checkout=
upload_pack=
bare=
origin=origin
reference=
origin=
origin_override=
use_separate_remote=
while
case "$#,$1" in
0,*) break ;;
@ -68,7 +118,14 @@ while @@ -68,7 +118,14 @@ while
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
local_shared=yes; use_local=yes ;;
*,-q|*,--quiet) quiet=-q ;;
*,--use-separate-remote)
use_separate_remote=t ;;
1,-o) usage;;
1,--reference) usage ;;
*,--reference)
shift; reference="$1" ;;
*,--reference=*)
reference=`expr "$1" : '--reference=\(.*\)'` ;;
*,-o)
git-check-ref-format "$2" || {
echo >&2 "'$2' is not suitable for a branch name"
@ -100,9 +157,24 @@ then @@ -100,9 +157,24 @@ then
echo >&2 '--bare and -o $origin options are incompatible.'
exit 1
fi
if test t = "$use_separate_remote"
then
echo >&2 '--bare and --use-separate-remote options are incompatible.'
exit 1
fi
no_checkout=yes
fi

if test -z "$origin_override$origin"
then
if test -n "$use_separate_remote"
then
origin=remotes/master
else
origin=heads/origin
fi
fi

# Turn the source into an absolute path if
# it is local
repo="$1"
@ -130,6 +202,28 @@ yes) @@ -130,6 +202,28 @@ yes)
GIT_DIR="$D/.git" ;;
esac

if test -n "$reference"
then
if test -d "$reference"
then
if test -d "$reference/.git/objects"
then
reference="$reference/.git"
fi
reference=$(cd "$reference" && pwd)
echo "$reference/objects" >"$GIT_DIR/objects/info/alternates"
(cd "$reference" && tar cf - refs) |
(cd "$GIT_DIR/refs" &&
mkdir reference-tmp &&
cd reference-tmp &&
tar xf -)
else
echo >&2 "$reference: not a local directory." && usage
fi
fi

rm -f "$GIT_DIR/CLONE_HEAD"

# We do local magic only when the user tells us to.
case "$local,$use_local" in
yes,yes)
@ -165,24 +259,14 @@ yes,yes) @@ -165,24 +259,14 @@ yes,yes)
} >"$GIT_DIR/objects/info/alternates"
;;
esac

# Make a duplicate of refs and HEAD pointer
HEAD=
if test -f "$repo/HEAD"
then
HEAD=HEAD
fi
(cd "$repo" && tar cf - refs $HEAD) |
(cd "$GIT_DIR" && tar xf -) || exit 1
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
;;
*)
case "$repo" in
rsync://*)
rsync $quiet -av --ignore-existing \
--exclude info "$repo/objects/" "$GIT_DIR/objects/" &&
rsync $quiet -av --ignore-existing \
--exclude info "$repo/refs/" "$GIT_DIR/refs/" || exit

--exclude info "$repo/objects/" "$GIT_DIR/objects/" ||
exit
# Look at objects/info/alternates for rsync -- http will
# support it natively and git native ones will do it on the
# remote end. Not having that file is not a crime.
@ -205,6 +289,7 @@ yes,yes) @@ -205,6 +289,7 @@ yes,yes)
done
rm -f "$GIT_DIR/TMP_ALT"
fi
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
;;
http://*)
if test -z "@@NO_CURL@@"
@ -217,37 +302,71 @@ yes,yes) @@ -217,37 +302,71 @@ yes,yes)
;;
*)
cd "$D" && case "$upload_pack" in
'') git-clone-pack $quiet "$repo" ;;
*) git-clone-pack $quiet "$upload_pack" "$repo" ;;
esac || {
echo >&2 "clone-pack from '$repo' failed."
'') git-fetch-pack --all -k $quiet "$repo" ;;
*) git-fetch-pack --all -k $quiet "$upload_pack" "$repo" ;;
esac >"$GIT_DIR/CLONE_HEAD" || {
echo >&2 "fetch-pack from '$repo' failed."
exit 1
}
;;
esac
;;
esac
test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp"

if test -f "$GIT_DIR/CLONE_HEAD"
then
# Figure out where the remote HEAD points at.
perl -e "$copy_refs" "$GIT_DIR" "$use_separate_remote"
fi

cd "$D" || exit

if test -f "$GIT_DIR/HEAD" && test -z "$bare"
if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD"
then
head_points_at=`git-symbolic-ref HEAD`
head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
# Figure out which remote branch HEAD points at.
case "$use_separate_remote" in
'') remote_top=refs/heads ;;
*) remote_top=refs/remotes ;;
esac
head_points_at=$(
(
echo "master"
cd "$GIT_DIR/$remote_top" &&
find . -type f -print | sed -e 's/^\.\///'
) | (
done=f
while read name
do
test t = $done && continue
branch_tip=`cat "$GIT_DIR/$remote_top/$name"`
if test "$head_sha1" = "$branch_tip"
then
echo "$name"
done=t
fi
done
)
)
case "$head_points_at" in
refs/heads/*)
head_points_at=`expr "$head_points_at" : 'refs/heads/\(.*\)'`
?*)
mkdir -p "$GIT_DIR/remotes" &&
echo >"$GIT_DIR/remotes/origin" \
"URL: $repo
Pull: $head_points_at:$origin" &&
git-update-ref "refs/heads/$origin" $(git-rev-parse HEAD) &&
(cd "$GIT_DIR" && find "refs/heads" -type f -print) |
Pull: refs/heads/$head_points_at:refs/$origin" &&
case "$use_separate_remote" in
t) git-update-ref HEAD "$head_sha1" ;;
*) git-update-ref "refs/$origin" $(git-rev-parse HEAD)
esac &&
(cd "$GIT_DIR" && find "$remote_top" -type f -print) |
while read ref
do
head=`expr "$ref" : 'refs/heads/\(.*\)'` &&
test "$head_points_at" = "$head" ||
head=`expr "$ref" : 'refs/\(.*\)'` &&
name=`expr "$ref" : 'refs/[^\/]*/\(.*\)'` &&
test "$head_points_at" = "$name" ||
test "$origin" = "$head" ||
echo "Pull: ${head}:${head}"
echo "Pull: refs/heads/${name}:$remote_top/${name}"
done >>"$GIT_DIR/remotes/origin"
esac

@ -256,6 +375,7 @@ Pull: $head_points_at:$origin" && @@ -256,6 +375,7 @@ Pull: $head_points_at:$origin" &&
git-read-tree -m -u -v HEAD HEAD
esac
fi
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"

trap - exit


Loading…
Cancel
Save