# Copyright (c) 2005 Junio C Hamano
#
-USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
+USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s <strategy>] [-m=<merge-message>] <commit>+'
SUBDIRECTORY_OK=Yes
. git-sh-setup
all_strategies='recur recursive octopus resolve stupid ours subtree'
default_twohead_strategies='recursive'
default_octopus_strategies='octopus'
-no_trivial_merge_strategies='ours subtree'
+no_fast_forward_strategies='subtree ours'
+no_trivial_strategies='recursive recur subtree ours'
use_strategies=
-index_merge=t
+allow_fast_forward=t
+allow_trivial_merge=t
dropsave() {
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
savestate() {
# Stash away any local modifications.
- git-diff-index -z --name-only $head |
+ git diff-index -z --name-only $head |
cpio -0 -o >"$GIT_DIR/MERGE_SAVE"
}
then
git reset --hard $head >/dev/null
cpio -iuv <"$GIT_DIR/MERGE_SAVE"
- git-update-index --refresh >/dev/null
+ git update-index --refresh >/dev/null
fi
}
squash_message () {
echo Squashed commit of the following:
echo
- git-log --no-merges ^"$head" $remote
+ git log --no-merges ^"$head" $remoteheads
}
finish () {
echo "No merge message -- not updating HEAD"
;;
*)
- git-update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
+ git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
+ git gc --auto
;;
esac
;;
'')
;;
?*)
- case "$no_summary" in
- '')
- git-diff-tree --stat --summary -M "$head" "$1"
- ;;
- esac
+ if test "$show_diffstat" = t
+ then
+ # We want color (if set), but no pager
+ GIT_PAGER='' git diff --stat --summary -M "$head" "$1"
+ fi
;;
esac
+
+ # Run a post-merge hook
+ if test -x "$GIT_DIR"/hooks/post-merge
+ then
+ case "$squash" in
+ t)
+ "$GIT_DIR"/hooks/post-merge 1
+ ;;
+ '')
+ "$GIT_DIR"/hooks/post-merge 0
+ ;;
+ esac
+ fi
}
merge_name () {
remote="$1"
- rh=$(git-rev-parse --verify "$remote^0" 2>/dev/null) || return
- bh=$(git-show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
+ rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
+ bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
if test "$rh" = "$bh"
then
echo "$rh branch '$remote' of ."
elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
- git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
+ git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
then
echo "$rh branch '$truname' (early part) of ."
elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
fi
}
-case "$#" in 0) usage ;; esac
-
-have_message=
-while case "$#" in 0) break ;; esac
-do
+parse_option () {
case "$1" in
-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
--no-summa|--no-summar|--no-summary)
- no_summary=t ;;
+ show_diffstat=false ;;
+ --summary)
+ show_diffstat=t ;;
--sq|--squ|--squa|--squas|--squash)
- squash=t no_commit=t ;;
+ allow_fast_forward=t squash=t no_commit=t ;;
+ --no-sq|--no-squ|--no-squa|--no-squas|--no-squash)
+ allow_fast_forward=t squash= no_commit= ;;
+ --c|--co|--com|--comm|--commi|--commit)
+ allow_fast_forward=t squash= no_commit= ;;
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
- no_commit=t ;;
+ allow_fast_forward=t squash= no_commit=t ;;
+ --ff)
+ allow_fast_forward=t squash= no_commit= ;;
+ --no-ff)
+ allow_fast_forward=false squash= no_commit= ;;
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
--strateg=*|--strategy=*|\
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
have_message=t
;;
-*) usage ;;
- *) break ;;
+ *) return 1 ;;
esac
shift
+ args_left=$#
+}
+
+parse_config () {
+ while test $# -gt 0
+ do
+ parse_option "$@" || usage
+ while test $args_left -lt $#
+ do
+ shift
+ done
+ done
+}
+
+test $# != 0 || usage
+
+have_message=
+
+if branch=$(git-symbolic-ref -q HEAD)
+then
+ mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions")
+ if test -n "$mergeopts"
+ then
+ parse_config $mergeopts
+ fi
+fi
+
+while parse_option "$@"
+do
+ while test $args_left -lt $#
+ do
+ shift
+ done
done
+if test -z "$show_diffstat"; then
+ test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
+ test -z "$show_diffstat" && show_diffstat=t
+fi
+
# This could be traditional "merge <msg> HEAD <commit>..." and the
# way we can tell it is to see if the second token is HEAD, but some
# people might have misused the interface and used a committish that
# have "-m" so it is an additional safety measure to check for it.
if test -z "$have_message" &&
- second_token=$(git-rev-parse --verify "$2^0" 2>/dev/null) &&
- head_commit=$(git-rev-parse --verify "HEAD" 2>/dev/null) &&
+ second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) &&
+ head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) &&
test "$second_token" = "$head_commit"
then
merge_msg="$1"
shift
head_arg="$1"
shift
-elif ! git-rev-parse --verify HEAD >/dev/null 2>&1
+elif ! git rev-parse --verify HEAD >/dev/null 2>&1
then
# If the merged head is a valid one there is no reason to
# forbid "git merge" into a branch yet to be born. We do
rh=$(git rev-parse --verify "$1^0") ||
die "$1 - not something we can merge"
- git-update-ref -m "initial pull" HEAD "$rh" "" &&
- git-read-tree --reset -u HEAD
+ git update-ref -m "initial pull" HEAD "$rh" "" &&
+ git read-tree --reset -u HEAD
exit
else
merge_name=$(for remote
do
merge_name "$remote"
- done | git-fmt-merge-msg
+ done | git fmt-merge-msg
)
merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
fi
-head=$(git-rev-parse --verify "$head_arg"^0) || usage
+head=$(git rev-parse --verify "$head_arg"^0) || usage
# All the rest are remote heads
test "$#" = 0 && usage ;# we need at least one remote head.
remoteheads=
for remote
do
- remotehead=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
+ remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) ||
die "$remote - not something we can merge"
remoteheads="${remoteheads}$remotehead "
eval GITHEAD_$remotehead='"$remote"'
'')
case "$#" in
1)
- var="`git-config --get pull.twohead`"
+ var="`git config --get pull.twohead`"
if test -n "$var"
then
use_strategies="$var"
use_strategies="$default_twohead_strategies"
fi ;;
*)
- var="`git-config --get pull.octopus`"
+ var="`git config --get pull.octopus`"
if test -n "$var"
then
use_strategies="$var"
for s in $use_strategies
do
- for nt in $no_trivial_merge_strategies
+ for ss in $no_fast_forward_strategies
do
case " $s " in
- *" $nt "*)
- index_merge=f
+ *" $ss "*)
+ allow_fast_forward=f
+ break
+ ;;
+ esac
+ done
+ for ss in $no_trivial_strategies
+ do
+ case " $s " in
+ *" $ss "*)
+ allow_trivial_merge=f
break
;;
esac
case "$#" in
1)
- common=$(git-merge-base --all $head "$@")
+ common=$(git merge-base --all $head "$@")
;;
*)
- common=$(git-show-branch --merge-base $head "$@")
+ common=$(git show-branch --merge-base $head "$@")
;;
esac
echo "$head" >"$GIT_DIR/ORIG_HEAD"
-case "$index_merge,$#,$common,$no_commit" in
-f,*)
- # We've been told not to try anything clever. Skip to real merge.
- ;;
+case "$allow_fast_forward,$#,$common,$no_commit" in
?,*,'',*)
# No common ancestors found. We need a real merge.
;;
finish_up_to_date "Already up-to-date."
exit 0
;;
-?,1,"$head",*)
+t,1,"$head",*)
# Again the most common case of merging one remote.
- echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
- git-update-index --refresh 2>/dev/null
+ echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
+ git update-index --refresh 2>/dev/null
msg="Fast forward"
if test -n "$have_message"
then
msg="$msg (no commit created; -m option ignored)"
fi
- new_head=$(git-rev-parse --verify "$1^0") &&
- git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
+ new_head=$(git rev-parse --verify "$1^0") &&
+ git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
finish "$new_head" "$msg" || exit
dropsave
exit 0
?,1,*,)
# We are not doing octopus, not fast forward, and have only
# one common.
- git-update-index --refresh 2>/dev/null
- case " $use_strategies " in
- *' recursive '*|*' recur '*)
- : run merge later
- ;;
- *)
+ git update-index --refresh 2>/dev/null
+ case "$allow_trivial_merge" in
+ t)
# See if it is really trivial.
git var GIT_COMMITTER_IDENT >/dev/null || exit
echo "Trying really trivial in-index merge..."
- if git-read-tree --trivial -m -u -v $common $head "$1" &&
- result_tree=$(git-write-tree)
+ if git read-tree --trivial -m -u -v $common $head "$1" &&
+ result_tree=$(git write-tree)
then
echo "Wonderful."
result_commit=$(
printf '%s\n' "$merge_msg" |
- git-commit-tree $result_tree -p HEAD -p "$1"
+ git commit-tree $result_tree -p HEAD -p "$1"
) || exit
finish "$result_commit" "In-index merge"
dropsave
up_to_date=t
for remote
do
- common_one=$(git-merge-base --all $head $remote)
+ common_one=$(git merge-base --all $head $remote)
if test "$common_one" != "$remote"
then
up_to_date=f
if test "$exit" -eq 1
then
cnt=`{
- git-diff-files --name-only
- git-ls-files --unmerged
+ git diff-files --name-only
+ git ls-files --unmerged
} | wc -l`
if test $best_cnt -le 0 -o $cnt -le $best_cnt
then
}
# Automerge succeeded.
- result_tree=$(git-write-tree) && break
+ result_tree=$(git write-tree) && break
done
# If we have a resulting tree, that means the strategy module
# auto resolved the merge cleanly.
if test '' != "$result_tree"
then
- parents=$(git-show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
- result_commit=$(printf '%s\n' "$merge_msg" | git-commit-tree $result_tree $parents) || exit
+ if test "$allow_fast_forward" = "t"
+ then
+ parents=$(git show-branch --independent "$head" "$@")
+ else
+ parents=$(git rev-parse "$head" "$@")
+ fi
+ parents=$(echo "$parents" | sed -e 's/^/-p /')
+ result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
finish "$result_commit" "Merge made by $wt_strategy."
dropsave
exit 0
sed -e 's/^[^ ]* / /' |
uniq
} >>"$GIT_DIR/MERGE_MSG"
- if test -d "$GIT_DIR/rr-cache"
- then
- git-rerere
- fi
+ git rerere
die "Automatic merge failed; fix conflicts and then commit the result."
fi