From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
Here is the current status of the multi-head download support.
[PATCH 1/3] Start adding the $GIT_DIR/remotes/ support.
[PATCH 2/3] Multi-head fetch.
[PATCH 3/3] Update git-pull to match updated git-fetch.
The first one in the series adds support for the long promised
$GIT_DIR/remotes/ files; they use the following format:
$ cat $GIT_DIR/remotes/www
URL: http://www.kernel.org/pub/scm/git/git.git/
Pull: master:ko-master pu:ko-pu
$ cat $GIT_DIR/remotes/mko
URL: master.kernel.org:/pub/scm/git/git.git/
Pull: master:ko-master pu:ko-pu
Push: master:master pu:pu foo:bar
The idea is that you should be able to say:
$ git push mko ;# push our master and pu to the same
# name, foo to .git/refs/heads/bar.
$ git push mko pu:refs/heads/testing
# instead of pushing to the usual ref,
# push our pu to refs/heads/testing,
# this time only.
$ git fetch www ;# get their master and pu to our ko-master
# and ko-pu.
$ git pull www bar
# instead of fetching from usual ref,
# fetch from there bar, and merge with it.
Having a Push: entry that uses anything but hostname:/path or
just local directory as its URL does not make any sense at this
moment, because git native send-pack transport is the only one
that is supported for push, as before.
Note that using a $GIT_DIR/branches/ file as a short-cut is
still supported, for both upload and download.
The second one extends "git fetch" to allow pulling from more
than one head. Earlier, "git fetch <remote>" with shorthand
form (i.e. a file $GIT_DIR/branches/<remote> exists and
describes the URL) always overwrote
$GIT_DIR/refs/heads/<remote>, but it does the "reverse push",
meaning it does not overwrite when the update does not result in
a fast-forward merge with the existing value of the reference.
The above "git fetch www" example would fetch both their master
and pu, and tries to fast forward our ko-master and ko-pu with
them. When fast forwarding fails, currently it just stops and
does not try to download all the heads. This may need to
change. The fetch results, whether they update the local
refs/heads/ hierarchy by fast forwarding or not, will be also
stored in $GIT_DIR/FETCH_HEAD.
Note that the format of this file has changed to accomodate
multiple heads. I've checked that neither Cogito nor StGIT
looks at this file, so this change should hopefully be a safe
one.
The third one is to update the "git pull" to prevent people from
abusing the extended "git fetch" and attempt to create a king
ghidorah. "git resolve" has not been updated to do anything but
two-head merge yet, so when you say "git pull www master pu rc",
it would run "git fetch www master pu rc" but stops without
running the resolving step. While it would not make much sense
xsto do an octopus merge against master, pu and rc from git.git
repository, doing an octopus would probably make a lot more
sense when pulling from a repository like what Jeff Garzik has,
so that would be an interesting future project.
I have tested the very basic form of the above works reasonably
well, but there probably are bugs, maybe attempting to pull
heads to a wrong local heads, or something silly like that.
Please handle it with care. It will stay in "pu" branch for
now.
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
Traditionally, fetch takes these forms:
$ git fetch <remote>
$ git fetch <remote> <head>
$ git fetch <remote> tag <tag>
This patch updates it to take
$ git fetch <remote> <refspec>...
where:
- A <refspec> of form "<src>:<dst>" is to fetch the objects
needed for the remote ref that matches <src>, and if <dst>
is not empty, store it as a local <dst>.
- "tag" followed by <next> is just an old way of saying
"refs/tags/<next>:refs/tags/<next>"; this mimics the
current behaviour of the third form above and means "fetch
that tag and store it under the same name".
- A single token <refspec> without colon is a shorthand for
"<refspec>:" That is, "fetch that ref but do not store
anywhere".
- when there is no <refspec> specified
- if <remote> is the name of a file under $GIT_DIR/remotes/
(i.e. a new-style shorthand), then it is the same as giving
the <refspec>s listed on Pull: line in that file.
- if <remote> is the name of a file under $GIT_DIR/branches/
(i.e. an old-style shorthand, without trailing path), then it
is the same as giving a single <refspec>
"<remote-name>:refs/heads/<remote>" on the command line, where
<remote-name> is the remote branch name (defaults to HEAD, but
can be overridden by .git/branches/<remote> file having the
URL fragment notation). That is, "fetch that branch head and
store it in refs/heads/<remote>".
- otherwise, it is the same as giving a single <refspec>
that is "HEAD:".
The SHA1 object names of fetched refs are stored in FETCH_HEAD,
one name per line.
Signed-off-by: Junio C Hamano <redacted>
---
git-fetch-script | 154 ++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 108 insertions(+), 46 deletions(-)
7dbfeb149dfc049015f27f15dd9135fd15d5f99f
@@ -1,54 +1,116 @@ #!/bin/sh # . git-sh-setup-script || die "Not a git archive"-. git-parse-remote "$@"-merge_repo="$_remote_repo"-merge_head="$_remote_head"-merge_store="$_remote_store"--TMP_HEAD="$GIT_DIR/TMP_HEAD"--case "$merge_repo" in-http://* | https://*)- if [ -n "$GIT_SSL_NO_VERIFY" ]; then- curl_extra_args="-k"- fi- _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' &&- _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" &&- head=$(curl -nsf $curl_extra_args "$merge_repo/$merge_head") &&- expr "$head" : "$_x40\$" >/dev/null || {- echo >&2 "Failed to fetch $merge_head from $merge_repo"- exit 1- }- echo Fetching "$merge_head" using http- git-http-pull -v -a "$head" "$merge_repo/" || exit- ;;-rsync://*)- rsync -L "$merge_repo/$merge_head" "$TMP_HEAD" || exit 1- head=$(git-rev-parse TMP_HEAD)- rm -f "$TMP_HEAD"- rsync -avz --ignore-existing "$merge_repo/objects/" "$GIT_OBJECT_DIRECTORY/"- ;;-*)- head=$(git-fetch-pack "$merge_repo" "$merge_head")- if h=`expr "$head" : '\([^ ][^ ]*\) '`+. git-parse-remote-script+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"++remote_nick="$1"+remote=$(get_remote_url "$@")+refs=+rref=+prev_was_literal_tag=+rsync_slurped_objects=+: >$GIT_DIR/FETCH_HEAD++fast_forward_local () {++ # NEEDSWORK: probably use the same cmpxchg protocol here...+ echo "$2" >"$GIT_DIR/$1.lock"+ if test -f "$GIT_DIR/$1" then- head=$h+ local=$(git-rev-parse --verify "$1^0") &&+ mb=$(git-merge-base "$local" "$2") &&+ test "$mb" = "$local" || {+ rm -f "$GIT_DIR/$1.lock"+ die "$1 does not fast forward to $4 from $3.";+ } fi- ;;-esac || exit 1--git-rev-parse --verify "$head" > /dev/null || exit 1+ mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1"+}-case "$merge_store" in-'')- ;;-*)- echo "$head" > "$GIT_DIR/$merge_store"-esac &&+for ref in $(get_remote_refs_for_fetch "$@")+do+ if test "$prev_was_literal_tag"+ then+ ref="refs/tags/${ref}:refs/tags/${ref}"+ prev_was_literal_tag=+ else+ case "$ref" in+ tag)+ prev_was_literal_tag=yes+ continue+ ;;+ esac+ fi-# FETCH_HEAD is fed to git-resolve-script which will eventually be-# passed to git-commit-tree as one of the parents. Make sure we do-# not give a tag object ID.+ refs="$refs $ref"-git-rev-parse "$head^0" >"$GIT_DIR/FETCH_HEAD"+ # These are relative path from $GIT_DIR, typically starting at refs/+ # but may be HEAD+ remote_name=$(expr "$ref" : '\([^:]*\):')+ local_name=$(expr "$ref" : '[^:]*:\(.*\)')++ rref="$rref $remote_name"++ # There are transports that can fetch only one head at a time...+ case "$remote" in+ http://* | https://*)+ if [ -n "$GIT_SSL_NO_VERIFY" ]; then+ curl_extra_args="-k"+ fi+ head=$(curl -nsf $curl_extra_args "$remote/$remote_name") &&+ expr "$head" : "$_x40\$" >/dev/null ||+ die "Failed to fetch $remote_name from $remote"+ echo Fetching "$remote_name from $remote" using http+ git-http-pull -v -a "$head" "$remote/" || exit+ ;;+ rsync://*)+ TMP_HEAD="$GIT_DIR/TMP_HEAD"+ rsync -L "$remote/$remote_name" "$TMP_HEAD" || exit 1+ head=$(git-rev-parse TMP_HEAD)+ rm -f "$TMP_HEAD"+ test "$rsync_slurped_objects" || {+ rsync -avz --ignore-existing "$remote/objects/" \+ "$GIT_OBJECT_DIRECTORY/" || exit+ rsync_slurped_objects=t+ }+ ;;+ *)+ # We will do git native transport at one go.+ continue ;;+ esac+ head=$(git-rev-parse --verify "$head^0") || exit+ echo "$head $remote_name from $remote_nick" >>$GIT_DIR/FETCH_HEAD+ case "$local_name" in '') continue ;; esac++ # We are storing the head locally. Make sure that it is+ # a fast forward (aka "reverse push").+ fast_forward_local "$local_name" "$head" "$remote" "$remote_name"++done++case "$remote" in+http://* | https://* | rsync://* )+ ;; # we are already done.+*)+ git-fetch-pack "$remote" $rref |+ while read sha1 remote_name+ do+ found=+ for ref in $refs+ do+ case "$ref" in+ $remote_name:*)+ found="$ref"+ break ;;+ esac+ done+ head=$(git-rev-parse --verify "$sha1^0") || exit+ echo "$head $remote_name from $remote_nick" >>$GIT_DIR/FETCH_HEAD+ case "$found" in '' | *:) continue ;; esac+ local=$(expr "$found" : '[^:]*:\(.*\)')+ fast_forward_local "$local" "$sha1" "$remote" "$remote_name"+ done+ ;;+esac
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
All the necessary parsing code is in git-parse-remote-script;
update git-push-script to use it.
Signed-off-by: Junio C Hamano <redacted>
---
Makefile | 2 -
git-parse-remote-script | 122 +++++++++++++++++++++++++++++++++++++++++++++++
git-push-script | 28 ++---------
3 files changed, 129 insertions(+), 23 deletions(-)
create mode 100755 git-parse-remote-script
f8892bf17675056cd18a252d3bc4e4ba381fb3bc
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
This retires the git-parse-remote script, and allows pull to fetch
from multiple remote references. There is no support for resolving
more than two heads, so that would be next.
Signed-off-by: Junio C Hamano <redacted>
---
Makefile | 2 +
git-parse-remote | 79 ------------------------------------------------------
git-pull-script | 14 ++++++----
3 files changed, 10 insertions(+), 85 deletions(-)
delete mode 100755 git-parse-remote
3a071a02828c71bbfdc2749d25814906cd9c8b18
@@ -1,79 +0,0 @@-: To be included in git-pull and git-fetch scripts.--# A remote repository can be specified on the command line-# in one of the following formats:-#-# <repo>-# <repo> <head>-# <repo> tag <tag>-#-# where <repo> could be one of:-#-# a URL (including absolute or local pathname)-# a short-hand-# a short-hand followed by a trailing path-#-# A short-hand <name> has a corresponding file $GIT_DIR/branches/<name>,-# whose contents is a URL, possibly followed by a URL fragment #<head>-# to name the default branch on the remote side to fetch from.--_remote_repo= _remote_store= _remote_head= _remote_name=--case "$1" in-*:* | /* | ../* | ./* )- _remote_repo="$1"- ;;-* )- # otherwise, it is a short hand.- case "$1" in- */*)- # a short-hand followed by a trailing path- _token=$(expr "$1" : '\([^/]*\)/')- _rest=$(expr "$1" : '[^/]*\(/.*\)$')- ;;- *)- _token="$1"- _rest=- _remote_store="refs/heads/$_token"- ;;- esac- test -f "$GIT_DIR/branches/$_token" ||- die "No such remote branch: $_token"-- _remote_repo=$(cat "$GIT_DIR/branches/$_token")"$_rest"- ;;-esac--case "$_remote_repo" in-*"#"*)- _remote_head=`expr "$_remote_repo" : '.*#\(.*\)$'`- _remote_repo=`expr "$_remote_repo" : '\(.*\)#'`- ;;-esac--_remote_name=$(echo "$_remote_repo" | sed 's|\.git/*$||')--case "$2" in-tag)- _remote_name="tag '$3' of $_remote_name"- _remote_head="refs/tags/$3"- _remote_store="$_remote_head"- ;;-?*)- # command line specified a head explicitly; do not- # store the fetched head as a branch head.- _remote_name="head '$2' of $_remote_name"- _remote_head="refs/heads/$2"- _remote_store=''- ;;-'')- case "$_remote_head" in- '')- _remote_head=HEAD ;;- *)- _remote_name="head '$_remote_head' of $_remote_name"- _remote_head="refs/heads/$_remote_head"- ;;- esac- ;;-esac
Isn't this mixing two kinds of information:
1) Some default/persistent mapping of local to remote heads
2) The default heads which are pulled/pushed when only giving the remote, like
in:
$ git push mko ;# push our master and pu to the same
# name, foo to .git/refs/heads/bar.
I think it makes sense to be able to store mappings without adding the head to
the default group of heads pulled.
Can we put the default pull/push actions in separate lines, like
$ cat $GIT_DIR/remotes/mko
URL: master.kernel.org:/pub/scm/git/git.git/
Pull: master:ko-master pu:ko-pu mylocal:myremote
Push: master:master pu:pu foo:bar
Default-Pull: master pu
Default-Push: master pu foo
$ git push mko pu:refs/heads/testing
# instead of pushing to the usual ref,
# push our pu to refs/heads/testing,
# this time only.
With a command (push/fetch/pull) giving an explicit local/remote mapping, it
would be cool to automatically add the given mapping to the remotes/ file if
there is no push-mapping for pu yet, so that you can have the same later with
only
git push mko pu
And finally, it would be nice to specify a default mapping for arbitrary heads
$ cat $GIT_DIR/remotes/www
URL: http://www.kernel.org/pub/scm/git/git.git/
Pull: www#*:*
Such that
git fetch www#pu
will fetch head pu into local head .refs/heads/www#pu
Josef
PS: I know I should provide patches for my proposals. But let discuss them
first.