From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
Now I think all the pieces are glued together. I'll send the
following patches, which are already in the proposed updates
branch.
I sent an early WIP for some of them, but the patches are
reorganized for easier review and applies on top of the current
"master" branch.
This series consists of the following:
Start adding the $GIT_DIR/remotes/ support.
Multi-head fetch.
Retire git-parse-remote.
Infamous 'octopus merge'
Make "git pull" and "git fetch" default to origin
Use .git/remote/origin, not .git/branches/origin.
The issues I discussed in "Multi-head fetches, pulls, and a King
Ghidorah" are addressed there, and the examples in "MyGITDay"
message I sent earlier should work with these patches. I'll
work on adding some documentation, maybe in howto or tutorial
format, over the weekend.
-jc
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>
---
** sorry I made a mistake of letting git send-email to
** send nonsense messages ...
Makefile | 2 -
git-parse-remote-script | 144 +++++++++++++++++++++++++++++++++++++++++++++++
git-push-script | 28 ++-------
3 files changed, 151 insertions(+), 23 deletions(-)
create mode 100755 git-parse-remote-script
284ba9655aedbdaaa897fdcc6aabae97de8d99d1
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
Now multi-head fetch is complete, let's migrate the
default configuration for new repositories created with
the "git clone" command.
The original $GIT_DIR/branches is not deprecated yet, but create
remotes directory by default from the templates as well.
Signed-off-by: Junio C Hamano <redacted>
---
git-clone-script | 8 +++++---
templates/remotes-- | 1 +
2 files changed, 6 insertions(+), 3 deletions(-)
create mode 100644 templates/remotes--
8ad3e7efb406377f6903171638c70b7d566254c5
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, with a comment to describe where it came from.
This is later used by "git resolve" and "git octopus".
Signed-off-by: Junio C Hamano <redacted>
---
git-fetch-script | 193 ++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 149 insertions(+), 44 deletions(-)
d947289005049c67f1dded517a1a6a8843fcf4a3
@@ -1,54 +1,159 @@ #!/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/"- ;;+. 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"++append=+case "$#" in+0)+ die "Where do you want to fetch from?" ;; *)- head=$(git-fetch-pack "$merge_repo" "$merge_head")- if h=`expr "$head" : '\([^ ][^ ]*\) '`+ case "$1" in+ -a|--a|--ap|--app|--appe|--appen|--append)+ append=t+ shift ;;+ esac+esac+remote_nick="$1"+remote=$(get_remote_url "$@")+refs=+rref=+rsync_slurped_objects=++if test "" = "$append"+then+ : >$GIT_DIR/FETCH_HEAD+fi++append_fetch_head () {+ head_="$1"+ remote_="$2"+ remote_name_="$3"+ remote_nick_="$4"+ local_name_="$5"++ # 2.6.11-tree tag would not be happy to be fed to resolve.+ if git-cat-file commit "$head_" >/dev/null 2>&1+ then+ head_=$(git-rev-parse --verify "$head_^0") || exit+ note_="$head_ $remote_name_ from $remote_nick_"+ echo "$note_" >>$GIT_DIR/FETCH_HEAD+ echo >&2 "* committish: $note_"+ else+ echo >&2 "* non-commit: $note_"+ fi+ if test "$local_name_" != ""+ then+ # 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_"+ fi+}++fast_forward_local () {+ case "$1" in+ refs/tags/*)+ # Tags need not be pointing at commits so there+ # is no way to guarantee "fast-forward" anyway.+ echo "$2" >"$GIT_DIR/$1" ;;+ refs/heads/*)+ # NEEDSWORK: 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") &&+ case "$2,$mb" in+ $local,*)+ echo >&2 "* $1: same as $4"+ echo >&2 " from $3"+ ;;+ *,$local)+ echo >&2 "* $1: fast forward to $4"+ echo >&2 " from $3"+ ;;+ *)+ false+ ;;+ esac || {+ mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1.remote"+ echo >&2 "* $1: does not fast forward to $4"+ echo >&2 " from $3; leaving it in '$1.remote'"+ }+ else+ echo >&2 "* $1: storing $4"+ echo >&2 " from $3." fi+ test -f "$GIT_DIR/$1.lock" &&+ mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1" ;;-esac || exit 1--git-rev-parse --verify "$head" > /dev/null || exit 1+ esac+}-case "$merge_store" in-'')+for ref in $(get_remote_refs_for_fetch "$@")+do+ refs="$refs $ref"++ # 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 with just one call later.+ continue ;;+ esac++ append_fetch_head "$head" "$remote" "$remote_name" "$remote_nick" "$local_name"++done++case "$remote" in+http://* | https://* | rsync://* )+ ;; # we are already done. *)- echo "$head" > "$GIT_DIR/$merge_store"-esac &&--# 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.--git-rev-parse "$head^0" >"$GIT_DIR/FETCH_HEAD"+ 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++ local_name=$(expr "$found" : '[^:]*:\(.*\)')+ append_fetch_head "$sha1" "$remote" "$remote_name" "$remote_nick" "$local_name"+ done+ ;;+esac
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
Update git-pull to match updated git-fetch and allow pull to
fetch from multiple remote references. There is no support for
resolving more than two heads, which will be done with "git
octopus".
Update "git ls-remote" to use git-parse-remote-script.
Signed-off-by: Junio C Hamano <redacted>
---
Makefile | 2 +
git-ls-remote-script | 4 +--
git-parse-remote | 79 --------------------------------------------------
git-pull-script | 14 ++++++---
4 files changed, 12 insertions(+), 87 deletions(-)
delete mode 100755 git-parse-remote
07c44b628e605c4e7fead8ff9e191711b7abe301
@@ -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
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
Amos Waterland sent in a patch for the pre-multi-head aware
version of "git pull" to do this, but the code changed quite a
bit since then. If there is no argument given to pull from, and
if "origin" makes sense, default to fetch/pull from "origin"
instead of barfing.
[jc: besides, the patch by Amos broke the non-default case where
explicit refspecs are specified, and did not make sure we know
what "origin" means before defaulting to it.]
Signed-off-by: Junio C Hamano <redacted>
---
git-fetch-script | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
6d8fcdbf772f4fc4ec7ae67630b52bc8a07d7b6d
@@ -8,7 +8,10 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x4 append= case "$#" in 0)- die "Where do you want to fetch from?" ;;+ test -f "$GIT_DIR/branches/origin" ||+ test -f "$GIT_DIR/remotes/origin" ||+ die "Where do you want to fetch from?"+ set origin ;; *) case "$1" in -a|--a|--ap|--app|--appe|--appen|--append)
From: Junio C Hamano <hidden> Date: 2016-06-15 22:42:04
This script uses the list of heads and their origin multi-head "git
fetch" left in the $GIT_DIR/FETCH_HEAD file, and makes an octopus
merge on top of the current HEAD using them.
The implementation tries to be strict for the sake of safety. It
insists that your working tree is clean (no local changes) and matches
the HEAD, and when any of the merged heads does not automerge, the
whole process is aborted and tries to rewind your working tree is to
the original state.
Signed-off-by: Junio C Hamano <redacted>
---
Makefile | 1 +
git-octopus-script | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+), 0 deletions(-)
create mode 100755 git-octopus-script
325918bdc88517826fb7f14fba87a34b92ea2b52
@@ -0,0 +1,103 @@+#!/bin/sh+#+# Copyright (c) 2005 Junio C Hamano+#+# Resolve two or more trees recorded in $GIT_DIR/FETCH_HEAD.+#+. git-sh-setup-script || die "Not a git archive"++usage () {+ die "usage: git octopus"+}++# Sanity check the heads early.+while read SHA1 REPO+do+ test $(git-cat-file -t $SHA1) = "commit" ||+ die "$REPO given to octopus is not a commit"+done <"$GIT_DIR/FETCH_HEAD"++head=$(git-rev-parse --verify HEAD) || exit++git-update-cache --refresh ||+ die "Your working tree is dirty."+test "$(git-diff-cache --cached "$head")" = "" ||+ die "Your working tree does not match HEAD."++# MRC is the current "merge reference commit"+# MRT is the current "merge result tree"++MRC=$head MSG= PARENT="-p $head"+MRT=$(git-write-tree)+CNT=1 ;# counting our head+NON_FF_MERGE=0+while read SHA1 REPO+do+ common=$(git-merge-base $MRC $SHA1) ||+ die "Unable to find common commit with $SHA1 from $REPO"++ if test "$common" = $SHA1+ then+ echo "Already up-to-date: $REPO"+ continue+ fi++ CNT=`expr $CNT + 1`+ PARENT="$PARENT -p $SHA1"+ MSG="$MSG+ $REPO"++ if test "$common,$NON_FF_MERGE" = "$MRC,0"+ then+ # The first head being merged was a fast-forward.+ # Advance MRC to the head being merged, and use that+ # tree as the intermediate result of the merge.+ # We still need to count this as part of the parent set.++ echo "Fast forwarding to: $REPO"+ git-read-tree -u -m $head $SHA1 || exit+ MRC=$SHA1 MRT=$(git-write-tree)+ continue+ fi++ NON_FF_MERGE=1++ echo "Trying simple merge with $REPO"+ git-read-tree -u -m $common $MRT $SHA1 || exit+ next=$(git-write-tree 2>/dev/null)+ if test $? -ne 0+ then+ echo "Simple merge did not work, trying automatic merge."+ git-merge-cache -o git-merge-one-file-script -a || {+ git-read-tree --reset "$head"+ git-checkout-tree -f -q -u -a+ die "Automatic merge failed; should not be doing Octopus"+ }+ next=$(git-write-tree 2>/dev/null)+ fi+ MRC=$common+ MRT=$next+done <"$GIT_DIR/FETCH_HEAD"++# Just to be careful in case the user feeds nonsense to us.+case "$CNT" in+1)+ echo "No changes."+ exit 0 ;;+2)+ echo "Not an Octopus; making an ordinary commit."+ MSG="Merge "`expr "$MSG" : '. \(.*\)'` ; # remove LF and TAB+ ;;+*)+ # In an octopus, the original head is just one of the equals,+ # so we should list it as such.+ HEAD_LINK=`readlink "$GIT_DIR/HEAD"`+ MSG="Octopus merge of the following:++ $HEAD_LINK from .$MSG"+ ;;+esac+result_commit=$(echo "$MSG" | git-commit-tree $MRT $PARENT)+echo "Committed merge $result_commit"+echo $result_commit >"$GIT_DIR"/HEAD+git-diff-tree -p $head $result_commit | git-apply --stat