Initial class construction
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,348 @@
|
||||
#!/bin/sh
|
||||
|
||||
USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
|
||||
LONG_USAGE='git bisect help
|
||||
print this long help message.
|
||||
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
|
||||
[--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
|
||||
reset bisect state and start bisection.
|
||||
git bisect (bad|new) [<rev>]
|
||||
mark <rev> a known-bad revision/
|
||||
a revision after change in a given property.
|
||||
git bisect (good|old) [<rev>...]
|
||||
mark <rev>... known-good revisions/
|
||||
revisions before change in a given property.
|
||||
git bisect terms [--term-good | --term-bad]
|
||||
show the terms used for old and new commits (default: bad, good)
|
||||
git bisect skip [(<rev>|<range>)...]
|
||||
mark <rev>... untestable revisions.
|
||||
git bisect next
|
||||
find next bisection to test and check it out.
|
||||
git bisect reset [<commit>]
|
||||
finish bisection search and go back to commit.
|
||||
git bisect (visualize|view)
|
||||
show bisect status in gitk.
|
||||
git bisect replay <logfile>
|
||||
replay bisection log.
|
||||
git bisect log
|
||||
show bisect log.
|
||||
git bisect run <cmd>...
|
||||
use <cmd>... to automatically bisect.
|
||||
|
||||
Please use "git help bisect" to get the full man page.'
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
_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"
|
||||
TERM_BAD=bad
|
||||
TERM_GOOD=good
|
||||
|
||||
bisect_head()
|
||||
{
|
||||
if test -f "$GIT_DIR/BISECT_HEAD"
|
||||
then
|
||||
echo BISECT_HEAD
|
||||
else
|
||||
echo HEAD
|
||||
fi
|
||||
}
|
||||
|
||||
bisect_autostart() {
|
||||
test -s "$GIT_DIR/BISECT_START" || {
|
||||
gettextln "You need to start by \"git bisect start\"" >&2
|
||||
if test -t 0
|
||||
then
|
||||
# TRANSLATORS: Make sure to include [Y] and [n] in your
|
||||
# translation. The program will only accept English input
|
||||
# at this point.
|
||||
gettext "Do you want me to do it for you [Y/n]? " >&2
|
||||
read yesno
|
||||
case "$yesno" in
|
||||
[Nn]*)
|
||||
exit ;;
|
||||
esac
|
||||
bisect_start
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
bisect_start() {
|
||||
git bisect--helper --bisect-start $@ || exit
|
||||
|
||||
#
|
||||
# Change state.
|
||||
# In case of mistaken revs or checkout error, or signals received,
|
||||
# "bisect_auto_next" below may exit or misbehave.
|
||||
# We have to trap this to be able to clean up using
|
||||
# "bisect_clean_state".
|
||||
#
|
||||
trap 'git bisect--helper --bisect-clean-state' 0
|
||||
trap 'exit 255' 1 2 3 15
|
||||
|
||||
#
|
||||
# Check if we can proceed to the next bisect state.
|
||||
#
|
||||
get_terms
|
||||
bisect_auto_next
|
||||
|
||||
trap '-' 0
|
||||
}
|
||||
|
||||
bisect_skip() {
|
||||
all=''
|
||||
for arg in "$@"
|
||||
do
|
||||
case "$arg" in
|
||||
*..*)
|
||||
revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
|
||||
*)
|
||||
revs=$(git rev-parse --sq-quote "$arg") ;;
|
||||
esac
|
||||
all="$all $revs"
|
||||
done
|
||||
eval bisect_state 'skip' $all
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
bisect_autostart
|
||||
state=$1
|
||||
git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
|
||||
get_terms
|
||||
case "$#,$state" in
|
||||
0,*)
|
||||
die "Please call 'bisect_state' with at least one argument." ;;
|
||||
1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
|
||||
bisected_head=$(bisect_head)
|
||||
rev=$(git rev-parse --verify "$bisected_head") ||
|
||||
die "$(eval_gettext "Bad rev input: \$bisected_head")"
|
||||
git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
|
||||
git bisect--helper --check-expected-revs "$rev" ;;
|
||||
2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
|
||||
shift
|
||||
hash_list=''
|
||||
for rev in "$@"
|
||||
do
|
||||
sha=$(git rev-parse --verify "$rev^{commit}") ||
|
||||
die "$(eval_gettext "Bad rev input: \$rev")"
|
||||
hash_list="$hash_list $sha"
|
||||
done
|
||||
for rev in $hash_list
|
||||
do
|
||||
git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
|
||||
done
|
||||
git bisect--helper --check-expected-revs $hash_list ;;
|
||||
*,"$TERM_BAD")
|
||||
die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_auto_next() {
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD && bisect_next || :
|
||||
}
|
||||
|
||||
bisect_next() {
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
bisect_autostart
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
|
||||
|
||||
# Perform all bisection computation, display and checkout
|
||||
git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
|
||||
res=$?
|
||||
|
||||
# Check if we should exit because bisection is finished
|
||||
if test $res -eq 10
|
||||
then
|
||||
bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD)
|
||||
bad_commit=$(git show-branch $bad_rev)
|
||||
echo "# first $TERM_BAD commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
|
||||
exit 0
|
||||
elif test $res -eq 2
|
||||
then
|
||||
echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
|
||||
good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/$TERM_GOOD-*")
|
||||
for skipped in $(git rev-list refs/bisect/$TERM_BAD --not $good_revs)
|
||||
do
|
||||
skipped_commit=$(git show-branch $skipped)
|
||||
echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
|
||||
done
|
||||
exit $res
|
||||
fi
|
||||
|
||||
# Check for an error in the bisection process
|
||||
test $res -ne 0 && exit $res
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
bisect_visualize() {
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
|
||||
|
||||
if test $# = 0
|
||||
then
|
||||
if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
|
||||
type gitk >/dev/null 2>&1
|
||||
then
|
||||
set gitk
|
||||
else
|
||||
set git log
|
||||
fi
|
||||
else
|
||||
case "$1" in
|
||||
git*|tig) ;;
|
||||
-*) set git log "$@" ;;
|
||||
*) set git "$@" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
|
||||
}
|
||||
|
||||
bisect_replay () {
|
||||
file="$1"
|
||||
test "$#" -eq 1 || die "$(gettext "No logfile given")"
|
||||
test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
|
||||
git bisect--helper --bisect-reset || exit
|
||||
while read git bisect command rev
|
||||
do
|
||||
test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
|
||||
if test "$git" = "git-bisect"
|
||||
then
|
||||
rev="$command"
|
||||
command="$bisect"
|
||||
fi
|
||||
get_terms
|
||||
git bisect--helper --check-and-set-terms "$command" "$TERM_GOOD" "$TERM_BAD" || exit
|
||||
get_terms
|
||||
case "$command" in
|
||||
start)
|
||||
cmd="bisect_start $rev"
|
||||
eval "$cmd" ;;
|
||||
"$TERM_GOOD"|"$TERM_BAD"|skip)
|
||||
git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
|
||||
terms)
|
||||
git bisect--helper --bisect-terms $rev || exit;;
|
||||
*)
|
||||
die "$(gettext "?? what are you talking about?")" ;;
|
||||
esac
|
||||
done <"$file"
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_run () {
|
||||
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
|
||||
|
||||
test -n "$*" || die "$(gettext "bisect run failed: no command provided.")"
|
||||
|
||||
while true
|
||||
do
|
||||
command="$@"
|
||||
eval_gettextln "running \$command"
|
||||
"$@"
|
||||
res=$?
|
||||
|
||||
# Check for really bad run error.
|
||||
if [ $res -lt 0 -o $res -ge 128 ]
|
||||
then
|
||||
eval_gettextln "bisect run failed:
|
||||
exit code \$res from '\$command' is < 0 or >= 128" >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
# Find current state depending on run success or failure.
|
||||
# A special exit code of 125 means cannot test.
|
||||
if [ $res -eq 125 ]
|
||||
then
|
||||
state='skip'
|
||||
elif [ $res -gt 0 ]
|
||||
then
|
||||
state="$TERM_BAD"
|
||||
else
|
||||
state="$TERM_GOOD"
|
||||
fi
|
||||
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
|
||||
res=$?
|
||||
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
|
||||
if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \
|
||||
>/dev/null
|
||||
then
|
||||
gettextln "bisect run cannot continue any more" >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if [ $res -ne 0 ]
|
||||
then
|
||||
eval_gettextln "bisect run failed:
|
||||
'bisect_state \$state' exited with error code \$res" >&2
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null
|
||||
then
|
||||
gettextln "bisect run success"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
bisect_log () {
|
||||
test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
|
||||
cat "$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
get_terms () {
|
||||
if test -s "$GIT_DIR/BISECT_TERMS"
|
||||
then
|
||||
{
|
||||
read TERM_BAD
|
||||
read TERM_GOOD
|
||||
} <"$GIT_DIR/BISECT_TERMS"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
usage ;;
|
||||
*)
|
||||
cmd="$1"
|
||||
get_terms
|
||||
shift
|
||||
case "$cmd" in
|
||||
help)
|
||||
git bisect -h ;;
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
skip)
|
||||
bisect_skip "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
visualize|view)
|
||||
bisect_visualize "$@" ;;
|
||||
reset)
|
||||
git bisect--helper --bisect-reset "$@" ;;
|
||||
replay)
|
||||
bisect_replay "$@" ;;
|
||||
log)
|
||||
bisect_log ;;
|
||||
run)
|
||||
bisect_run "$@" ;;
|
||||
terms)
|
||||
git bisect--helper --bisect-terms "$@" || exit;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
esac
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Tcl ignores the next line -*- tcl -*- \
|
||||
exec wish "$0" -- "$@"
|
||||
|
||||
if { $argc >=2 && [lindex $argv 0] == "--working-dir" } {
|
||||
set workdir [lindex $argv 1]
|
||||
cd $workdir
|
||||
if {[lindex [file split $workdir] end] eq {.git}} {
|
||||
# Workaround for Explorer right click "Git GUI Here" on .git/
|
||||
cd ..
|
||||
}
|
||||
set argv [lrange $argv 2 end]
|
||||
incr argc -2
|
||||
}
|
||||
|
||||
set basedir [file dirname \
|
||||
[file dirname \
|
||||
[file dirname [info script]]]]
|
||||
set bindir [file join $basedir bin]
|
||||
set bindir "$bindir;[file join $basedir mingw bin]"
|
||||
regsub -all ";" $bindir "\\;" bindir
|
||||
set env(PATH) "$bindir;$env(PATH)"
|
||||
unset bindir
|
||||
|
||||
source [file join [file dirname [info script]] git-gui.tcl]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
|
||||
# This script is typically launched by using the 'git difftool'
|
||||
# convenience command.
|
||||
#
|
||||
# Copyright (c) 2009, 2010 David Aguilar
|
||||
|
||||
TOOL_MODE=diff
|
||||
. git-mergetool--lib
|
||||
|
||||
# difftool.prompt controls the default prompt/no-prompt behavior
|
||||
# and is overridden with $GIT_DIFFTOOL*_PROMPT.
|
||||
should_prompt () {
|
||||
prompt_merge=$(git config --bool mergetool.prompt || echo true)
|
||||
prompt=$(git config --bool difftool.prompt || echo $prompt_merge)
|
||||
if test "$prompt" = true
|
||||
then
|
||||
test -z "$GIT_DIFFTOOL_NO_PROMPT"
|
||||
else
|
||||
test -n "$GIT_DIFFTOOL_PROMPT"
|
||||
fi
|
||||
}
|
||||
|
||||
# Indicates that --extcmd=... was specified
|
||||
use_ext_cmd () {
|
||||
test -n "$GIT_DIFFTOOL_EXTCMD"
|
||||
}
|
||||
|
||||
launch_merge_tool () {
|
||||
# Merged is the filename as it appears in the work tree
|
||||
# Local is the contents of a/filename
|
||||
# Remote is the contents of b/filename
|
||||
# Custom merge tool commands might use $BASE so we provide it
|
||||
MERGED="$1"
|
||||
LOCAL="$2"
|
||||
REMOTE="$3"
|
||||
BASE="$1"
|
||||
|
||||
# $LOCAL and $REMOTE are temporary files so prompt
|
||||
# the user with the real $MERGED name before launching $merge_tool.
|
||||
if should_prompt
|
||||
then
|
||||
printf "\nViewing (%s/%s): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \
|
||||
"$GIT_DIFF_PATH_TOTAL" "$MERGED"
|
||||
if use_ext_cmd
|
||||
then
|
||||
printf "Launch '%s' [Y/n]? " \
|
||||
"$GIT_DIFFTOOL_EXTCMD"
|
||||
else
|
||||
printf "Launch '%s' [Y/n]? " "$merge_tool"
|
||||
fi
|
||||
read ans || return
|
||||
if test "$ans" = n
|
||||
then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if use_ext_cmd
|
||||
then
|
||||
export BASE
|
||||
eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
|
||||
else
|
||||
run_merge_tool "$merge_tool"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! use_ext_cmd
|
||||
then
|
||||
if test -n "$GIT_DIFF_TOOL"
|
||||
then
|
||||
merge_tool="$GIT_DIFF_TOOL"
|
||||
else
|
||||
merge_tool="$(get_merge_tool)" || exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$GIT_DIFFTOOL_DIRDIFF"
|
||||
then
|
||||
LOCAL="$1"
|
||||
REMOTE="$2"
|
||||
run_merge_tool "$merge_tool" false
|
||||
else
|
||||
# Launch the merge tool on each path provided by 'git diff'
|
||||
while test $# -gt 6
|
||||
do
|
||||
launch_merge_tool "$1" "$2" "$5"
|
||||
status=$?
|
||||
if test $status -ge 126
|
||||
then
|
||||
# Command not found (127), not executable (126) or
|
||||
# exited via a signal (>= 128).
|
||||
exit $status
|
||||
fi
|
||||
|
||||
if test "$status" != 0 &&
|
||||
test "$GIT_DIFFTOOL_TRUST_EXIT_CODE" = true
|
||||
then
|
||||
exit $status
|
||||
fi
|
||||
shift 7
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,662 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Rewrite revision history
|
||||
# Copyright (c) Petr Baudis, 2006
|
||||
# Minimal changes to "port" it to core-git (c) Johannes Schindelin, 2007
|
||||
#
|
||||
# Lets you rewrite the revision history of the current branch, creating
|
||||
# a new branch. You can specify a number of filters to modify the commits,
|
||||
# files and trees.
|
||||
|
||||
# The following functions will also be available in the commit filter:
|
||||
|
||||
functions=$(cat << \EOF
|
||||
EMPTY_TREE=$(git hash-object -t tree /dev/null)
|
||||
|
||||
warn () {
|
||||
echo "$*" >&2
|
||||
}
|
||||
|
||||
map()
|
||||
{
|
||||
# if it was not rewritten, take the original
|
||||
if test -r "$workdir/../map/$1"
|
||||
then
|
||||
cat "$workdir/../map/$1"
|
||||
else
|
||||
echo "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# if you run 'skip_commit "$@"' in a commit filter, it will print
|
||||
# the (mapped) parents, effectively skipping the commit.
|
||||
|
||||
skip_commit()
|
||||
{
|
||||
shift;
|
||||
while [ -n "$1" ];
|
||||
do
|
||||
shift;
|
||||
map "$1";
|
||||
shift;
|
||||
done;
|
||||
}
|
||||
|
||||
# if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
|
||||
# it will skip commits that leave the tree untouched, commit the other.
|
||||
git_commit_non_empty_tree()
|
||||
{
|
||||
if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
|
||||
map "$3"
|
||||
elif test $# = 1 && test "$1" = $EMPTY_TREE; then
|
||||
:
|
||||
else
|
||||
git commit-tree "$@"
|
||||
fi
|
||||
}
|
||||
# override die(): this version puts in an extra line break, so that
|
||||
# the progress is still visible
|
||||
|
||||
die()
|
||||
{
|
||||
echo >&2
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
eval "$functions"
|
||||
|
||||
finish_ident() {
|
||||
# Ensure non-empty id name.
|
||||
echo "case \"\$GIT_$1_NAME\" in \"\") GIT_$1_NAME=\"\${GIT_$1_EMAIL%%@*}\" && export GIT_$1_NAME;; esac"
|
||||
# And make sure everything is exported.
|
||||
echo "export GIT_$1_NAME"
|
||||
echo "export GIT_$1_EMAIL"
|
||||
echo "export GIT_$1_DATE"
|
||||
}
|
||||
|
||||
set_ident () {
|
||||
parse_ident_from_commit author AUTHOR committer COMMITTER
|
||||
finish_ident AUTHOR
|
||||
finish_ident COMMITTER
|
||||
}
|
||||
|
||||
USAGE="[--setup <command>] [--subdirectory-filter <directory>] [--env-filter <command>]
|
||||
[--tree-filter <command>] [--index-filter <command>]
|
||||
[--parent-filter <command>] [--msg-filter <command>]
|
||||
[--commit-filter <command>] [--tag-name-filter <command>]
|
||||
[--original <namespace>]
|
||||
[-d <directory>] [-f | --force] [--state-branch <branch>]
|
||||
[--] [<rev-list options>...]"
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
if [ "$(is_bare_repository)" = false ]; then
|
||||
require_clean_work_tree 'rewrite branches'
|
||||
fi
|
||||
|
||||
tempdir=.git-rewrite
|
||||
filter_setup=
|
||||
filter_env=
|
||||
filter_tree=
|
||||
filter_index=
|
||||
filter_parent=
|
||||
filter_msg=cat
|
||||
filter_commit=
|
||||
filter_tag_name=
|
||||
filter_subdir=
|
||||
state_branch=
|
||||
orig_namespace=refs/original/
|
||||
force=
|
||||
prune_empty=
|
||||
remap_to_ancestor=
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
--force|-f)
|
||||
shift
|
||||
force=t
|
||||
continue
|
||||
;;
|
||||
--remap-to-ancestor)
|
||||
# deprecated ($remap_to_ancestor is set now automatically)
|
||||
shift
|
||||
remap_to_ancestor=t
|
||||
continue
|
||||
;;
|
||||
--prune-empty)
|
||||
shift
|
||||
prune_empty=t
|
||||
continue
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break;
|
||||
esac
|
||||
|
||||
# all switches take one argument
|
||||
ARG="$1"
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
OPTARG="$1"
|
||||
shift
|
||||
|
||||
case "$ARG" in
|
||||
-d)
|
||||
tempdir="$OPTARG"
|
||||
;;
|
||||
--setup)
|
||||
filter_setup="$OPTARG"
|
||||
;;
|
||||
--subdirectory-filter)
|
||||
filter_subdir="$OPTARG"
|
||||
remap_to_ancestor=t
|
||||
;;
|
||||
--env-filter)
|
||||
filter_env="$OPTARG"
|
||||
;;
|
||||
--tree-filter)
|
||||
filter_tree="$OPTARG"
|
||||
;;
|
||||
--index-filter)
|
||||
filter_index="$OPTARG"
|
||||
;;
|
||||
--parent-filter)
|
||||
filter_parent="$OPTARG"
|
||||
;;
|
||||
--msg-filter)
|
||||
filter_msg="$OPTARG"
|
||||
;;
|
||||
--commit-filter)
|
||||
filter_commit="$functions; $OPTARG"
|
||||
;;
|
||||
--tag-name-filter)
|
||||
filter_tag_name="$OPTARG"
|
||||
;;
|
||||
--original)
|
||||
orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
|
||||
;;
|
||||
--state-branch)
|
||||
state_branch="$OPTARG"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$prune_empty,$filter_commit" in
|
||||
,)
|
||||
filter_commit='git commit-tree "$@"';;
|
||||
t,)
|
||||
filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
|
||||
,*)
|
||||
;;
|
||||
*)
|
||||
die "Cannot set --prune-empty and --commit-filter at the same time"
|
||||
esac
|
||||
|
||||
case "$force" in
|
||||
t)
|
||||
rm -rf "$tempdir"
|
||||
;;
|
||||
'')
|
||||
test -d "$tempdir" &&
|
||||
die "$tempdir already exists, please remove it"
|
||||
esac
|
||||
orig_dir=$(pwd)
|
||||
mkdir -p "$tempdir/t" &&
|
||||
tempdir="$(cd "$tempdir"; pwd)" &&
|
||||
cd "$tempdir/t" &&
|
||||
workdir="$(pwd)" ||
|
||||
die ""
|
||||
|
||||
# Remove tempdir on exit
|
||||
trap 'cd "$orig_dir"; rm -rf "$tempdir"' 0
|
||||
|
||||
ORIG_GIT_DIR="$GIT_DIR"
|
||||
ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
|
||||
ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
|
||||
ORIG_GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME"
|
||||
ORIG_GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL"
|
||||
ORIG_GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE"
|
||||
ORIG_GIT_COMMITTER_NAME="$GIT_COMMITTER_NAME"
|
||||
ORIG_GIT_COMMITTER_EMAIL="$GIT_COMMITTER_EMAIL"
|
||||
ORIG_GIT_COMMITTER_DATE="$GIT_COMMITTER_DATE"
|
||||
|
||||
GIT_WORK_TREE=.
|
||||
export GIT_DIR GIT_WORK_TREE
|
||||
|
||||
# Make sure refs/original is empty
|
||||
git for-each-ref > "$tempdir"/backup-refs || exit
|
||||
while read sha1 type name
|
||||
do
|
||||
case "$force,$name" in
|
||||
,$orig_namespace*)
|
||||
die "Cannot create a new backup.
|
||||
A previous backup already exists in $orig_namespace
|
||||
Force overwriting the backup with -f"
|
||||
;;
|
||||
t,$orig_namespace*)
|
||||
git update-ref -d "$name" $sha1
|
||||
;;
|
||||
esac
|
||||
done < "$tempdir"/backup-refs
|
||||
|
||||
# The refs should be updated if their heads were rewritten
|
||||
git rev-parse --no-flags --revs-only --symbolic-full-name \
|
||||
--default HEAD "$@" > "$tempdir"/raw-refs || exit
|
||||
while read ref
|
||||
do
|
||||
case "$ref" in ^?*) continue ;; esac
|
||||
|
||||
if git rev-parse --verify "$ref"^0 >/dev/null 2>&1
|
||||
then
|
||||
echo "$ref"
|
||||
else
|
||||
warn "WARNING: not rewriting '$ref' (not a committish)"
|
||||
fi
|
||||
done >"$tempdir"/heads <"$tempdir"/raw-refs
|
||||
|
||||
test -s "$tempdir"/heads ||
|
||||
die "You must specify a ref to rewrite."
|
||||
|
||||
GIT_INDEX_FILE="$(pwd)/../index"
|
||||
export GIT_INDEX_FILE
|
||||
|
||||
# map old->new commit ids for rewriting parents
|
||||
mkdir ../map || die "Could not create map/ directory"
|
||||
|
||||
if test -n "$state_branch"
|
||||
then
|
||||
state_commit=$(git rev-parse --no-flags --revs-only "$state_branch")
|
||||
if test -n "$state_commit"
|
||||
then
|
||||
echo "Populating map from $state_branch ($state_commit)" 1>&2
|
||||
perl -e'open(MAP, "-|", "git show $ARGV[0]:filter.map") or die;
|
||||
while (<MAP>) {
|
||||
m/(.*):(.*)/ or die;
|
||||
open F, ">../map/$1" or die;
|
||||
print F "$2" or die;
|
||||
close(F) or die;
|
||||
}
|
||||
close(MAP) or die;' "$state_commit" \
|
||||
|| die "Unable to load state from $state_branch:filter.map"
|
||||
else
|
||||
echo "Branch $state_branch does not exist. Will create" 1>&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# we need "--" only if there are no path arguments in $@
|
||||
nonrevs=$(git rev-parse --no-revs "$@") || exit
|
||||
if test -z "$nonrevs"
|
||||
then
|
||||
dashdash=--
|
||||
else
|
||||
dashdash=
|
||||
remap_to_ancestor=t
|
||||
fi
|
||||
|
||||
git rev-parse --revs-only "$@" >../parse
|
||||
|
||||
case "$filter_subdir" in
|
||||
"")
|
||||
eval set -- "$(git rev-parse --sq --no-revs "$@")"
|
||||
;;
|
||||
*)
|
||||
eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
|
||||
"$filter_subdir")"
|
||||
;;
|
||||
esac
|
||||
|
||||
git rev-list --reverse --topo-order --default HEAD \
|
||||
--parents --simplify-merges --stdin "$@" <../parse >../revs ||
|
||||
die "Could not get the commits"
|
||||
commits=$(wc -l <../revs | tr -d " ")
|
||||
|
||||
test $commits -eq 0 && die_with_status 2 "Found nothing to rewrite"
|
||||
|
||||
# Rewrite the commits
|
||||
report_progress ()
|
||||
{
|
||||
if test -n "$progress" &&
|
||||
test $git_filter_branch__commit_count -gt $next_sample_at
|
||||
then
|
||||
count=$git_filter_branch__commit_count
|
||||
|
||||
now=$(date +%s)
|
||||
elapsed=$(($now - $start_timestamp))
|
||||
remaining=$(( ($commits - $count) * $elapsed / $count ))
|
||||
if test $elapsed -gt 0
|
||||
then
|
||||
next_sample_at=$(( ($elapsed + 1) * $count / $elapsed ))
|
||||
else
|
||||
next_sample_at=$(($next_sample_at + 1))
|
||||
fi
|
||||
progress=" ($elapsed seconds passed, remaining $remaining predicted)"
|
||||
fi
|
||||
printf "\rRewrite $commit ($count/$commits)$progress "
|
||||
}
|
||||
|
||||
git_filter_branch__commit_count=0
|
||||
|
||||
progress= start_timestamp=
|
||||
if date '+%s' 2>/dev/null | grep -q '^[0-9][0-9]*$'
|
||||
then
|
||||
next_sample_at=0
|
||||
progress="dummy to ensure this is not empty"
|
||||
start_timestamp=$(date '+%s')
|
||||
fi
|
||||
|
||||
if test -n "$filter_index" ||
|
||||
test -n "$filter_tree" ||
|
||||
test -n "$filter_subdir"
|
||||
then
|
||||
need_index=t
|
||||
else
|
||||
need_index=
|
||||
fi
|
||||
|
||||
eval "$filter_setup" < /dev/null ||
|
||||
die "filter setup failed: $filter_setup"
|
||||
|
||||
while read commit parents; do
|
||||
git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
|
||||
|
||||
report_progress
|
||||
test -f "$workdir"/../map/$commit && continue
|
||||
|
||||
case "$filter_subdir" in
|
||||
"")
|
||||
if test -n "$need_index"
|
||||
then
|
||||
GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# The commit may not have the subdirectory at all
|
||||
err=$(GIT_ALLOW_NULL_SHA1=1 \
|
||||
git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
|
||||
if ! git rev-parse -q --verify $commit:"$filter_subdir"
|
||||
then
|
||||
rm -f "$GIT_INDEX_FILE"
|
||||
else
|
||||
echo >&2 "$err"
|
||||
false
|
||||
fi
|
||||
}
|
||||
esac || die "Could not initialize the index"
|
||||
|
||||
GIT_COMMIT=$commit
|
||||
export GIT_COMMIT
|
||||
git cat-file commit "$commit" >../commit ||
|
||||
die "Cannot read commit $commit"
|
||||
|
||||
eval "$(set_ident <../commit)" ||
|
||||
die "setting author/committer failed for commit $commit"
|
||||
eval "$filter_env" < /dev/null ||
|
||||
die "env filter failed: $filter_env"
|
||||
|
||||
if [ "$filter_tree" ]; then
|
||||
git checkout-index -f -u -a ||
|
||||
die "Could not checkout the index"
|
||||
# files that $commit removed are now still in the working tree;
|
||||
# remove them, else they would be added again
|
||||
git clean -d -q -f -x
|
||||
eval "$filter_tree" < /dev/null ||
|
||||
die "tree filter failed: $filter_tree"
|
||||
|
||||
(
|
||||
git diff-index -r --name-only --ignore-submodules $commit -- &&
|
||||
git ls-files --others
|
||||
) > "$tempdir"/tree-state || exit
|
||||
git update-index --add --replace --remove --stdin \
|
||||
< "$tempdir"/tree-state || exit
|
||||
fi
|
||||
|
||||
eval "$filter_index" < /dev/null ||
|
||||
die "index filter failed: $filter_index"
|
||||
|
||||
parentstr=
|
||||
for parent in $parents; do
|
||||
for reparent in $(map "$parent"); do
|
||||
case "$parentstr " in
|
||||
*" -p $reparent "*)
|
||||
;;
|
||||
*)
|
||||
parentstr="$parentstr -p $reparent"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
if [ "$filter_parent" ]; then
|
||||
parentstr="$(echo "$parentstr" | eval "$filter_parent")" ||
|
||||
die "parent filter failed: $filter_parent"
|
||||
fi
|
||||
|
||||
{
|
||||
while IFS='' read -r header_line && test -n "$header_line"
|
||||
do
|
||||
# skip header lines...
|
||||
:;
|
||||
done
|
||||
# and output the actual commit message
|
||||
cat
|
||||
} <../commit |
|
||||
eval "$filter_msg" > ../message ||
|
||||
die "msg filter failed: $filter_msg"
|
||||
|
||||
if test -n "$need_index"
|
||||
then
|
||||
tree=$(git write-tree)
|
||||
else
|
||||
tree=$(git rev-parse "$commit^{tree}")
|
||||
fi
|
||||
workdir=$workdir /bin/sh -c "$filter_commit" "git commit-tree" \
|
||||
"$tree" $parentstr < ../message > ../map/$commit ||
|
||||
die "could not write rewritten commit"
|
||||
done <../revs
|
||||
|
||||
# If we are filtering for paths, as in the case of a subdirectory
|
||||
# filter, it is possible that a specified head is not in the set of
|
||||
# rewritten commits, because it was pruned by the revision walker.
|
||||
# Ancestor remapping fixes this by mapping these heads to the unique
|
||||
# nearest ancestor that survived the pruning.
|
||||
|
||||
if test "$remap_to_ancestor" = t
|
||||
then
|
||||
while read ref
|
||||
do
|
||||
sha1=$(git rev-parse "$ref"^0)
|
||||
test -f "$workdir"/../map/$sha1 && continue
|
||||
ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
|
||||
test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
|
||||
done < "$tempdir"/heads
|
||||
fi
|
||||
|
||||
# Finally update the refs
|
||||
|
||||
_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"
|
||||
echo
|
||||
while read ref
|
||||
do
|
||||
# avoid rewriting a ref twice
|
||||
test -f "$orig_namespace$ref" && continue
|
||||
|
||||
sha1=$(git rev-parse "$ref"^0)
|
||||
rewritten=$(map $sha1)
|
||||
|
||||
test $sha1 = "$rewritten" &&
|
||||
warn "WARNING: Ref '$ref' is unchanged" &&
|
||||
continue
|
||||
|
||||
case "$rewritten" in
|
||||
'')
|
||||
echo "Ref '$ref' was deleted"
|
||||
git update-ref -m "filter-branch: delete" -d "$ref" $sha1 ||
|
||||
die "Could not delete $ref"
|
||||
;;
|
||||
$_x40)
|
||||
echo "Ref '$ref' was rewritten"
|
||||
if ! git update-ref -m "filter-branch: rewrite" \
|
||||
"$ref" $rewritten $sha1 2>/dev/null; then
|
||||
if test $(git cat-file -t "$ref") = tag; then
|
||||
if test -z "$filter_tag_name"; then
|
||||
warn "WARNING: You said to rewrite tagged commits, but not the corresponding tag."
|
||||
warn "WARNING: Perhaps use '--tag-name-filter cat' to rewrite the tag."
|
||||
fi
|
||||
else
|
||||
die "Could not rewrite $ref"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# NEEDSWORK: possibly add -Werror, making this an error
|
||||
warn "WARNING: '$ref' was rewritten into multiple commits:"
|
||||
warn "$rewritten"
|
||||
warn "WARNING: Ref '$ref' points to the first one now."
|
||||
rewritten=$(echo "$rewritten" | head -n 1)
|
||||
git update-ref -m "filter-branch: rewrite to first" \
|
||||
"$ref" $rewritten $sha1 ||
|
||||
die "Could not rewrite $ref"
|
||||
;;
|
||||
esac
|
||||
git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 ||
|
||||
exit
|
||||
done < "$tempdir"/heads
|
||||
|
||||
# TODO: This should possibly go, with the semantics that all positive given
|
||||
# refs are updated, and their original heads stored in refs/original/
|
||||
# Filter tags
|
||||
|
||||
if [ "$filter_tag_name" ]; then
|
||||
git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags |
|
||||
while read sha1 type ref; do
|
||||
ref="${ref#refs/tags/}"
|
||||
# XXX: Rewrite tagged trees as well?
|
||||
if [ "$type" != "commit" -a "$type" != "tag" ]; then
|
||||
continue;
|
||||
fi
|
||||
|
||||
if [ "$type" = "tag" ]; then
|
||||
# Dereference to a commit
|
||||
sha1t="$sha1"
|
||||
sha1="$(git rev-parse -q "$sha1"^{commit})" || continue
|
||||
fi
|
||||
|
||||
[ -f "../map/$sha1" ] || continue
|
||||
new_sha1="$(cat "../map/$sha1")"
|
||||
GIT_COMMIT="$sha1"
|
||||
export GIT_COMMIT
|
||||
new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
|
||||
die "tag name filter failed: $filter_tag_name"
|
||||
|
||||
echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
|
||||
|
||||
if [ "$type" = "tag" ]; then
|
||||
new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
|
||||
"$new_sha1" "$new_ref"
|
||||
git cat-file tag "$ref" |
|
||||
sed -n \
|
||||
-e '1,/^$/{
|
||||
/^object /d
|
||||
/^type /d
|
||||
/^tag /d
|
||||
}' \
|
||||
-e '/^-----BEGIN PGP SIGNATURE-----/q' \
|
||||
-e 'p' ) |
|
||||
git hash-object -t tag -w --stdin) ||
|
||||
die "Could not create new tag object for $ref"
|
||||
if git cat-file tag "$ref" | \
|
||||
sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
|
||||
then
|
||||
warn "gpg signature stripped from tag object $sha1t"
|
||||
fi
|
||||
fi
|
||||
|
||||
git update-ref "refs/tags/$new_ref" "$new_sha1" ||
|
||||
die "Could not write tag $new_ref"
|
||||
done
|
||||
fi
|
||||
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
|
||||
unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE
|
||||
test -z "$ORIG_GIT_DIR" || {
|
||||
GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
|
||||
}
|
||||
test -z "$ORIG_GIT_WORK_TREE" || {
|
||||
GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
|
||||
export GIT_WORK_TREE
|
||||
}
|
||||
test -z "$ORIG_GIT_INDEX_FILE" || {
|
||||
GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
|
||||
export GIT_INDEX_FILE
|
||||
}
|
||||
test -z "$ORIG_GIT_AUTHOR_NAME" || {
|
||||
GIT_AUTHOR_NAME="$ORIG_GIT_AUTHOR_NAME" &&
|
||||
export GIT_AUTHOR_NAME
|
||||
}
|
||||
test -z "$ORIG_GIT_AUTHOR_EMAIL" || {
|
||||
GIT_AUTHOR_EMAIL="$ORIG_GIT_AUTHOR_EMAIL" &&
|
||||
export GIT_AUTHOR_EMAIL
|
||||
}
|
||||
test -z "$ORIG_GIT_AUTHOR_DATE" || {
|
||||
GIT_AUTHOR_DATE="$ORIG_GIT_AUTHOR_DATE" &&
|
||||
export GIT_AUTHOR_DATE
|
||||
}
|
||||
test -z "$ORIG_GIT_COMMITTER_NAME" || {
|
||||
GIT_COMMITTER_NAME="$ORIG_GIT_COMMITTER_NAME" &&
|
||||
export GIT_COMMITTER_NAME
|
||||
}
|
||||
test -z "$ORIG_GIT_COMMITTER_EMAIL" || {
|
||||
GIT_COMMITTER_EMAIL="$ORIG_GIT_COMMITTER_EMAIL" &&
|
||||
export GIT_COMMITTER_EMAIL
|
||||
}
|
||||
test -z "$ORIG_GIT_COMMITTER_DATE" || {
|
||||
GIT_COMMITTER_DATE="$ORIG_GIT_COMMITTER_DATE" &&
|
||||
export GIT_COMMITTER_DATE
|
||||
}
|
||||
|
||||
if test -n "$state_branch"
|
||||
then
|
||||
echo "Saving rewrite state to $state_branch" 1>&2
|
||||
state_blob=$(
|
||||
perl -e'opendir D, "../map" or die;
|
||||
open H, "|-", "git hash-object -w --stdin" or die;
|
||||
foreach (sort readdir(D)) {
|
||||
next if m/^\.\.?$/;
|
||||
open F, "<../map/$_" or die;
|
||||
chomp($f = <F>);
|
||||
print H "$_:$f\n" or die;
|
||||
}
|
||||
close(H) or die;' || die "Unable to save state")
|
||||
state_tree=$(printf '100644 blob %s\tfilter.map\n' "$state_blob" | git mktree)
|
||||
if test -n "$state_commit"
|
||||
then
|
||||
state_commit=$(echo "Sync" | git commit-tree "$state_tree" -p "$state_commit")
|
||||
else
|
||||
state_commit=$(echo "Sync" | git commit-tree "$state_tree" )
|
||||
fi
|
||||
git update-ref "$state_branch" "$state_commit"
|
||||
fi
|
||||
|
||||
cd "$orig_dir"
|
||||
rm -rf "$tempdir"
|
||||
|
||||
trap - 0
|
||||
|
||||
if [ "$(is_bare_repository)" = false ]; then
|
||||
git read-tree -u -m HEAD || exit
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Tcl ignores the next line -*- tcl -*- \
|
||||
exec wish "$0" -- "$@"
|
||||
|
||||
if { $argc >=2 && [lindex $argv 0] == "--working-dir" } {
|
||||
set workdir [lindex $argv 1]
|
||||
cd $workdir
|
||||
if {[lindex [file split $workdir] end] eq {.git}} {
|
||||
# Workaround for Explorer right click "Git GUI Here" on .git/
|
||||
cd ..
|
||||
}
|
||||
set argv [lrange $argv 2 end]
|
||||
incr argc -2
|
||||
}
|
||||
|
||||
set basedir [file dirname \
|
||||
[file dirname \
|
||||
[file dirname [info script]]]]
|
||||
set bindir [file join $basedir bin]
|
||||
set bindir "$bindir;[file join $basedir mingw bin]"
|
||||
regsub -all ";" $bindir "\\;" bindir
|
||||
set env(PATH) "$bindir;$env(PATH)"
|
||||
unset bindir
|
||||
|
||||
source [file join [file dirname [info script]] git-gui.tcl]
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
# Tcl ignores the next line -*- tcl -*- \
|
||||
exec wish "$0" -- "$@"
|
||||
|
||||
# This is a trivial implementation of an SSH_ASKPASS handler.
|
||||
# Git-gui uses this script if none are already configured.
|
||||
|
||||
package require Tk
|
||||
|
||||
set answer {}
|
||||
set yesno 0
|
||||
set rc 255
|
||||
|
||||
if {$argc < 1} {
|
||||
set prompt "Enter your OpenSSH passphrase:"
|
||||
} else {
|
||||
set prompt [join $argv " "]
|
||||
if {[regexp -nocase {\(yes\/no\)\?\s*$} $prompt]} {
|
||||
set yesno 1
|
||||
}
|
||||
}
|
||||
|
||||
message .m -text $prompt -justify center -aspect 4000
|
||||
pack .m -side top -fill x -padx 20 -pady 20 -expand 1
|
||||
|
||||
entry .e -textvariable answer -width 50
|
||||
pack .e -side top -fill x -padx 10 -pady 10
|
||||
|
||||
if {!$yesno} {
|
||||
.e configure -show "*"
|
||||
}
|
||||
|
||||
frame .b
|
||||
button .b.ok -text OK -command finish
|
||||
button .b.cancel -text Cancel -command cancel
|
||||
|
||||
pack .b.ok -side left -expand 1
|
||||
pack .b.cancel -side right -expand 1
|
||||
pack .b -side bottom -fill x -padx 10 -pady 10
|
||||
|
||||
bind . <Visibility> {focus -force .e}
|
||||
bind . <Key-Return> [list .b.ok invoke]
|
||||
bind . <Key-Escape> [list .b.cancel invoke]
|
||||
bind . <Destroy> {set rc $rc}
|
||||
|
||||
proc cancel {} {
|
||||
set ::rc 255
|
||||
}
|
||||
|
||||
proc finish {} {
|
||||
if {$::yesno} {
|
||||
if {$::answer ne "yes" && $::answer ne "no"} {
|
||||
tk_messageBox -icon error -title "Error" -type ok \
|
||||
-message "Only 'yes' or 'no' input allowed."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
puts $::answer
|
||||
set ::rc 0
|
||||
}
|
||||
|
||||
wm title . "OpenSSH"
|
||||
tk::PlaceWindow .
|
||||
vwait rc
|
||||
exit $rc
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
# Tcl ignores the next line -*- tcl -*- \
|
||||
exec wish "$0" -- "$@"
|
||||
|
||||
# This is an implementation of a simple yes no dialog
|
||||
# which is injected into the git commandline by git gui
|
||||
# in case a yesno question needs to be answered.
|
||||
|
||||
set NS {}
|
||||
set use_ttk [package vsatisfies [package provide Tk] 8.5]
|
||||
if {$use_ttk} {
|
||||
set NS ttk
|
||||
}
|
||||
|
||||
set title "Question?"
|
||||
if {$argc < 1} {
|
||||
puts stderr "Usage: $argv0 <question>"
|
||||
exit 1
|
||||
} else {
|
||||
if {$argc > 2 && [lindex $argv 0] == "--title"} {
|
||||
set title [lindex $argv 1]
|
||||
set argv [lreplace $argv 0 1]
|
||||
}
|
||||
set prompt [join $argv " "]
|
||||
}
|
||||
|
||||
${NS}::frame .t
|
||||
${NS}::label .t.m -text $prompt -justify center -width 400px
|
||||
.t.m configure -wraplength 400px
|
||||
pack .t.m -side top -fill x -padx 20 -pady 20 -expand 1
|
||||
pack .t -side top -fill x -ipadx 20 -ipady 20 -expand 1
|
||||
|
||||
${NS}::frame .b
|
||||
${NS}::frame .b.left -width 200
|
||||
${NS}::button .b.yes -text Yes -command yes
|
||||
${NS}::button .b.no -text No -command no
|
||||
|
||||
|
||||
pack .b.left -side left -expand 1 -fill x
|
||||
pack .b.yes -side left -expand 1
|
||||
pack .b.no -side right -expand 1 -ipadx 5
|
||||
pack .b -side bottom -fill x -ipadx 20 -ipady 15
|
||||
|
||||
bind . <Key-Return> {exit 0}
|
||||
bind . <Key-Escape> {exit 1}
|
||||
|
||||
proc no {} {
|
||||
exit 1
|
||||
}
|
||||
|
||||
proc yes {} {
|
||||
exit 0
|
||||
}
|
||||
|
||||
if {$::tcl_platform(platform) eq {windows}} {
|
||||
set icopath [file dirname [file normalize $argv0]]
|
||||
if {[file tail $icopath] eq {git-core}} {
|
||||
set icopath [file dirname $icopath]
|
||||
}
|
||||
set icopath [file dirname $icopath]
|
||||
set icopath [file join $icopath share git git-for-windows.ico]
|
||||
if {[file exists $icopath]} {
|
||||
wm iconbitmap . -default $icopath
|
||||
}
|
||||
}
|
||||
|
||||
wm title . $title
|
||||
tk::PlaceWindow .
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,789 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006 Eric Wong
|
||||
#
|
||||
|
||||
PERL='/usr/bin/perl'
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_STUCKLONG=
|
||||
OPTIONS_SPEC="\
|
||||
git instaweb [options] (--start | --stop | --restart)
|
||||
--
|
||||
l,local only bind on 127.0.0.1
|
||||
p,port= the port to bind to
|
||||
d,httpd= the command to launch
|
||||
b,browser= the browser to launch
|
||||
m,module-path= the module path (only needed for apache2)
|
||||
Action
|
||||
stop stop the web server
|
||||
start start the web server
|
||||
restart restart the web server
|
||||
"
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
|
||||
fqgitdir="$GIT_DIR"
|
||||
local="$(git config --bool --get instaweb.local)"
|
||||
httpd="$(git config --get instaweb.httpd)"
|
||||
root="$(git config --get instaweb.gitwebdir)"
|
||||
port=$(git config --get instaweb.port)
|
||||
module_path="$(git config --get instaweb.modulepath)"
|
||||
action="browse"
|
||||
|
||||
conf="$GIT_DIR/gitweb/httpd.conf"
|
||||
|
||||
# Defaults:
|
||||
|
||||
# if installed, it doesn't need further configuration (module_path)
|
||||
test -z "$httpd" && httpd='lighttpd -f'
|
||||
|
||||
# Default is /mingw64/share/gitweb
|
||||
test -z "$root" && root='/mingw64/share/gitweb'
|
||||
|
||||
# any untaken local port will do...
|
||||
test -z "$port" && port=1234
|
||||
|
||||
resolve_full_httpd () {
|
||||
case "$httpd" in
|
||||
*apache2*|*lighttpd*|*httpd*)
|
||||
# yes, *httpd* covers *lighttpd* above, but it is there for clarity
|
||||
# ensure that the apache2/lighttpd command ends with "-f"
|
||||
if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
|
||||
then
|
||||
httpd="$httpd -f"
|
||||
fi
|
||||
;;
|
||||
*plackup*)
|
||||
# server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
|
||||
full_httpd="$fqgitdir/gitweb/gitweb.psgi"
|
||||
httpd_only="${httpd%% *}" # cut on first space
|
||||
return
|
||||
;;
|
||||
*webrick*)
|
||||
# server is started by running via generated webrick.rb in
|
||||
# $fqgitdir/gitweb
|
||||
full_httpd="$fqgitdir/gitweb/webrick.rb"
|
||||
httpd_only="${httpd%% *}" # cut on first space
|
||||
return
|
||||
;;
|
||||
*python*)
|
||||
# server is started by running via generated gitweb.py in
|
||||
# $fqgitdir/gitweb
|
||||
full_httpd="$fqgitdir/gitweb/gitweb.py"
|
||||
httpd_only="${httpd%% *}" # cut on first space
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
httpd_only="$(echo $httpd | cut -f1 -d' ')"
|
||||
if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null 2>&1;; esac
|
||||
then
|
||||
full_httpd=$httpd
|
||||
else
|
||||
# many httpds are installed in /usr/sbin or /usr/local/sbin
|
||||
# these days and those are not in most users $PATHs
|
||||
# in addition, we may have generated a server script
|
||||
# in $fqgitdir/gitweb.
|
||||
for i in /usr/local/sbin /usr/sbin "$root" "$fqgitdir/gitweb"
|
||||
do
|
||||
if test -x "$i/$httpd_only"
|
||||
then
|
||||
full_httpd=$i/$httpd
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
echo >&2 "$httpd_only not found. Install $httpd_only or use" \
|
||||
"--httpd to specify another httpd daemon."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
start_httpd () {
|
||||
if test -f "$fqgitdir/pid"; then
|
||||
say "Instance already running. Restarting..."
|
||||
stop_httpd
|
||||
fi
|
||||
|
||||
# here $httpd should have a meaningful value
|
||||
resolve_full_httpd
|
||||
mkdir -p "$fqgitdir/gitweb/$httpd_only"
|
||||
conf="$fqgitdir/gitweb/$httpd_only.conf"
|
||||
|
||||
# generate correct config file if it doesn't exist
|
||||
test -f "$conf" || configure_httpd
|
||||
test -f "$fqgitdir/gitweb/gitweb_config.perl" || gitweb_conf
|
||||
|
||||
# don't quote $full_httpd, there can be arguments to it (-f)
|
||||
case "$httpd" in
|
||||
*mongoose*|*plackup*|*python*)
|
||||
#These servers don't have a daemon mode so we'll have to fork it
|
||||
$full_httpd "$conf" &
|
||||
#Save the pid before doing anything else (we'll print it later)
|
||||
pid=$!
|
||||
|
||||
if test $? != 0; then
|
||||
echo "Could not execute http daemon $httpd."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat > "$fqgitdir/pid" <<EOF
|
||||
$pid
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
$full_httpd "$conf"
|
||||
if test $? != 0; then
|
||||
echo "Could not execute http daemon $httpd."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
stop_httpd () {
|
||||
test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
|
||||
rm -f "$fqgitdir/pid"
|
||||
}
|
||||
|
||||
httpd_is_ready () {
|
||||
"$PERL" -MIO::Socket::INET -e "
|
||||
local \$| = 1; # turn on autoflush
|
||||
exit if (IO::Socket::INET->new('127.0.0.1:$port'));
|
||||
print 'Waiting for \'$httpd\' to start ..';
|
||||
do {
|
||||
print '.';
|
||||
sleep(1);
|
||||
} until (IO::Socket::INET->new('127.0.0.1:$port'));
|
||||
print qq! (done)\n!;
|
||||
"
|
||||
}
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--stop|stop)
|
||||
action="stop"
|
||||
;;
|
||||
--start|start)
|
||||
action="start"
|
||||
;;
|
||||
--restart|restart)
|
||||
action="restart"
|
||||
;;
|
||||
-l|--local)
|
||||
local=true
|
||||
;;
|
||||
-d|--httpd)
|
||||
shift
|
||||
httpd="$1"
|
||||
;;
|
||||
-b|--browser)
|
||||
shift
|
||||
browser="$1"
|
||||
;;
|
||||
-p|--port)
|
||||
shift
|
||||
port="$1"
|
||||
;;
|
||||
-m|--module-path)
|
||||
shift
|
||||
module_path="$1"
|
||||
;;
|
||||
--)
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
mkdir -p "$GIT_DIR/gitweb/tmp"
|
||||
GIT_EXEC_PATH="$(git --exec-path)"
|
||||
GIT_DIR="$fqgitdir"
|
||||
GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
|
||||
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
|
||||
|
||||
webrick_conf () {
|
||||
# webrick seems to have no way of passing arbitrary environment
|
||||
# variables to the underlying CGI executable, so we wrap the
|
||||
# actual gitweb.cgi using a shell script to force it
|
||||
wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
|
||||
cat > "$wrapper" <<EOF
|
||||
#!/bin/sh
|
||||
# we use this shell script wrapper around the real gitweb.cgi since
|
||||
# there appears to be no other way to pass arbitrary environment variables
|
||||
# into the CGI process
|
||||
GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
|
||||
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
|
||||
exec $root/gitweb.cgi
|
||||
EOF
|
||||
chmod +x "$wrapper"
|
||||
|
||||
# This assumes _ruby_ is in the user's $PATH. that's _one_
|
||||
# portable way to run ruby, which could be installed anywhere, really.
|
||||
# generate a standalone server script in $fqgitdir/gitweb.
|
||||
cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
|
||||
#!/usr/bin/env ruby
|
||||
require 'webrick'
|
||||
require 'logger'
|
||||
options = {
|
||||
:Port => $port,
|
||||
:DocumentRoot => "$root",
|
||||
:Logger => Logger.new('$fqgitdir/gitweb/error.log'),
|
||||
:AccessLog => [
|
||||
[ Logger.new('$fqgitdir/gitweb/access.log'),
|
||||
WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
|
||||
],
|
||||
:DirectoryIndex => ["gitweb.cgi"],
|
||||
:CGIInterpreter => "$wrapper",
|
||||
:StartCallback => lambda do
|
||||
File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
|
||||
end,
|
||||
:ServerType => WEBrick::Daemon,
|
||||
}
|
||||
options[:BindAddress] = '127.0.0.1' if "$local" == "true"
|
||||
server = WEBrick::HTTPServer.new(options)
|
||||
['INT', 'TERM'].each do |signal|
|
||||
trap(signal) {server.shutdown}
|
||||
end
|
||||
server.start
|
||||
EOF
|
||||
chmod +x "$fqgitdir/gitweb/$httpd.rb"
|
||||
# configuration is embedded in server script file, webrick.rb
|
||||
rm -f "$conf"
|
||||
}
|
||||
|
||||
lighttpd_conf () {
|
||||
cat > "$conf" <<EOF
|
||||
server.document-root = "$root"
|
||||
server.port = $port
|
||||
server.modules = ( "mod_setenv", "mod_cgi" )
|
||||
server.indexfiles = ( "gitweb.cgi" )
|
||||
server.pid-file = "$fqgitdir/pid"
|
||||
server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
|
||||
|
||||
# to enable, add "mod_access", "mod_accesslog" to server.modules
|
||||
# variable above and uncomment this
|
||||
#accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
|
||||
|
||||
setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
|
||||
|
||||
cgi.assign = ( ".cgi" => "" )
|
||||
|
||||
# mimetype mapping
|
||||
mimetype.assign = (
|
||||
".pdf" => "application/pdf",
|
||||
".sig" => "application/pgp-signature",
|
||||
".spl" => "application/futuresplash",
|
||||
".class" => "application/octet-stream",
|
||||
".ps" => "application/postscript",
|
||||
".torrent" => "application/x-bittorrent",
|
||||
".dvi" => "application/x-dvi",
|
||||
".gz" => "application/x-gzip",
|
||||
".pac" => "application/x-ns-proxy-autoconfig",
|
||||
".swf" => "application/x-shockwave-flash",
|
||||
".tar.gz" => "application/x-tgz",
|
||||
".tgz" => "application/x-tgz",
|
||||
".tar" => "application/x-tar",
|
||||
".zip" => "application/zip",
|
||||
".mp3" => "audio/mpeg",
|
||||
".m3u" => "audio/x-mpegurl",
|
||||
".wma" => "audio/x-ms-wma",
|
||||
".wax" => "audio/x-ms-wax",
|
||||
".ogg" => "application/ogg",
|
||||
".wav" => "audio/x-wav",
|
||||
".gif" => "image/gif",
|
||||
".jpg" => "image/jpeg",
|
||||
".jpeg" => "image/jpeg",
|
||||
".png" => "image/png",
|
||||
".xbm" => "image/x-xbitmap",
|
||||
".xpm" => "image/x-xpixmap",
|
||||
".xwd" => "image/x-xwindowdump",
|
||||
".css" => "text/css",
|
||||
".html" => "text/html",
|
||||
".htm" => "text/html",
|
||||
".js" => "text/javascript",
|
||||
".asc" => "text/plain",
|
||||
".c" => "text/plain",
|
||||
".cpp" => "text/plain",
|
||||
".log" => "text/plain",
|
||||
".conf" => "text/plain",
|
||||
".text" => "text/plain",
|
||||
".txt" => "text/plain",
|
||||
".dtd" => "text/xml",
|
||||
".xml" => "text/xml",
|
||||
".mpeg" => "video/mpeg",
|
||||
".mpg" => "video/mpeg",
|
||||
".mov" => "video/quicktime",
|
||||
".qt" => "video/quicktime",
|
||||
".avi" => "video/x-msvideo",
|
||||
".asf" => "video/x-ms-asf",
|
||||
".asx" => "video/x-ms-asf",
|
||||
".wmv" => "video/x-ms-wmv",
|
||||
".bz2" => "application/x-bzip",
|
||||
".tbz" => "application/x-bzip-compressed-tar",
|
||||
".tar.bz2" => "application/x-bzip-compressed-tar",
|
||||
"" => "text/plain"
|
||||
)
|
||||
EOF
|
||||
test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
|
||||
}
|
||||
|
||||
apache2_conf () {
|
||||
for candidate in \
|
||||
/etc/httpd \
|
||||
/usr/lib/apache2 \
|
||||
/usr/lib/httpd ;
|
||||
do
|
||||
if test -d "$candidate/modules"
|
||||
then
|
||||
module_path="$candidate/modules"
|
||||
break
|
||||
fi
|
||||
done
|
||||
bind=
|
||||
test x"$local" = xtrue && bind='127.0.0.1:'
|
||||
echo 'text/css css' > "$fqgitdir/mime.types"
|
||||
cat > "$conf" <<EOF
|
||||
ServerName "git-instaweb"
|
||||
ServerRoot "$root"
|
||||
DocumentRoot "$root"
|
||||
ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
|
||||
CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
|
||||
PidFile "$fqgitdir/pid"
|
||||
Listen $bind$port
|
||||
EOF
|
||||
|
||||
for mod in mpm_event mpm_prefork mpm_worker
|
||||
do
|
||||
if test -e $module_path/mod_${mod}.so
|
||||
then
|
||||
echo "LoadModule ${mod}_module " \
|
||||
"$module_path/mod_${mod}.so" >> "$conf"
|
||||
# only one mpm module permitted
|
||||
break
|
||||
fi
|
||||
done
|
||||
for mod in mime dir env log_config authz_core unixd
|
||||
do
|
||||
if test -e $module_path/mod_${mod}.so
|
||||
then
|
||||
echo "LoadModule ${mod}_module " \
|
||||
"$module_path/mod_${mod}.so" >> "$conf"
|
||||
fi
|
||||
done
|
||||
cat >> "$conf" <<EOF
|
||||
TypesConfig "$fqgitdir/mime.types"
|
||||
DirectoryIndex gitweb.cgi
|
||||
EOF
|
||||
|
||||
# check to see if Dennis Stosberg's mod_perl compatibility patch
|
||||
# (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
|
||||
if test -f "$module_path/mod_perl.so" &&
|
||||
sane_grep 'MOD_PERL' "$root/gitweb.cgi" >/dev/null
|
||||
then
|
||||
# favor mod_perl if available
|
||||
cat >> "$conf" <<EOF
|
||||
LoadModule perl_module $module_path/mod_perl.so
|
||||
PerlPassEnv GIT_DIR
|
||||
PerlPassEnv GIT_EXEC_PATH
|
||||
PerlPassEnv GITWEB_CONFIG
|
||||
<Location /gitweb.cgi>
|
||||
SetHandler perl-script
|
||||
PerlResponseHandler ModPerl::Registry
|
||||
PerlOptions +ParseHeaders
|
||||
Options +ExecCGI
|
||||
</Location>
|
||||
EOF
|
||||
else
|
||||
# plain-old CGI
|
||||
resolve_full_httpd
|
||||
list_mods=$(echo "$full_httpd" | sed 's/-f$/-l/')
|
||||
$list_mods | sane_grep 'mod_cgi\.c' >/dev/null 2>&1 || \
|
||||
if test -f "$module_path/mod_cgi.so"
|
||||
then
|
||||
echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
|
||||
else
|
||||
$list_mods | grep 'mod_cgid\.c' >/dev/null 2>&1 || \
|
||||
if test -f "$module_path/mod_cgid.so"
|
||||
then
|
||||
echo "LoadModule cgid_module $module_path/mod_cgid.so" \
|
||||
>> "$conf"
|
||||
else
|
||||
echo "You have no CGI support!"
|
||||
exit 2
|
||||
fi
|
||||
echo "ScriptSock logs/gitweb.sock" >> "$conf"
|
||||
fi
|
||||
cat >> "$conf" <<EOF
|
||||
PassEnv GIT_DIR
|
||||
PassEnv GIT_EXEC_PATH
|
||||
PassEnv GITWEB_CONFIG
|
||||
AddHandler cgi-script .cgi
|
||||
<Location /gitweb.cgi>
|
||||
Options +ExecCGI
|
||||
</Location>
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
mongoose_conf() {
|
||||
cat > "$conf" <<EOF
|
||||
# Mongoose web server configuration file.
|
||||
# Lines starting with '#' and empty lines are ignored.
|
||||
# For detailed description of every option, visit
|
||||
# http://code.google.com/p/mongoose/wiki/MongooseManual
|
||||
|
||||
root $root
|
||||
ports $port
|
||||
index_files gitweb.cgi
|
||||
#ssl_cert $fqgitdir/gitweb/ssl_cert.pem
|
||||
error_log $fqgitdir/gitweb/$httpd_only/error.log
|
||||
access_log $fqgitdir/gitweb/$httpd_only/access.log
|
||||
|
||||
#cgi setup
|
||||
cgi_env PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
|
||||
cgi_interp $PERL
|
||||
cgi_ext cgi,pl
|
||||
|
||||
# mimetype mapping
|
||||
mime_types .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
|
||||
EOF
|
||||
}
|
||||
|
||||
plackup_conf () {
|
||||
# generate a standalone 'plackup' server script in $fqgitdir/gitweb
|
||||
# with embedded configuration; it does not use "$conf" file
|
||||
cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
|
||||
#!$PERL
|
||||
|
||||
# gitweb - simple web interface to track changes in git repositories
|
||||
# PSGI wrapper and server starter (see http://plackperl.org)
|
||||
|
||||
use strict;
|
||||
|
||||
use IO::Handle;
|
||||
use Plack::MIME;
|
||||
use Plack::Builder;
|
||||
use Plack::App::WrapCGI;
|
||||
use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
|
||||
|
||||
# mimetype mapping (from lighttpd_conf)
|
||||
Plack::MIME->add_type(
|
||||
".pdf" => "application/pdf",
|
||||
".sig" => "application/pgp-signature",
|
||||
".spl" => "application/futuresplash",
|
||||
".class" => "application/octet-stream",
|
||||
".ps" => "application/postscript",
|
||||
".torrent" => "application/x-bittorrent",
|
||||
".dvi" => "application/x-dvi",
|
||||
".gz" => "application/x-gzip",
|
||||
".pac" => "application/x-ns-proxy-autoconfig",
|
||||
".swf" => "application/x-shockwave-flash",
|
||||
".tar.gz" => "application/x-tgz",
|
||||
".tgz" => "application/x-tgz",
|
||||
".tar" => "application/x-tar",
|
||||
".zip" => "application/zip",
|
||||
".mp3" => "audio/mpeg",
|
||||
".m3u" => "audio/x-mpegurl",
|
||||
".wma" => "audio/x-ms-wma",
|
||||
".wax" => "audio/x-ms-wax",
|
||||
".ogg" => "application/ogg",
|
||||
".wav" => "audio/x-wav",
|
||||
".gif" => "image/gif",
|
||||
".jpg" => "image/jpeg",
|
||||
".jpeg" => "image/jpeg",
|
||||
".png" => "image/png",
|
||||
".xbm" => "image/x-xbitmap",
|
||||
".xpm" => "image/x-xpixmap",
|
||||
".xwd" => "image/x-xwindowdump",
|
||||
".css" => "text/css",
|
||||
".html" => "text/html",
|
||||
".htm" => "text/html",
|
||||
".js" => "text/javascript",
|
||||
".asc" => "text/plain",
|
||||
".c" => "text/plain",
|
||||
".cpp" => "text/plain",
|
||||
".log" => "text/plain",
|
||||
".conf" => "text/plain",
|
||||
".text" => "text/plain",
|
||||
".txt" => "text/plain",
|
||||
".dtd" => "text/xml",
|
||||
".xml" => "text/xml",
|
||||
".mpeg" => "video/mpeg",
|
||||
".mpg" => "video/mpeg",
|
||||
".mov" => "video/quicktime",
|
||||
".qt" => "video/quicktime",
|
||||
".avi" => "video/x-msvideo",
|
||||
".asf" => "video/x-ms-asf",
|
||||
".asx" => "video/x-ms-asf",
|
||||
".wmv" => "video/x-ms-wmv",
|
||||
".bz2" => "application/x-bzip",
|
||||
".tbz" => "application/x-bzip-compressed-tar",
|
||||
".tar.bz2" => "application/x-bzip-compressed-tar",
|
||||
"" => "text/plain"
|
||||
);
|
||||
|
||||
my \$app = builder {
|
||||
# to be able to override \$SIG{__WARN__} to log build time warnings
|
||||
use CGI::Carp; # it sets \$SIG{__WARN__} itself
|
||||
|
||||
my \$logdir = "$fqgitdir/gitweb/$httpd_only";
|
||||
open my \$access_log_fh, '>>', "\$logdir/access.log"
|
||||
or die "Couldn't open access log '\$logdir/access.log': \$!";
|
||||
open my \$error_log_fh, '>>', "\$logdir/error.log"
|
||||
or die "Couldn't open error log '\$logdir/error.log': \$!";
|
||||
|
||||
\$access_log_fh->autoflush(1);
|
||||
\$error_log_fh->autoflush(1);
|
||||
|
||||
# redirect build time warnings to error.log
|
||||
\$SIG{'__WARN__'} = sub {
|
||||
my \$msg = shift;
|
||||
# timestamp warning like in CGI::Carp::warn
|
||||
my \$stamp = CGI::Carp::stamp();
|
||||
\$msg =~ s/^/\$stamp/gm;
|
||||
print \$error_log_fh \$msg;
|
||||
};
|
||||
|
||||
# write errors to error.log, access to access.log
|
||||
enable 'AccessLog',
|
||||
format => "combined",
|
||||
logger => sub { print \$access_log_fh @_; };
|
||||
enable sub {
|
||||
my \$app = shift;
|
||||
sub {
|
||||
my \$env = shift;
|
||||
\$env->{'psgi.errors'} = \$error_log_fh;
|
||||
\$app->(\$env);
|
||||
}
|
||||
};
|
||||
# gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
|
||||
# because it uses 'close $fd or die...' on piped filehandle $fh
|
||||
# (which causes the parent process to wait for child to finish).
|
||||
enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
|
||||
my \$app = shift;
|
||||
sub {
|
||||
my \$env = shift;
|
||||
local \$SIG{'CHLD'} = 'DEFAULT';
|
||||
local \$SIG{'CLD'} = 'DEFAULT';
|
||||
\$app->(\$env);
|
||||
}
|
||||
};
|
||||
# serve static files, i.e. stylesheet, images, script
|
||||
enable 'Static',
|
||||
path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
|
||||
root => "$root/",
|
||||
encoding => 'utf-8'; # encoding for 'text/plain' files
|
||||
# convert CGI application to PSGI app
|
||||
Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
|
||||
};
|
||||
|
||||
# make it runnable as standalone app,
|
||||
# like it would be run via 'plackup' utility
|
||||
if (caller) {
|
||||
return \$app;
|
||||
} else {
|
||||
require Plack::Runner;
|
||||
|
||||
my \$runner = Plack::Runner->new();
|
||||
\$runner->parse_options(qw(--env deployment --port $port),
|
||||
"$local" ? qw(--host 127.0.0.1) : ());
|
||||
\$runner->run(\$app);
|
||||
}
|
||||
__END__
|
||||
EOF
|
||||
|
||||
chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
|
||||
# configuration is embedded in server script file, gitweb.psgi
|
||||
rm -f "$conf"
|
||||
}
|
||||
|
||||
python_conf() {
|
||||
# Python's builtin http.server and its CGI support is very limited.
|
||||
# CGI handler is capable of running CGI script only from inside a directory.
|
||||
# Trying to set cgi_directories=["/"] will add double slash to SCRIPT_NAME
|
||||
# and that in turn breaks gitweb's relative link generation.
|
||||
|
||||
# create a simple web root where $fqgitdir/gitweb/$httpd_only is our root
|
||||
mkdir -p "$fqgitdir/gitweb/$httpd_only/cgi-bin"
|
||||
# Python http.server follows the symlinks
|
||||
ln -sf "$root/gitweb.cgi" "$fqgitdir/gitweb/$httpd_only/cgi-bin/gitweb.cgi"
|
||||
ln -sf "$root/static" "$fqgitdir/gitweb/$httpd_only/"
|
||||
|
||||
# generate a standalone 'python http.server' script in $fqgitdir/gitweb
|
||||
# This asumes that python is in user's $PATH
|
||||
# This script is Python 2 and 3 compatible
|
||||
cat > "$fqgitdir/gitweb/gitweb.py" <<EOF
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Open log file in line buffering mode
|
||||
accesslogfile = open("$fqgitdir/gitweb/access.log", 'a', buffering=1)
|
||||
errorlogfile = open("$fqgitdir/gitweb/error.log", 'a', buffering=1)
|
||||
|
||||
# and replace our stdout and stderr with log files
|
||||
# also do a lowlevel duplicate of the logfile file descriptors so that
|
||||
# our CGI child process writes any stderr warning also to the log file
|
||||
_orig_stdout_fd = sys.stdout.fileno()
|
||||
sys.stdout.close()
|
||||
os.dup2(accesslogfile.fileno(), _orig_stdout_fd)
|
||||
sys.stdout = accesslogfile
|
||||
|
||||
_orig_stderr_fd = sys.stderr.fileno()
|
||||
sys.stderr.close()
|
||||
os.dup2(errorlogfile.fileno(), _orig_stderr_fd)
|
||||
sys.stderr = errorlogfile
|
||||
|
||||
from functools import partial
|
||||
|
||||
if sys.version_info < (3, 0): # Python 2
|
||||
from CGIHTTPServer import CGIHTTPRequestHandler
|
||||
from BaseHTTPServer import HTTPServer as ServerClass
|
||||
else: # Python 3
|
||||
from http.server import CGIHTTPRequestHandler
|
||||
from http.server import HTTPServer as ServerClass
|
||||
|
||||
|
||||
# Those environment variables will be passed to the cgi script
|
||||
os.environ.update({
|
||||
"GIT_EXEC_PATH": "$GIT_EXEC_PATH",
|
||||
"GIT_DIR": "$GIT_DIR",
|
||||
"GITWEB_CONFIG": "$GITWEB_CONFIG"
|
||||
})
|
||||
|
||||
|
||||
class GitWebRequestHandler(CGIHTTPRequestHandler):
|
||||
|
||||
def log_message(self, format, *args):
|
||||
# Write access logs to stdout
|
||||
sys.stdout.write("%s - - [%s] %s\n" %
|
||||
(self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
format%args))
|
||||
|
||||
def do_HEAD(self):
|
||||
self.redirect_path()
|
||||
CGIHTTPRequestHandler.do_HEAD(self)
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == "/":
|
||||
self.send_response(303, "See Other")
|
||||
self.send_header("Location", "/cgi-bin/gitweb.cgi")
|
||||
self.end_headers()
|
||||
return
|
||||
self.redirect_path()
|
||||
CGIHTTPRequestHandler.do_GET(self)
|
||||
|
||||
def do_POST(self):
|
||||
self.redirect_path()
|
||||
CGIHTTPRequestHandler.do_POST(self)
|
||||
|
||||
# rewrite path of every request that is not gitweb.cgi to out of cgi-bin
|
||||
def redirect_path(self):
|
||||
if not self.path.startswith("/cgi-bin/gitweb.cgi"):
|
||||
self.path = self.path.replace("/cgi-bin/", "/")
|
||||
|
||||
# gitweb.cgi is the only thing that is ever going to be run here.
|
||||
# Ignore everything else
|
||||
def is_cgi(self):
|
||||
result = False
|
||||
if self.path.startswith('/cgi-bin/gitweb.cgi'):
|
||||
result = CGIHTTPRequestHandler.is_cgi(self)
|
||||
return result
|
||||
|
||||
|
||||
bind = "127.0.0.1"
|
||||
if "$local" == "true":
|
||||
bind = "0.0.0.0"
|
||||
|
||||
# Set our http root directory
|
||||
# This is a work around for a missing directory argument in older Python versions
|
||||
# as this was added to SimpleHTTPRequestHandler in Python 3.7
|
||||
os.chdir("$fqgitdir/gitweb/$httpd_only/")
|
||||
|
||||
GitWebRequestHandler.protocol_version = "HTTP/1.0"
|
||||
httpd = ServerClass((bind, $port), GitWebRequestHandler)
|
||||
|
||||
sa = httpd.socket.getsockname()
|
||||
print("Serving HTTP on", sa[0], "port", sa[1], "...")
|
||||
httpd.serve_forever()
|
||||
EOF
|
||||
|
||||
chmod a+x "$fqgitdir/gitweb/gitweb.py"
|
||||
}
|
||||
|
||||
gitweb_conf() {
|
||||
cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
|
||||
#!/usr/bin/perl
|
||||
our \$projectroot = "$(dirname "$fqgitdir")";
|
||||
our \$git_temp = "$fqgitdir/gitweb/tmp";
|
||||
our \$projects_list = \$projectroot;
|
||||
|
||||
\$feature{'remote_heads'}{'default'} = [1];
|
||||
EOF
|
||||
}
|
||||
|
||||
configure_httpd() {
|
||||
case "$httpd" in
|
||||
*lighttpd*)
|
||||
lighttpd_conf
|
||||
;;
|
||||
*apache2*|*httpd*)
|
||||
apache2_conf
|
||||
;;
|
||||
webrick)
|
||||
webrick_conf
|
||||
;;
|
||||
*mongoose*)
|
||||
mongoose_conf
|
||||
;;
|
||||
*plackup*)
|
||||
plackup_conf
|
||||
;;
|
||||
*python*)
|
||||
python_conf
|
||||
;;
|
||||
*)
|
||||
echo "Unknown httpd specified: $httpd"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
case "$action" in
|
||||
stop)
|
||||
stop_httpd
|
||||
exit 0
|
||||
;;
|
||||
start)
|
||||
start_httpd
|
||||
exit 0
|
||||
;;
|
||||
restart)
|
||||
stop_httpd
|
||||
start_httpd
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
gitweb_conf
|
||||
|
||||
resolve_full_httpd
|
||||
mkdir -p "$fqgitdir/gitweb/$httpd_only"
|
||||
conf="$fqgitdir/gitweb/$httpd_only.conf"
|
||||
|
||||
configure_httpd
|
||||
|
||||
start_httpd
|
||||
url=http://127.0.0.1:$port
|
||||
|
||||
if test -n "$browser"; then
|
||||
httpd_is_ready && git web--browse -b "$browser" $url || echo $url
|
||||
else
|
||||
httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
|
||||
fi
|
||||
Binary file not shown.
@@ -0,0 +1,745 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano.
|
||||
#
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_STUCKLONG=t
|
||||
OPTIONS_SPEC="\
|
||||
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
|
||||
git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
|
||||
git rebase --continue | --abort | --skip | --edit-todo
|
||||
--
|
||||
Available options are
|
||||
v,verbose! display a diffstat of what changed upstream
|
||||
q,quiet! be quiet. implies --no-stat
|
||||
autostash automatically stash/stash pop before and after
|
||||
fork-point use 'merge-base --fork-point' to refine upstream
|
||||
onto=! rebase onto given branch instead of upstream
|
||||
r,rebase-merges? try to rebase merges instead of skipping them
|
||||
p,preserve-merges! try to recreate merges instead of ignoring them
|
||||
s,strategy=! use the given merge strategy
|
||||
X,strategy-option=! pass the argument through to the merge strategy
|
||||
no-ff! cherry-pick all commits, even if unchanged
|
||||
f,force-rebase! cherry-pick all commits, even if unchanged
|
||||
m,merge! use merging strategies to rebase
|
||||
i,interactive! let the user edit the list of commits to rebase
|
||||
x,exec=! add exec lines after each commit of the editable list
|
||||
k,keep-empty preserve empty commits during rebase
|
||||
allow-empty-message allow rebasing commits with empty messages
|
||||
stat! display a diffstat of what changed upstream
|
||||
n,no-stat! do not show diffstat of what changed upstream
|
||||
verify allow pre-rebase hook to run
|
||||
rerere-autoupdate allow rerere to update index with resolved conflicts
|
||||
root! rebase all reachable commits up to the root(s)
|
||||
autosquash move commits that begin with squash!/fixup! under -i
|
||||
signoff add a Signed-off-by: line to each commit
|
||||
committer-date-is-author-date! passed to 'git am'
|
||||
ignore-date! passed to 'git am'
|
||||
whitespace=! passed to 'git apply'
|
||||
ignore-whitespace! passed to 'git apply'
|
||||
C=! passed to 'git apply'
|
||||
S,gpg-sign? GPG-sign commits
|
||||
Actions:
|
||||
continue! continue
|
||||
abort! abort and check out the original branch
|
||||
skip! skip current patch and continue
|
||||
edit-todo! edit the todo list during an interactive rebase
|
||||
quit! abort but keep HEAD where it is
|
||||
show-current-patch! show the patch file being applied or merged
|
||||
reschedule-failed-exec automatically reschedule failed exec commands
|
||||
"
|
||||
. git-sh-setup
|
||||
set_reflog_action rebase
|
||||
require_work_tree_exists
|
||||
cd_to_toplevel
|
||||
|
||||
LF='
|
||||
'
|
||||
ok_to_skip_pre_rebase=
|
||||
|
||||
squash_onto=
|
||||
unset onto
|
||||
unset restrict_revision
|
||||
cmd=
|
||||
strategy=
|
||||
strategy_opts=
|
||||
do_merge=
|
||||
merge_dir="$GIT_DIR"/rebase-merge
|
||||
apply_dir="$GIT_DIR"/rebase-apply
|
||||
verbose=
|
||||
diffstat=
|
||||
test "$(git config --bool rebase.stat)" = true && diffstat=t
|
||||
autostash="$(git config --bool rebase.autostash || echo false)"
|
||||
fork_point=auto
|
||||
git_am_opt=
|
||||
git_format_patch_opt=
|
||||
rebase_root=
|
||||
force_rebase=
|
||||
allow_rerere_autoupdate=
|
||||
# Non-empty if a rebase was in progress when 'git rebase' was invoked
|
||||
in_progress=
|
||||
# One of {am, merge, interactive}
|
||||
type=
|
||||
# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
|
||||
state_dir=
|
||||
# One of {'', continue, skip, abort}, as parsed from command line
|
||||
action=
|
||||
rebase_merges=
|
||||
rebase_cousins=
|
||||
preserve_merges=
|
||||
autosquash=
|
||||
keep_empty=
|
||||
allow_empty_message=--allow-empty-message
|
||||
signoff=
|
||||
reschedule_failed_exec=
|
||||
test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
|
||||
case "$(git config --bool commit.gpgsign)" in
|
||||
true) gpg_sign_opt=-S ;;
|
||||
*) gpg_sign_opt= ;;
|
||||
esac
|
||||
test "$(git config --bool rebase.reschedulefailedexec)" = "true" &&
|
||||
reschedule_failed_exec=--reschedule-failed-exec
|
||||
. git-rebase--common
|
||||
|
||||
read_basic_state () {
|
||||
test -f "$state_dir/head-name" &&
|
||||
test -f "$state_dir/onto" &&
|
||||
head_name=$(cat "$state_dir"/head-name) &&
|
||||
onto=$(cat "$state_dir"/onto) &&
|
||||
# We always write to orig-head, but interactive rebase used to write to
|
||||
# head. Fall back to reading from head to cover for the case that the
|
||||
# user upgraded git with an ongoing interactive rebase.
|
||||
if test -f "$state_dir"/orig-head
|
||||
then
|
||||
orig_head=$(cat "$state_dir"/orig-head)
|
||||
else
|
||||
orig_head=$(cat "$state_dir"/head)
|
||||
fi &&
|
||||
test -f "$state_dir"/quiet && GIT_QUIET=t
|
||||
test -f "$state_dir"/verbose && verbose=t
|
||||
test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
|
||||
test -f "$state_dir"/strategy_opts &&
|
||||
strategy_opts="$(cat "$state_dir"/strategy_opts)"
|
||||
test -f "$state_dir"/allow_rerere_autoupdate &&
|
||||
allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
|
||||
test -f "$state_dir"/gpg_sign_opt &&
|
||||
gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
|
||||
test -f "$state_dir"/signoff && {
|
||||
signoff="$(cat "$state_dir"/signoff)"
|
||||
force_rebase=t
|
||||
}
|
||||
test -f "$state_dir"/reschedule-failed-exec &&
|
||||
reschedule_failed_exec=t
|
||||
}
|
||||
|
||||
finish_rebase () {
|
||||
rm -f "$(git rev-parse --git-path REBASE_HEAD)"
|
||||
apply_autostash &&
|
||||
{ git gc --auto || true; } &&
|
||||
rm -rf "$state_dir"
|
||||
}
|
||||
|
||||
run_specific_rebase () {
|
||||
if [ "$interactive_rebase" = implied ]; then
|
||||
GIT_SEQUENCE_EDITOR=:
|
||||
export GIT_SEQUENCE_EDITOR
|
||||
autosquash=
|
||||
fi
|
||||
|
||||
if test -n "$interactive_rebase" -a -z "$preserve_merges"
|
||||
then
|
||||
. git-legacy-rebase--$type
|
||||
|
||||
git_rebase__$type
|
||||
else
|
||||
. git-rebase--$type
|
||||
|
||||
if test -z "$preserve_merges"
|
||||
then
|
||||
git_rebase__$type
|
||||
else
|
||||
git_rebase__preserve_merges
|
||||
fi
|
||||
fi
|
||||
|
||||
ret=$?
|
||||
if test $ret -eq 0
|
||||
then
|
||||
finish_rebase
|
||||
elif test $ret -eq 2 # special exit status for rebase -p
|
||||
then
|
||||
apply_autostash &&
|
||||
rm -rf "$state_dir" &&
|
||||
if test -n "$interactive_rebase" -a -z "$preserve_merges"
|
||||
then
|
||||
die "error: nothing to do"
|
||||
else
|
||||
die "Nothing to do"
|
||||
fi
|
||||
fi
|
||||
exit $ret
|
||||
}
|
||||
|
||||
run_pre_rebase_hook () {
|
||||
if test -z "$ok_to_skip_pre_rebase" &&
|
||||
test -x "$(git rev-parse --git-path hooks/pre-rebase)"
|
||||
then
|
||||
"$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
|
||||
die "$(gettext "The pre-rebase hook refused to rebase.")"
|
||||
fi
|
||||
}
|
||||
|
||||
test -f "$apply_dir"/applying &&
|
||||
die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")"
|
||||
|
||||
if test -d "$apply_dir"
|
||||
then
|
||||
type=am
|
||||
state_dir="$apply_dir"
|
||||
elif test -d "$merge_dir"
|
||||
then
|
||||
type=interactive
|
||||
if test -d "$merge_dir"/rewritten
|
||||
then
|
||||
type=preserve-merges
|
||||
interactive_rebase=explicit
|
||||
preserve_merges=t
|
||||
elif test -f "$merge_dir"/interactive
|
||||
then
|
||||
interactive_rebase=explicit
|
||||
fi
|
||||
state_dir="$merge_dir"
|
||||
fi
|
||||
test -n "$type" && in_progress=t
|
||||
|
||||
total_argc=$#
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--no-verify)
|
||||
ok_to_skip_pre_rebase=yes
|
||||
;;
|
||||
--verify)
|
||||
ok_to_skip_pre_rebase=
|
||||
;;
|
||||
--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
|
||||
test $total_argc -eq 2 || usage
|
||||
action=${1##--}
|
||||
;;
|
||||
--onto=*)
|
||||
onto="${1#--onto=}"
|
||||
;;
|
||||
--exec=*)
|
||||
cmd="${cmd}exec ${1#--exec=}${LF}"
|
||||
test -z "$interactive_rebase" && interactive_rebase=implied
|
||||
;;
|
||||
--interactive)
|
||||
interactive_rebase=explicit
|
||||
;;
|
||||
--keep-empty)
|
||||
keep_empty=yes
|
||||
;;
|
||||
--allow-empty-message)
|
||||
allow_empty_message=--allow-empty-message
|
||||
;;
|
||||
--no-keep-empty)
|
||||
keep_empty=
|
||||
;;
|
||||
--rebase-merges)
|
||||
rebase_merges=t
|
||||
test -z "$interactive_rebase" && interactive_rebase=implied
|
||||
;;
|
||||
--rebase-merges=*)
|
||||
rebase_merges=t
|
||||
case "${1#*=}" in
|
||||
rebase-cousins) rebase_cousins=t;;
|
||||
no-rebase-cousins) rebase_cousins=;;
|
||||
*) die "Unknown mode: $1";;
|
||||
esac
|
||||
test -z "$interactive_rebase" && interactive_rebase=implied
|
||||
;;
|
||||
--preserve-merges)
|
||||
preserve_merges=t
|
||||
test -z "$interactive_rebase" && interactive_rebase=implied
|
||||
;;
|
||||
--autosquash)
|
||||
autosquash=t
|
||||
;;
|
||||
--no-autosquash)
|
||||
autosquash=
|
||||
;;
|
||||
--fork-point)
|
||||
fork_point=t
|
||||
;;
|
||||
--no-fork-point)
|
||||
fork_point=
|
||||
;;
|
||||
--merge)
|
||||
do_merge=t
|
||||
;;
|
||||
--strategy-option=*)
|
||||
strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)"
|
||||
do_merge=t
|
||||
test -z "$strategy" && strategy=recursive
|
||||
;;
|
||||
--strategy=*)
|
||||
strategy="${1#--strategy=}"
|
||||
do_merge=t
|
||||
;;
|
||||
--no-stat)
|
||||
diffstat=
|
||||
;;
|
||||
--stat)
|
||||
diffstat=t
|
||||
;;
|
||||
--autostash)
|
||||
autostash=true
|
||||
;;
|
||||
--no-autostash)
|
||||
autostash=false
|
||||
;;
|
||||
--verbose)
|
||||
verbose=t
|
||||
diffstat=t
|
||||
GIT_QUIET=
|
||||
;;
|
||||
--quiet)
|
||||
GIT_QUIET=t
|
||||
git_am_opt="$git_am_opt -q"
|
||||
verbose=
|
||||
diffstat=
|
||||
;;
|
||||
--whitespace=*)
|
||||
git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}"
|
||||
case "${1#--whitespace=}" in
|
||||
fix|strip)
|
||||
force_rebase=t
|
||||
;;
|
||||
warn|nowarn|error|error-all)
|
||||
;; # okay, known whitespace option
|
||||
*)
|
||||
die "fatal: Invalid whitespace option: '${1#*=}'"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--ignore-whitespace)
|
||||
git_am_opt="$git_am_opt $1"
|
||||
;;
|
||||
--signoff)
|
||||
signoff=--signoff
|
||||
;;
|
||||
--no-signoff)
|
||||
signoff=
|
||||
;;
|
||||
--committer-date-is-author-date|--ignore-date)
|
||||
git_am_opt="$git_am_opt $1"
|
||||
force_rebase=t
|
||||
;;
|
||||
-C*[!0-9]*)
|
||||
die "fatal: switch \`C' expects a numerical value"
|
||||
;;
|
||||
-C*)
|
||||
git_am_opt="$git_am_opt $1"
|
||||
;;
|
||||
--root)
|
||||
rebase_root=t
|
||||
;;
|
||||
--force-rebase|--no-ff)
|
||||
force_rebase=t
|
||||
;;
|
||||
--rerere-autoupdate|--no-rerere-autoupdate)
|
||||
allow_rerere_autoupdate="$1"
|
||||
;;
|
||||
--gpg-sign)
|
||||
gpg_sign_opt=-S
|
||||
;;
|
||||
--gpg-sign=*)
|
||||
gpg_sign_opt="-S${1#--gpg-sign=}"
|
||||
;;
|
||||
--reschedule-failed-exec)
|
||||
reschedule_failed_exec=--reschedule-failed-exec
|
||||
;;
|
||||
--no-reschedule-failed-exec)
|
||||
reschedule_failed_exec=
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
test $# -gt 2 && usage
|
||||
|
||||
if test -n "$action"
|
||||
then
|
||||
test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
|
||||
# Only interactive rebase uses detailed reflog messages
|
||||
if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase
|
||||
then
|
||||
GIT_REFLOG_ACTION="rebase -i ($action)"
|
||||
export GIT_REFLOG_ACTION
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$action" = "edit-todo" && test -z "$interactive_rebase"
|
||||
then
|
||||
die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
|
||||
fi
|
||||
|
||||
case "$action" in
|
||||
continue)
|
||||
# Sanity check
|
||||
git rev-parse --verify HEAD >/dev/null ||
|
||||
die "$(gettext "Cannot read HEAD")"
|
||||
git update-index --ignore-submodules --refresh &&
|
||||
git diff-files --quiet --ignore-submodules || {
|
||||
echo "$(gettext "You must edit all merge conflicts and then
|
||||
mark them as resolved using git add")"
|
||||
exit 1
|
||||
}
|
||||
read_basic_state
|
||||
run_specific_rebase
|
||||
;;
|
||||
skip)
|
||||
output git reset --hard HEAD || exit $?
|
||||
read_basic_state
|
||||
run_specific_rebase
|
||||
;;
|
||||
abort)
|
||||
git rerere clear
|
||||
read_basic_state
|
||||
case "$head_name" in
|
||||
refs/*)
|
||||
git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
|
||||
die "$(eval_gettext "Could not move back to \$head_name")"
|
||||
;;
|
||||
esac
|
||||
output git reset --hard $orig_head
|
||||
finish_rebase
|
||||
exit
|
||||
;;
|
||||
quit)
|
||||
exec rm -rf "$state_dir"
|
||||
;;
|
||||
edit-todo)
|
||||
run_specific_rebase
|
||||
;;
|
||||
show-current-patch)
|
||||
run_specific_rebase
|
||||
die "BUG: run_specific_rebase is not supposed to return here"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Make sure no rebase is in progress
|
||||
if test -n "$in_progress"
|
||||
then
|
||||
state_dir_base=${state_dir##*/}
|
||||
cmd_live_rebase="git rebase (--continue | --abort | --skip)"
|
||||
cmd_clear_stale_rebase="rm -fr \"$state_dir\""
|
||||
die "
|
||||
$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
|
||||
I wonder if you are in the middle of another rebase. If that is the
|
||||
case, please try
|
||||
$cmd_live_rebase
|
||||
If that is not the case, please
|
||||
$cmd_clear_stale_rebase
|
||||
and run me again. I am stopping in case you still have something
|
||||
valuable there.')"
|
||||
fi
|
||||
|
||||
if test -n "$rebase_root" && test -z "$onto"
|
||||
then
|
||||
test -z "$interactive_rebase" && interactive_rebase=implied
|
||||
fi
|
||||
|
||||
if test -n "$keep_empty"
|
||||
then
|
||||
test -z "$interactive_rebase" && interactive_rebase=implied
|
||||
fi
|
||||
|
||||
actually_interactive=
|
||||
if test -n "$interactive_rebase"
|
||||
then
|
||||
if test -z "$preserve_merges"
|
||||
then
|
||||
type=interactive
|
||||
else
|
||||
type=preserve-merges
|
||||
fi
|
||||
actually_interactive=t
|
||||
state_dir="$merge_dir"
|
||||
elif test -n "$do_merge"
|
||||
then
|
||||
interactive_rebase=implied
|
||||
type=interactive
|
||||
state_dir="$merge_dir"
|
||||
else
|
||||
type=am
|
||||
state_dir="$apply_dir"
|
||||
fi
|
||||
|
||||
if test -t 2 && test -z "$GIT_QUIET"
|
||||
then
|
||||
git_format_patch_opt="$git_format_patch_opt --progress"
|
||||
fi
|
||||
|
||||
incompatible_opts=$(echo " $git_am_opt " | \
|
||||
sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
|
||||
if test -n "$incompatible_opts"
|
||||
then
|
||||
if test -n "$actually_interactive" || test "$do_merge"
|
||||
then
|
||||
die "$(gettext "fatal: cannot combine am options with either interactive or merge options")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$signoff"
|
||||
then
|
||||
test -n "$preserve_merges" &&
|
||||
die "$(gettext "fatal: cannot combine '--signoff' with '--preserve-merges'")"
|
||||
git_am_opt="$git_am_opt $signoff"
|
||||
force_rebase=t
|
||||
fi
|
||||
|
||||
if test -n "$preserve_merges"
|
||||
then
|
||||
# Note: incompatibility with --signoff handled in signoff block above
|
||||
# Note: incompatibility with --interactive is just a strong warning;
|
||||
# git-rebase.txt caveats with "unless you know what you are doing"
|
||||
test -n "$rebase_merges" &&
|
||||
die "$(gettext "fatal: cannot combine '--preserve-merges' with '--rebase-merges'")"
|
||||
|
||||
test -n "$reschedule_failed_exec" &&
|
||||
die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")"
|
||||
fi
|
||||
|
||||
if test -n "$rebase_merges"
|
||||
then
|
||||
test -n "$strategy_opts" &&
|
||||
die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy-option'")"
|
||||
test -n "$strategy" &&
|
||||
die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy'")"
|
||||
fi
|
||||
|
||||
if test -z "$rebase_root"
|
||||
then
|
||||
case "$#" in
|
||||
0)
|
||||
if ! upstream_name=$(git rev-parse --symbolic-full-name \
|
||||
--verify -q @{upstream} 2>/dev/null)
|
||||
then
|
||||
. git-parse-remote
|
||||
error_on_missing_default_upstream "rebase" "rebase" \
|
||||
"against" "git rebase $(gettext '<branch>')"
|
||||
fi
|
||||
|
||||
test "$fork_point" = auto && fork_point=t
|
||||
;;
|
||||
*) upstream_name="$1"
|
||||
if test "$upstream_name" = "-"
|
||||
then
|
||||
upstream_name="@{-1}"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
upstream=$(peel_committish "${upstream_name}") ||
|
||||
die "$(eval_gettext "invalid upstream '\$upstream_name'")"
|
||||
upstream_arg="$upstream_name"
|
||||
else
|
||||
if test -z "$onto"
|
||||
then
|
||||
empty_tree=$(git hash-object -t tree /dev/null)
|
||||
onto=$(git commit-tree $empty_tree </dev/null)
|
||||
squash_onto="$onto"
|
||||
fi
|
||||
unset upstream_name
|
||||
unset upstream
|
||||
test $# -gt 1 && usage
|
||||
upstream_arg=--root
|
||||
fi
|
||||
|
||||
# Make sure the branch to rebase onto is valid.
|
||||
onto_name=${onto-"$upstream_name"}
|
||||
case "$onto_name" in
|
||||
*...*)
|
||||
if left=${onto_name%...*} right=${onto_name#*...} &&
|
||||
onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
|
||||
then
|
||||
case "$onto" in
|
||||
?*"$LF"?*)
|
||||
die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
|
||||
;;
|
||||
'')
|
||||
die "$(eval_gettext "\$onto_name: there is no merge base")"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
die "$(eval_gettext "\$onto_name: there is no merge base")"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
onto=$(peel_committish "$onto_name") ||
|
||||
die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
|
||||
;;
|
||||
esac
|
||||
|
||||
# If the branch to rebase is given, that is the branch we will rebase
|
||||
# $branch_name -- branch/commit being rebased, or HEAD (already detached)
|
||||
# $orig_head -- commit object name of tip of the branch before rebasing
|
||||
# $head_name -- refs/heads/<that-branch> or "detached HEAD"
|
||||
switch_to=
|
||||
case "$#" in
|
||||
1)
|
||||
# Is it "rebase other $branchname" or "rebase other $commit"?
|
||||
branch_name="$1"
|
||||
switch_to="$1"
|
||||
|
||||
# Is it a local branch?
|
||||
if git show-ref --verify --quiet -- "refs/heads/$branch_name" &&
|
||||
orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name")
|
||||
then
|
||||
head_name="refs/heads/$branch_name"
|
||||
# If not is it a valid ref (branch or commit)?
|
||||
elif orig_head=$(git rev-parse -q --verify "$branch_name")
|
||||
then
|
||||
head_name="detached HEAD"
|
||||
|
||||
else
|
||||
die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")"
|
||||
fi
|
||||
;;
|
||||
0)
|
||||
# Do not need to switch branches, we are already on it.
|
||||
if branch_name=$(git symbolic-ref -q HEAD)
|
||||
then
|
||||
head_name=$branch_name
|
||||
branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)')
|
||||
else
|
||||
head_name="detached HEAD"
|
||||
branch_name=HEAD
|
||||
fi
|
||||
orig_head=$(git rev-parse --verify HEAD) || exit
|
||||
;;
|
||||
*)
|
||||
die "BUG: unexpected number of arguments left to parse"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "$fork_point" = t
|
||||
then
|
||||
new_upstream=$(git merge-base --fork-point "$upstream_name" \
|
||||
"${switch_to:-HEAD}")
|
||||
if test -n "$new_upstream"
|
||||
then
|
||||
restrict_revision=$new_upstream
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
|
||||
then
|
||||
stash_sha1=$(git stash create "autostash") ||
|
||||
die "$(gettext 'Cannot autostash')"
|
||||
|
||||
mkdir -p "$state_dir" &&
|
||||
echo $stash_sha1 >"$state_dir/autostash" &&
|
||||
stash_abbrev=$(git rev-parse --short $stash_sha1) &&
|
||||
echo "$(eval_gettext 'Created autostash: $stash_abbrev')" &&
|
||||
git reset --hard
|
||||
fi
|
||||
|
||||
require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
|
||||
|
||||
# Now we are rebasing commits $upstream..$orig_head (or with --root,
|
||||
# everything leading up to $orig_head) on top of $onto
|
||||
|
||||
# Check if we are already based on $onto with linear history,
|
||||
# but this should be done only when upstream and onto are the same
|
||||
# and if this is not an interactive rebase.
|
||||
mb=$(git merge-base "$onto" "$orig_head")
|
||||
if test -z "$actually_interactive" && test "$upstream" = "$onto" &&
|
||||
test "$mb" = "$onto" && test -z "$restrict_revision" &&
|
||||
# linear history?
|
||||
! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
|
||||
then
|
||||
if test -z "$force_rebase"
|
||||
then
|
||||
# Lazily switch to the target branch if needed...
|
||||
test -z "$switch_to" ||
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
|
||||
git checkout -q "$switch_to" --
|
||||
if test "$branch_name" = "HEAD" &&
|
||||
! git symbolic-ref -q HEAD
|
||||
then
|
||||
say "$(eval_gettext "HEAD is up to date.")"
|
||||
else
|
||||
say "$(eval_gettext "Current branch \$branch_name is up to date.")"
|
||||
fi
|
||||
finish_rebase
|
||||
exit 0
|
||||
else
|
||||
if test "$branch_name" = "HEAD" &&
|
||||
! git symbolic-ref -q HEAD
|
||||
then
|
||||
say "$(eval_gettext "HEAD is up to date, rebase forced.")"
|
||||
else
|
||||
say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# If a hook exists, give it a chance to interrupt
|
||||
run_pre_rebase_hook "$upstream_arg" "$@"
|
||||
|
||||
if test -n "$diffstat"
|
||||
then
|
||||
if test -n "$verbose"
|
||||
then
|
||||
if test -z "$mb"
|
||||
then
|
||||
echo "$(eval_gettext "Changes to \$onto:")"
|
||||
else
|
||||
echo "$(eval_gettext "Changes from \$mb to \$onto:")"
|
||||
fi
|
||||
fi
|
||||
mb_tree="${mb:-$(git hash-object -t tree /dev/null)}"
|
||||
# We want color (if set), but no pager
|
||||
GIT_PAGER='' git diff --stat --summary "$mb_tree" "$onto"
|
||||
fi
|
||||
|
||||
if test -z "$actually_interactive" && test "$mb" = "$orig_head"
|
||||
then
|
||||
say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
|
||||
git checkout -q "$onto^0" || die "could not detach HEAD"
|
||||
# If the $onto is a proper descendant of the tip of the branch, then
|
||||
# we just fast-forwarded.
|
||||
git update-ref ORIG_HEAD $orig_head
|
||||
move_to_original_branch
|
||||
finish_rebase
|
||||
exit 0
|
||||
fi
|
||||
|
||||
test -n "$interactive_rebase" && run_specific_rebase
|
||||
|
||||
# Detach HEAD and reset the tree
|
||||
say "$(gettext "First, rewinding head to replay your work on top of it...")"
|
||||
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
|
||||
git checkout -q "$onto^0" || die "could not detach HEAD"
|
||||
git update-ref ORIG_HEAD $orig_head
|
||||
|
||||
if test -n "$rebase_root"
|
||||
then
|
||||
revisions="$onto..$orig_head"
|
||||
else
|
||||
revisions="${restrict_revision-$upstream}..$orig_head"
|
||||
fi
|
||||
|
||||
run_specific_rebase
|
||||
@@ -0,0 +1,283 @@
|
||||
# This shell script fragment is sourced by git-rebase to implement
|
||||
# its interactive mode. "git rebase --interactive" makes it easy
|
||||
# to fix up commits in the middle of a series and rearrange commits.
|
||||
#
|
||||
# Copyright (c) 2006 Johannes E. Schindelin
|
||||
#
|
||||
# The original idea comes from Eric W. Biederman, in
|
||||
# https://public-inbox.org/git/m1odwkyuf5.fsf_-_@ebiederm.dsl.xmission.com/
|
||||
#
|
||||
# The file containing rebase commands, comments, and empty lines.
|
||||
# This file is created by "git rebase -i" then edited by the user. As
|
||||
# the lines are processed, they are removed from the front of this
|
||||
# file and written to the tail of $done.
|
||||
todo="$state_dir"/git-rebase-todo
|
||||
|
||||
GIT_CHERRY_PICK_HELP="$resolvemsg"
|
||||
export GIT_CHERRY_PICK_HELP
|
||||
|
||||
comment_char=$(git config --get core.commentchar 2>/dev/null)
|
||||
case "$comment_char" in
|
||||
'' | auto)
|
||||
comment_char="#"
|
||||
;;
|
||||
?)
|
||||
;;
|
||||
*)
|
||||
comment_char=$(echo "$comment_char" | cut -c1)
|
||||
;;
|
||||
esac
|
||||
|
||||
orig_reflog_action="$GIT_REFLOG_ACTION"
|
||||
|
||||
comment_for_reflog () {
|
||||
case "$orig_reflog_action" in
|
||||
''|rebase*)
|
||||
GIT_REFLOG_ACTION="rebase -i ($1)"
|
||||
export GIT_REFLOG_ACTION
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
append_todo_help () {
|
||||
gettext "
|
||||
Commands:
|
||||
p, pick <commit> = use commit
|
||||
r, reword <commit> = use commit, but edit the commit message
|
||||
e, edit <commit> = use commit, but stop for amending
|
||||
s, squash <commit> = use commit, but meld into previous commit
|
||||
f, fixup <commit> = like \"squash\", but discard this commit's log message
|
||||
x, exec <command> = run command (the rest of the line) using shell
|
||||
d, drop <commit> = remove commit
|
||||
l, label <label> = label current HEAD with a name
|
||||
t, reset <label> = reset HEAD to a label
|
||||
m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
|
||||
. create a merge commit using the original merge commit's
|
||||
. message (or the oneline, if no original merge commit was
|
||||
. specified). Use -c <commit> to reword the commit message.
|
||||
|
||||
These lines can be re-ordered; they are executed from top to bottom.
|
||||
" | git stripspace --comment-lines >>"$todo"
|
||||
|
||||
if test $(get_missing_commit_check_level) = error
|
||||
then
|
||||
gettext "
|
||||
Do not remove any line. Use 'drop' explicitly to remove a commit.
|
||||
" | git stripspace --comment-lines >>"$todo"
|
||||
else
|
||||
gettext "
|
||||
If you remove a line here THAT COMMIT WILL BE LOST.
|
||||
" | git stripspace --comment-lines >>"$todo"
|
||||
fi
|
||||
}
|
||||
|
||||
die_abort () {
|
||||
apply_autostash
|
||||
rm -rf "$state_dir"
|
||||
die "$1"
|
||||
}
|
||||
|
||||
has_action () {
|
||||
test -n "$(git stripspace --strip-comments <"$1")"
|
||||
}
|
||||
|
||||
git_sequence_editor () {
|
||||
if test -z "$GIT_SEQUENCE_EDITOR"
|
||||
then
|
||||
GIT_SEQUENCE_EDITOR="$(git config sequence.editor)"
|
||||
if [ -z "$GIT_SEQUENCE_EDITOR" ]
|
||||
then
|
||||
GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $?
|
||||
fi
|
||||
fi
|
||||
|
||||
eval "$GIT_SEQUENCE_EDITOR" '"$@"'
|
||||
}
|
||||
|
||||
expand_todo_ids() {
|
||||
git rebase--interactive --expand-ids
|
||||
}
|
||||
|
||||
collapse_todo_ids() {
|
||||
git rebase--interactive --shorten-ids
|
||||
}
|
||||
|
||||
# Switch to the branch in $into and notify it in the reflog
|
||||
checkout_onto () {
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
|
||||
output git checkout $onto || die_abort "$(gettext "could not detach HEAD")"
|
||||
git update-ref ORIG_HEAD $orig_head
|
||||
}
|
||||
|
||||
get_missing_commit_check_level () {
|
||||
check_level=$(git config --get rebase.missingCommitsCheck)
|
||||
check_level=${check_level:-ignore}
|
||||
# Don't be case sensitive
|
||||
printf '%s' "$check_level" | tr 'A-Z' 'a-z'
|
||||
}
|
||||
|
||||
# Initiate an action. If the cannot be any
|
||||
# further action it may exec a command
|
||||
# or exit and not return.
|
||||
#
|
||||
# TODO: Consider a cleaner return model so it
|
||||
# never exits and always return 0 if process
|
||||
# is complete.
|
||||
#
|
||||
# Parameter 1 is the action to initiate.
|
||||
#
|
||||
# Returns 0 if the action was able to complete
|
||||
# and if 1 if further processing is required.
|
||||
initiate_action () {
|
||||
case "$1" in
|
||||
continue)
|
||||
exec git rebase--interactive ${force_rebase:+--no-ff} $allow_empty_message \
|
||||
--continue
|
||||
;;
|
||||
skip)
|
||||
git rerere clear
|
||||
exec git rebase--interactive ${force_rebase:+--no-ff} $allow_empty_message \
|
||||
--continue
|
||||
;;
|
||||
edit-todo)
|
||||
git stripspace --strip-comments <"$todo" >"$todo".new
|
||||
mv -f "$todo".new "$todo"
|
||||
collapse_todo_ids
|
||||
append_todo_help
|
||||
gettext "
|
||||
You are editing the todo file of an ongoing interactive rebase.
|
||||
To continue rebase after editing, run:
|
||||
git rebase --continue
|
||||
|
||||
" | git stripspace --comment-lines >>"$todo"
|
||||
|
||||
git_sequence_editor "$todo" ||
|
||||
die "$(gettext "Could not execute editor")"
|
||||
expand_todo_ids
|
||||
|
||||
exit
|
||||
;;
|
||||
show-current-patch)
|
||||
exec git show REBASE_HEAD --
|
||||
;;
|
||||
*)
|
||||
return 1 # continue
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
setup_reflog_action () {
|
||||
comment_for_reflog start
|
||||
|
||||
if test ! -z "$switch_to"
|
||||
then
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
|
||||
output git checkout "$switch_to" -- ||
|
||||
die "$(eval_gettext "Could not checkout \$switch_to")"
|
||||
|
||||
comment_for_reflog start
|
||||
fi
|
||||
}
|
||||
|
||||
init_basic_state () {
|
||||
orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
|
||||
mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
|
||||
rm -f "$(git rev-parse --git-path REBASE_HEAD)"
|
||||
|
||||
: > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
|
||||
write_basic_state
|
||||
}
|
||||
|
||||
init_revisions_and_shortrevisions () {
|
||||
shorthead=$(git rev-parse --short $orig_head)
|
||||
shortonto=$(git rev-parse --short $onto)
|
||||
if test -z "$rebase_root"
|
||||
# this is now equivalent to ! -z "$upstream"
|
||||
then
|
||||
shortupstream=$(git rev-parse --short $upstream)
|
||||
revisions=$upstream...$orig_head
|
||||
shortrevisions=$shortupstream..$shorthead
|
||||
else
|
||||
revisions=$onto...$orig_head
|
||||
shortrevisions=$shorthead
|
||||
test -z "$squash_onto" ||
|
||||
echo "$squash_onto" >"$state_dir"/squash-onto
|
||||
fi
|
||||
}
|
||||
|
||||
complete_action() {
|
||||
test -s "$todo" || echo noop >> "$todo"
|
||||
test -z "$autosquash" || git rebase--interactive --rearrange-squash || exit
|
||||
test -n "$cmd" && git rebase--interactive --add-exec-commands --cmd "$cmd"
|
||||
|
||||
todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
|
||||
todocount=${todocount##* }
|
||||
|
||||
cat >>"$todo" <<EOF
|
||||
|
||||
$comment_char $(eval_ngettext \
|
||||
"Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
|
||||
"Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
|
||||
"$todocount")
|
||||
EOF
|
||||
append_todo_help
|
||||
gettext "
|
||||
However, if you remove everything, the rebase will be aborted.
|
||||
|
||||
" | git stripspace --comment-lines >>"$todo"
|
||||
|
||||
if test -z "$keep_empty"
|
||||
then
|
||||
printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo"
|
||||
fi
|
||||
|
||||
|
||||
has_action "$todo" ||
|
||||
return 2
|
||||
|
||||
cp "$todo" "$todo".backup
|
||||
collapse_todo_ids
|
||||
git_sequence_editor "$todo" ||
|
||||
die_abort "$(gettext "Could not execute editor")"
|
||||
|
||||
has_action "$todo" ||
|
||||
return 2
|
||||
|
||||
git rebase--interactive --check-todo-list || {
|
||||
ret=$?
|
||||
checkout_onto
|
||||
exit $ret
|
||||
}
|
||||
|
||||
expand_todo_ids
|
||||
|
||||
test -n "$force_rebase" ||
|
||||
onto="$(git rebase--interactive --skip-unnecessary-picks)" ||
|
||||
die "Could not skip unnecessary pick commands"
|
||||
|
||||
checkout_onto
|
||||
require_clean_work_tree "rebase"
|
||||
exec git rebase--interactive ${force_rebase:+--no-ff} $allow_empty_message \
|
||||
--continue
|
||||
}
|
||||
|
||||
git_rebase__interactive () {
|
||||
initiate_action "$action"
|
||||
ret=$?
|
||||
if test $ret = 0; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
setup_reflog_action
|
||||
init_basic_state
|
||||
|
||||
init_revisions_and_shortrevisions
|
||||
|
||||
git rebase--interactive --make-script ${keep_empty:+--keep-empty} \
|
||||
${rebase_merges:+--rebase-merges} \
|
||||
${rebase_cousins:+--rebase-cousins} \
|
||||
$revisions ${restrict_revision+^$restrict_revision} >"$todo" ||
|
||||
die "$(gettext "Could not generate todo list")"
|
||||
|
||||
complete_action
|
||||
}
|
||||
@@ -0,0 +1,797 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2007, Nanako Shiraishi
|
||||
|
||||
dashless=$(basename "$0" | sed -e 's/-/ /')
|
||||
USAGE="list [<options>]
|
||||
or: $dashless show [<stash>]
|
||||
or: $dashless drop [-q|--quiet] [<stash>]
|
||||
or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
|
||||
or: $dashless branch <branchname> [<stash>]
|
||||
or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
|
||||
[-u|--include-untracked] [-a|--all] [<message>]
|
||||
or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
|
||||
[-u|--include-untracked] [-a|--all] [-m <message>]
|
||||
[-- <pathspec>...]]
|
||||
or: $dashless clear"
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_SPEC=
|
||||
START_DIR=$(pwd)
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
prefix=$(git rev-parse --show-prefix) || exit 1
|
||||
cd_to_toplevel
|
||||
|
||||
TMP="$GIT_DIR/.git-stash.$$"
|
||||
TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
|
||||
trap 'rm -f "$TMP-"* "$TMPindex"' 0
|
||||
|
||||
ref_stash=refs/stash
|
||||
|
||||
if git config --get-colorbool color.interactive; then
|
||||
help_color="$(git config --get-color color.interactive.help 'red bold')"
|
||||
reset_color="$(git config --get-color '' reset)"
|
||||
else
|
||||
help_color=
|
||||
reset_color=
|
||||
fi
|
||||
|
||||
no_changes () {
|
||||
git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
|
||||
git diff-files --quiet --ignore-submodules -- "$@" &&
|
||||
(test -z "$untracked" || test -z "$(untracked_files "$@")")
|
||||
}
|
||||
|
||||
untracked_files () {
|
||||
if test "$1" = "-z"
|
||||
then
|
||||
shift
|
||||
z=-z
|
||||
else
|
||||
z=
|
||||
fi
|
||||
excl_opt=--exclude-standard
|
||||
test "$untracked" = "all" && excl_opt=
|
||||
git ls-files -o $z $excl_opt -- "$@"
|
||||
}
|
||||
|
||||
prepare_fallback_ident () {
|
||||
if ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1
|
||||
then
|
||||
GIT_AUTHOR_NAME="git stash"
|
||||
GIT_AUTHOR_EMAIL=git@stash
|
||||
GIT_COMMITTER_NAME="git stash"
|
||||
GIT_COMMITTER_EMAIL=git@stash
|
||||
export GIT_AUTHOR_NAME
|
||||
export GIT_AUTHOR_EMAIL
|
||||
export GIT_COMMITTER_NAME
|
||||
export GIT_COMMITTER_EMAIL
|
||||
fi
|
||||
}
|
||||
|
||||
clear_stash () {
|
||||
if test $# != 0
|
||||
then
|
||||
die "$(gettext "git stash clear with parameters is unimplemented")"
|
||||
fi
|
||||
if current=$(git rev-parse --verify --quiet $ref_stash)
|
||||
then
|
||||
git update-ref -d $ref_stash $current
|
||||
fi
|
||||
}
|
||||
|
||||
maybe_quiet () {
|
||||
case "$1" in
|
||||
--keep-stdout)
|
||||
shift
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
eval "$@" 2>/dev/null
|
||||
else
|
||||
eval "$@"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
eval "$@" >/dev/null 2>&1
|
||||
else
|
||||
eval "$@"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
create_stash () {
|
||||
|
||||
prepare_fallback_ident
|
||||
|
||||
stash_msg=
|
||||
untracked=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-m|--message)
|
||||
shift
|
||||
stash_msg=${1?"BUG: create_stash () -m requires an argument"}
|
||||
;;
|
||||
-m*)
|
||||
stash_msg=${1#-m}
|
||||
;;
|
||||
--message=*)
|
||||
stash_msg=${1#--message=}
|
||||
;;
|
||||
-u|--include-untracked)
|
||||
shift
|
||||
untracked=${1?"BUG: create_stash () -u requires an argument"}
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
git update-index -q --refresh
|
||||
if maybe_quiet no_changes "$@"
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# state of the base commit
|
||||
if b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)
|
||||
then
|
||||
head=$(git rev-list --oneline -n 1 HEAD --)
|
||||
elif test -n "$GIT_QUIET"
|
||||
then
|
||||
exit 1
|
||||
else
|
||||
die "$(gettext "You do not have the initial commit yet")"
|
||||
fi
|
||||
|
||||
if branch=$(git symbolic-ref -q HEAD)
|
||||
then
|
||||
branch=${branch#refs/heads/}
|
||||
else
|
||||
branch='(no branch)'
|
||||
fi
|
||||
msg=$(printf '%s: %s' "$branch" "$head")
|
||||
|
||||
# state of the index
|
||||
i_tree=$(git write-tree) &&
|
||||
i_commit=$(printf 'index on %s\n' "$msg" |
|
||||
git commit-tree $i_tree -p $b_commit) ||
|
||||
die "$(gettext "Cannot save the current index state")"
|
||||
|
||||
if test -n "$untracked"
|
||||
then
|
||||
# Untracked files are stored by themselves in a parentless commit, for
|
||||
# ease of unpacking later.
|
||||
u_commit=$(
|
||||
untracked_files -z "$@" | (
|
||||
GIT_INDEX_FILE="$TMPindex" &&
|
||||
export GIT_INDEX_FILE &&
|
||||
rm -f "$TMPindex" &&
|
||||
git update-index -z --add --remove --stdin &&
|
||||
u_tree=$(git write-tree) &&
|
||||
printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree &&
|
||||
rm -f "$TMPindex"
|
||||
) ) || die "$(gettext "Cannot save the untracked files")"
|
||||
|
||||
untracked_commit_option="-p $u_commit";
|
||||
else
|
||||
untracked_commit_option=
|
||||
fi
|
||||
|
||||
if test -z "$patch_mode"
|
||||
then
|
||||
|
||||
# state of the working tree
|
||||
w_tree=$( (
|
||||
git read-tree --index-output="$TMPindex" -m $i_tree &&
|
||||
GIT_INDEX_FILE="$TMPindex" &&
|
||||
export GIT_INDEX_FILE &&
|
||||
git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
|
||||
git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
|
||||
git write-tree &&
|
||||
rm -f "$TMPindex"
|
||||
) ) ||
|
||||
die "$(gettext "Cannot save the current worktree state")"
|
||||
|
||||
else
|
||||
|
||||
rm -f "$TMP-index" &&
|
||||
GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
|
||||
|
||||
# find out what the user wants
|
||||
GIT_INDEX_FILE="$TMP-index" \
|
||||
git add--interactive --patch=stash -- "$@" &&
|
||||
|
||||
# state of the working tree
|
||||
w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
|
||||
die "$(gettext "Cannot save the current worktree state")"
|
||||
|
||||
git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
|
||||
test -s "$TMP-patch" ||
|
||||
die "$(gettext "No changes selected")"
|
||||
|
||||
rm -f "$TMP-index" ||
|
||||
die "$(gettext "Cannot remove temporary index (can't happen)")"
|
||||
|
||||
fi
|
||||
|
||||
# create the stash
|
||||
if test -z "$stash_msg"
|
||||
then
|
||||
stash_msg=$(printf 'WIP on %s' "$msg")
|
||||
else
|
||||
stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
|
||||
fi
|
||||
w_commit=$(printf '%s\n' "$stash_msg" |
|
||||
git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
|
||||
die "$(gettext "Cannot record working tree state")"
|
||||
}
|
||||
|
||||
store_stash () {
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-m|--message)
|
||||
shift
|
||||
stash_msg="$1"
|
||||
;;
|
||||
-m*)
|
||||
stash_msg=${1#-m}
|
||||
;;
|
||||
--message=*)
|
||||
stash_msg=${1#--message=}
|
||||
;;
|
||||
-q|--quiet)
|
||||
quiet=t
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
test $# = 1 ||
|
||||
die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
|
||||
|
||||
w_commit="$1"
|
||||
if test -z "$stash_msg"
|
||||
then
|
||||
stash_msg="Created via \"git stash store\"."
|
||||
fi
|
||||
|
||||
git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
|
||||
ret=$?
|
||||
test $ret != 0 && test -z "$quiet" &&
|
||||
die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
|
||||
return $ret
|
||||
}
|
||||
|
||||
push_stash () {
|
||||
keep_index=
|
||||
patch_mode=
|
||||
untracked=
|
||||
stash_msg=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-k|--keep-index)
|
||||
keep_index=t
|
||||
;;
|
||||
--no-keep-index)
|
||||
keep_index=n
|
||||
;;
|
||||
-p|--patch)
|
||||
patch_mode=t
|
||||
# only default to keep if we don't already have an override
|
||||
test -z "$keep_index" && keep_index=t
|
||||
;;
|
||||
-q|--quiet)
|
||||
GIT_QUIET=t
|
||||
;;
|
||||
-u|--include-untracked)
|
||||
untracked=untracked
|
||||
;;
|
||||
-a|--all)
|
||||
untracked=all
|
||||
;;
|
||||
-m|--message)
|
||||
shift
|
||||
test -z ${1+x} && usage
|
||||
stash_msg=$1
|
||||
;;
|
||||
-m*)
|
||||
stash_msg=${1#-m}
|
||||
;;
|
||||
--message=*)
|
||||
stash_msg=${1#--message=}
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
option="$1"
|
||||
eval_gettextln "error: unknown option for 'stash push': \$option"
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
|
||||
|
||||
if test -n "$patch_mode" && test -n "$untracked"
|
||||
then
|
||||
die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
|
||||
fi
|
||||
|
||||
test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
|
||||
|
||||
git update-index -q --refresh
|
||||
if maybe_quiet no_changes "$@"
|
||||
then
|
||||
say "$(gettext "No local changes to save")"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git reflog exists $ref_stash ||
|
||||
clear_stash || die "$(gettext "Cannot initialize stash")"
|
||||
|
||||
create_stash -m "$stash_msg" -u "$untracked" -- "$@"
|
||||
store_stash -m "$stash_msg" -q $w_commit ||
|
||||
die "$(gettext "Cannot save the current status")"
|
||||
say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
|
||||
|
||||
if test -z "$patch_mode"
|
||||
then
|
||||
test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
|
||||
if test -n "$untracked" && test $# = 0
|
||||
then
|
||||
git clean --force --quiet -d $CLEAN_X_OPTION
|
||||
fi
|
||||
|
||||
if test $# != 0
|
||||
then
|
||||
test -z "$untracked" && UPDATE_OPTION="-u" || UPDATE_OPTION=
|
||||
test "$untracked" = "all" && FORCE_OPTION="--force" || FORCE_OPTION=
|
||||
git add $UPDATE_OPTION $FORCE_OPTION -- "$@"
|
||||
git diff-index -p --cached --binary HEAD -- "$@" |
|
||||
git apply --index -R
|
||||
else
|
||||
git reset --hard -q
|
||||
fi
|
||||
|
||||
if test "$keep_index" = "t" && test -n "$i_tree"
|
||||
then
|
||||
git read-tree --reset $i_tree
|
||||
git ls-files -z --modified -- "$@" |
|
||||
git checkout-index -z --force --stdin
|
||||
fi
|
||||
else
|
||||
git apply -R < "$TMP-patch" ||
|
||||
die "$(gettext "Cannot remove worktree changes")"
|
||||
|
||||
if test "$keep_index" != "t"
|
||||
then
|
||||
git reset -q -- "$@"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
save_stash () {
|
||||
push_options=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-q|--quiet)
|
||||
GIT_QUIET=t
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
# pass all options through to push_stash
|
||||
push_options="$push_options $1"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
stash_msg="$*"
|
||||
|
||||
if test -z "$stash_msg"
|
||||
then
|
||||
push_stash $push_options
|
||||
else
|
||||
push_stash $push_options -m "$stash_msg"
|
||||
fi
|
||||
}
|
||||
|
||||
have_stash () {
|
||||
git rev-parse --verify --quiet $ref_stash >/dev/null
|
||||
}
|
||||
|
||||
list_stash () {
|
||||
have_stash || return 0
|
||||
git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
|
||||
}
|
||||
|
||||
show_stash () {
|
||||
ALLOW_UNKNOWN_FLAGS=t
|
||||
assert_stash_like "$@"
|
||||
|
||||
if test -z "$FLAGS"
|
||||
then
|
||||
if test "$(git config --bool stash.showStat || echo true)" = "true"
|
||||
then
|
||||
FLAGS=--stat
|
||||
fi
|
||||
|
||||
if test "$(git config --bool stash.showPatch || echo false)" = "true"
|
||||
then
|
||||
FLAGS=${FLAGS}${FLAGS:+ }-p
|
||||
fi
|
||||
|
||||
if test -z "$FLAGS"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
git diff ${FLAGS} $b_commit $w_commit
|
||||
}
|
||||
|
||||
show_help () {
|
||||
exec git help stash
|
||||
exit 1
|
||||
}
|
||||
|
||||
#
|
||||
# Parses the remaining options looking for flags and
|
||||
# at most one revision defaulting to ${ref_stash}@{0}
|
||||
# if none found.
|
||||
#
|
||||
# Derives related tree and commit objects from the
|
||||
# revision, if one is found.
|
||||
#
|
||||
# stash records the work tree, and is a merge between the
|
||||
# base commit (first parent) and the index tree (second parent).
|
||||
#
|
||||
# REV is set to the symbolic version of the specified stash-like commit
|
||||
# IS_STASH_LIKE is non-blank if ${REV} looks like a stash
|
||||
# IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
|
||||
# s is set to the SHA1 of the stash commit
|
||||
# w_commit is set to the commit containing the working tree
|
||||
# b_commit is set to the base commit
|
||||
# i_commit is set to the commit containing the index tree
|
||||
# u_commit is set to the commit containing the untracked files tree
|
||||
# w_tree is set to the working tree
|
||||
# b_tree is set to the base tree
|
||||
# i_tree is set to the index tree
|
||||
# u_tree is set to the untracked files tree
|
||||
#
|
||||
# GIT_QUIET is set to t if -q is specified
|
||||
# INDEX_OPTION is set to --index if --index is specified.
|
||||
# FLAGS is set to the remaining flags (if allowed)
|
||||
#
|
||||
# dies if:
|
||||
# * too many revisions specified
|
||||
# * no revision is specified and there is no stash stack
|
||||
# * a revision is specified which cannot be resolve to a SHA1
|
||||
# * a non-existent stash reference is specified
|
||||
# * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
|
||||
#
|
||||
|
||||
parse_flags_and_rev()
|
||||
{
|
||||
test "$PARSE_CACHE" = "$*" && return 0 # optimisation
|
||||
PARSE_CACHE="$*"
|
||||
|
||||
IS_STASH_LIKE=
|
||||
IS_STASH_REF=
|
||||
INDEX_OPTION=
|
||||
s=
|
||||
w_commit=
|
||||
b_commit=
|
||||
i_commit=
|
||||
u_commit=
|
||||
w_tree=
|
||||
b_tree=
|
||||
i_tree=
|
||||
u_tree=
|
||||
|
||||
FLAGS=
|
||||
REV=
|
||||
for opt
|
||||
do
|
||||
case "$opt" in
|
||||
-q|--quiet)
|
||||
GIT_QUIET=-t
|
||||
;;
|
||||
--index)
|
||||
INDEX_OPTION=--index
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
;;
|
||||
-*)
|
||||
test "$ALLOW_UNKNOWN_FLAGS" = t ||
|
||||
die "$(eval_gettext "unknown option: \$opt")"
|
||||
FLAGS="${FLAGS}${FLAGS:+ }$opt"
|
||||
;;
|
||||
*)
|
||||
REV="${REV}${REV:+ }'$opt'"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
eval set -- $REV
|
||||
|
||||
case $# in
|
||||
0)
|
||||
have_stash || die "$(gettext "No stash entries found.")"
|
||||
set -- ${ref_stash}@{0}
|
||||
;;
|
||||
1)
|
||||
:
|
||||
;;
|
||||
*)
|
||||
die "$(eval_gettext "Too many revisions specified: \$REV")"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$1" in
|
||||
*[!0-9]*)
|
||||
:
|
||||
;;
|
||||
*)
|
||||
set -- "${ref_stash}@{$1}"
|
||||
;;
|
||||
esac
|
||||
|
||||
REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
|
||||
reference="$1"
|
||||
die "$(eval_gettext "\$reference is not a valid reference")"
|
||||
}
|
||||
|
||||
i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
|
||||
set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
|
||||
s=$1 &&
|
||||
w_commit=$1 &&
|
||||
b_commit=$2 &&
|
||||
w_tree=$3 &&
|
||||
b_tree=$4 &&
|
||||
i_tree=$5 &&
|
||||
IS_STASH_LIKE=t &&
|
||||
test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
|
||||
IS_STASH_REF=t
|
||||
|
||||
u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
|
||||
u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
|
||||
}
|
||||
|
||||
is_stash_like()
|
||||
{
|
||||
parse_flags_and_rev "$@"
|
||||
test -n "$IS_STASH_LIKE"
|
||||
}
|
||||
|
||||
assert_stash_like() {
|
||||
is_stash_like "$@" || {
|
||||
args="$*"
|
||||
die "$(eval_gettext "'\$args' is not a stash-like commit")"
|
||||
}
|
||||
}
|
||||
|
||||
is_stash_ref() {
|
||||
is_stash_like "$@" && test -n "$IS_STASH_REF"
|
||||
}
|
||||
|
||||
assert_stash_ref() {
|
||||
is_stash_ref "$@" || {
|
||||
args="$*"
|
||||
die "$(eval_gettext "'\$args' is not a stash reference")"
|
||||
}
|
||||
}
|
||||
|
||||
apply_stash () {
|
||||
|
||||
assert_stash_like "$@"
|
||||
|
||||
git update-index -q --refresh || die "$(gettext "unable to refresh index")"
|
||||
|
||||
# current index state
|
||||
c_tree=$(git write-tree) ||
|
||||
die "$(gettext "Cannot apply a stash in the middle of a merge")"
|
||||
|
||||
unstashed_index_tree=
|
||||
if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
|
||||
test "$c_tree" != "$i_tree"
|
||||
then
|
||||
git diff-tree --binary $s^2^..$s^2 | git apply --cached
|
||||
test $? -ne 0 &&
|
||||
die "$(gettext "Conflicts in index. Try without --index.")"
|
||||
unstashed_index_tree=$(git write-tree) ||
|
||||
die "$(gettext "Could not save index tree")"
|
||||
git reset
|
||||
fi
|
||||
|
||||
if test -n "$u_tree"
|
||||
then
|
||||
GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
|
||||
GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
|
||||
rm -f "$TMPindex" ||
|
||||
die "$(gettext "Could not restore untracked files from stash entry")"
|
||||
fi
|
||||
|
||||
eval "
|
||||
GITHEAD_$w_tree='Stashed changes' &&
|
||||
GITHEAD_$c_tree='Updated upstream' &&
|
||||
GITHEAD_$b_tree='Version stash was based on' &&
|
||||
export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
|
||||
"
|
||||
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
|
||||
fi
|
||||
if git merge-recursive $b_tree -- $c_tree $w_tree
|
||||
then
|
||||
# No conflict
|
||||
if test -n "$unstashed_index_tree"
|
||||
then
|
||||
git read-tree "$unstashed_index_tree"
|
||||
else
|
||||
a="$TMP-added" &&
|
||||
git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
|
||||
git read-tree --reset $c_tree &&
|
||||
git update-index --add --stdin <"$a" ||
|
||||
die "$(gettext "Cannot unstage modified files")"
|
||||
rm -f "$a"
|
||||
fi
|
||||
squelch=
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
squelch='>/dev/null 2>&1'
|
||||
fi
|
||||
(cd "$START_DIR" && eval "git status $squelch") || :
|
||||
else
|
||||
# Merge conflict; keep the exit status from merge-recursive
|
||||
status=$?
|
||||
git rerere
|
||||
if test -n "$INDEX_OPTION"
|
||||
then
|
||||
gettextln "Index was not unstashed." >&2
|
||||
fi
|
||||
exit $status
|
||||
fi
|
||||
}
|
||||
|
||||
pop_stash() {
|
||||
assert_stash_ref "$@"
|
||||
|
||||
if apply_stash "$@"
|
||||
then
|
||||
drop_stash "$@"
|
||||
else
|
||||
status=$?
|
||||
say "$(gettext "The stash entry is kept in case you need it again.")"
|
||||
exit $status
|
||||
fi
|
||||
}
|
||||
|
||||
drop_stash () {
|
||||
assert_stash_ref "$@"
|
||||
|
||||
git reflog delete --updateref --rewrite "${REV}" &&
|
||||
say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
|
||||
die "$(eval_gettext "\${REV}: Could not drop stash entry")"
|
||||
|
||||
# clear_stash if we just dropped the last stash entry
|
||||
git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
|
||||
clear_stash
|
||||
}
|
||||
|
||||
apply_to_branch () {
|
||||
test -n "$1" || die "$(gettext "No branch name specified")"
|
||||
branch=$1
|
||||
shift 1
|
||||
|
||||
set -- --index "$@"
|
||||
assert_stash_like "$@"
|
||||
|
||||
git checkout -b $branch $REV^ &&
|
||||
apply_stash "$@" && {
|
||||
test -z "$IS_STASH_REF" || drop_stash "$@"
|
||||
}
|
||||
}
|
||||
|
||||
test "$1" = "-p" && set "push" "$@"
|
||||
|
||||
PARSE_CACHE='--not-parsed'
|
||||
# The default command is "push" if nothing but options are given
|
||||
seen_non_option=
|
||||
for opt
|
||||
do
|
||||
case "$opt" in
|
||||
--) break ;;
|
||||
-*) ;;
|
||||
*) seen_non_option=t; break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -n "$seen_non_option" || set "push" "$@"
|
||||
|
||||
# Main command set
|
||||
case "$1" in
|
||||
list)
|
||||
shift
|
||||
list_stash "$@"
|
||||
;;
|
||||
show)
|
||||
shift
|
||||
show_stash "$@"
|
||||
;;
|
||||
save)
|
||||
shift
|
||||
save_stash "$@"
|
||||
;;
|
||||
push)
|
||||
shift
|
||||
push_stash "$@"
|
||||
;;
|
||||
apply)
|
||||
shift
|
||||
apply_stash "$@"
|
||||
;;
|
||||
clear)
|
||||
shift
|
||||
clear_stash "$@"
|
||||
;;
|
||||
create)
|
||||
shift
|
||||
create_stash -m "$*" && echo "$w_commit"
|
||||
;;
|
||||
store)
|
||||
shift
|
||||
store_stash "$@"
|
||||
;;
|
||||
drop)
|
||||
shift
|
||||
drop_stash "$@"
|
||||
;;
|
||||
pop)
|
||||
shift
|
||||
pop_stash "$@"
|
||||
;;
|
||||
branch)
|
||||
shift
|
||||
apply_to_branch "$@"
|
||||
;;
|
||||
*)
|
||||
case $# in
|
||||
0)
|
||||
push_stash &&
|
||||
say "$(gettext "(To restore them type \"git stash apply\")")"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,112 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
# Resolve two or more trees.
|
||||
#
|
||||
|
||||
. git-sh-setup
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
# The first parameters up to -- are merge bases; the rest are heads.
|
||||
bases= head= remotes= sep_seen=
|
||||
for arg
|
||||
do
|
||||
case ",$sep_seen,$head,$arg," in
|
||||
*,--,)
|
||||
sep_seen=yes
|
||||
;;
|
||||
,yes,,*)
|
||||
head=$arg
|
||||
;;
|
||||
,yes,*)
|
||||
remotes="$remotes$arg "
|
||||
;;
|
||||
*)
|
||||
bases="$bases$arg "
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Reject if this is not an octopus -- resolve should be used instead.
|
||||
case "$remotes" in
|
||||
?*' '?*)
|
||||
;;
|
||||
*)
|
||||
exit 2 ;;
|
||||
esac
|
||||
|
||||
# MRC is the current "merge reference commit"
|
||||
# MRT is the current "merge result tree"
|
||||
|
||||
if ! git diff-index --quiet --cached HEAD --
|
||||
then
|
||||
gettextln "Error: Your local changes to the following files would be overwritten by merge"
|
||||
git diff-index --cached --name-only HEAD -- | sed -e 's/^/ /'
|
||||
exit 2
|
||||
fi
|
||||
MRC=$(git rev-parse --verify -q $head)
|
||||
MRT=$(git write-tree)
|
||||
NON_FF_MERGE=0
|
||||
OCTOPUS_FAILURE=0
|
||||
for SHA1 in $remotes
|
||||
do
|
||||
case "$OCTOPUS_FAILURE" in
|
||||
1)
|
||||
# We allow only last one to have a hand-resolvable
|
||||
# conflicts. Last round failed and we still had
|
||||
# a head to merge.
|
||||
gettextln "Automated merge did not work."
|
||||
gettextln "Should not be doing an octopus."
|
||||
exit 2
|
||||
esac
|
||||
|
||||
eval pretty_name=\${GITHEAD_$SHA1:-$SHA1}
|
||||
if test "$SHA1" = "$pretty_name"
|
||||
then
|
||||
SHA1_UP="$(echo "$SHA1" | tr a-z A-Z)"
|
||||
eval pretty_name=\${GITHEAD_$SHA1_UP:-$pretty_name}
|
||||
fi
|
||||
common=$(git merge-base --all $SHA1 $MRC) ||
|
||||
die "$(eval_gettext "Unable to find common commit with \$pretty_name")"
|
||||
|
||||
case "$LF$common$LF" in
|
||||
*"$LF$SHA1$LF"*)
|
||||
eval_gettextln "Already up to date with \$pretty_name"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
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.
|
||||
|
||||
eval_gettextln "Fast-forwarding to: \$pretty_name"
|
||||
git read-tree -u -m $head $SHA1 || exit
|
||||
MRC=$SHA1 MRT=$(git write-tree)
|
||||
continue
|
||||
fi
|
||||
|
||||
NON_FF_MERGE=1
|
||||
|
||||
eval_gettextln "Trying simple merge with \$pretty_name"
|
||||
git read-tree -u -m --aggressive $common $MRT $SHA1 || exit 2
|
||||
next=$(git write-tree 2>/dev/null)
|
||||
if test $? -ne 0
|
||||
then
|
||||
gettextln "Simple merge did not work, trying automatic merge."
|
||||
git merge-index -o git-merge-one-file -a ||
|
||||
OCTOPUS_FAILURE=1
|
||||
next=$(git write-tree 2>/dev/null)
|
||||
fi
|
||||
|
||||
MRC="$MRC $SHA1"
|
||||
MRT=$next
|
||||
done
|
||||
|
||||
exit "$OCTOPUS_FAILURE"
|
||||
@@ -0,0 +1,167 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) Linus Torvalds, 2005
|
||||
#
|
||||
# This is the git per-file merge script, called with
|
||||
#
|
||||
# $1 - original file SHA1 (or empty)
|
||||
# $2 - file in branch1 SHA1 (or empty)
|
||||
# $3 - file in branch2 SHA1 (or empty)
|
||||
# $4 - pathname in repository
|
||||
# $5 - original file mode (or empty)
|
||||
# $6 - file in branch1 mode (or empty)
|
||||
# $7 - file in branch2 mode (or empty)
|
||||
#
|
||||
# Handle some trivial cases.. The _really_ trivial cases have
|
||||
# been handled already by git read-tree, but that one doesn't
|
||||
# do any merges that might change the tree layout.
|
||||
|
||||
USAGE='<orig blob> <our blob> <their blob> <path>'
|
||||
USAGE="$USAGE <orig mode> <our mode> <their mode>"
|
||||
LONG_USAGE="usage: git merge-one-file $USAGE
|
||||
|
||||
Blob ids and modes should be empty for missing files."
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
cd_to_toplevel
|
||||
require_work_tree
|
||||
|
||||
if test $# != 7
|
||||
then
|
||||
echo "$LONG_USAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "${1:-.}${2:-.}${3:-.}" in
|
||||
#
|
||||
# Deleted in both or deleted in one and unchanged in the other
|
||||
#
|
||||
"$1.." | "$1.$1" | "$1$1.")
|
||||
if { test -z "$6" && test "$5" != "$7"; } ||
|
||||
{ test -z "$7" && test "$5" != "$6"; }
|
||||
then
|
||||
echo "ERROR: File $4 deleted on one branch but had its" >&2
|
||||
echo "ERROR: permissions changed on the other." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -n "$2"
|
||||
then
|
||||
echo "Removing $4"
|
||||
else
|
||||
# read-tree checked that index matches HEAD already,
|
||||
# so we know we do not have this path tracked.
|
||||
# there may be an unrelated working tree file here,
|
||||
# which we should just leave unmolested. Make sure
|
||||
# we do not have it in the index, though.
|
||||
exec git update-index --remove -- "$4"
|
||||
fi
|
||||
if test -f "$4"
|
||||
then
|
||||
rm -f -- "$4" &&
|
||||
rmdir -p "$(expr "z$4" : 'z\(.*\)/')" 2>/dev/null || :
|
||||
fi &&
|
||||
exec git update-index --remove -- "$4"
|
||||
;;
|
||||
|
||||
#
|
||||
# Added in one.
|
||||
#
|
||||
".$2.")
|
||||
# the other side did not add and we added so there is nothing
|
||||
# to be done, except making the path merged.
|
||||
exec git update-index --add --cacheinfo "$6" "$2" "$4"
|
||||
;;
|
||||
"..$3")
|
||||
echo "Adding $4"
|
||||
if test -f "$4"
|
||||
then
|
||||
echo "ERROR: untracked $4 is overwritten by the merge." >&2
|
||||
exit 1
|
||||
fi
|
||||
git update-index --add --cacheinfo "$7" "$3" "$4" &&
|
||||
exec git checkout-index -u -f -- "$4"
|
||||
;;
|
||||
|
||||
#
|
||||
# Added in both, identically (check for same permissions).
|
||||
#
|
||||
".$3$2")
|
||||
if test "$6" != "$7"
|
||||
then
|
||||
echo "ERROR: File $4 added identically in both branches," >&2
|
||||
echo "ERROR: but permissions conflict $6->$7." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Adding $4"
|
||||
git update-index --add --cacheinfo "$6" "$2" "$4" &&
|
||||
exec git checkout-index -u -f -- "$4"
|
||||
;;
|
||||
|
||||
#
|
||||
# Modified in both, but differently.
|
||||
#
|
||||
"$1$2$3" | ".$2$3")
|
||||
|
||||
case ",$6,$7," in
|
||||
*,120000,*)
|
||||
echo "ERROR: $4: Not merging symbolic link changes." >&2
|
||||
exit 1
|
||||
;;
|
||||
*,160000,*)
|
||||
echo "ERROR: $4: Not merging conflicting submodule changes." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
src1=$(git unpack-file $2)
|
||||
src2=$(git unpack-file $3)
|
||||
case "$1" in
|
||||
'')
|
||||
echo "Added $4 in both, but differently."
|
||||
orig=$(git unpack-file $(git hash-object /dev/null))
|
||||
;;
|
||||
*)
|
||||
echo "Auto-merging $4"
|
||||
orig=$(git unpack-file $1)
|
||||
;;
|
||||
esac
|
||||
|
||||
git merge-file "$src1" "$orig" "$src2"
|
||||
ret=$?
|
||||
msg=
|
||||
if test $ret != 0 || test -z "$1"
|
||||
then
|
||||
msg='content conflict'
|
||||
ret=1
|
||||
fi
|
||||
|
||||
# Create the working tree file, using "our tree" version from the
|
||||
# index, and then store the result of the merge.
|
||||
git checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4" || exit 1
|
||||
rm -f -- "$orig" "$src1" "$src2"
|
||||
|
||||
if test "$6" != "$7"
|
||||
then
|
||||
if test -n "$msg"
|
||||
then
|
||||
msg="$msg, "
|
||||
fi
|
||||
msg="${msg}permissions conflict: $5->$6,$7"
|
||||
ret=1
|
||||
fi
|
||||
|
||||
if test $ret != 0
|
||||
then
|
||||
echo "ERROR: $msg in $4" >&2
|
||||
exit 1
|
||||
fi
|
||||
exec git update-index -- "$4"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "ERROR: $4: Not handling case $1 -> $2 -> $3" >&2
|
||||
;;
|
||||
esac
|
||||
exit 1
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
# Resolve two trees, using enhanced multi-base read-tree.
|
||||
|
||||
# The first parameters up to -- are merge bases; the rest are heads.
|
||||
bases= head= remotes= sep_seen=
|
||||
for arg
|
||||
do
|
||||
case ",$sep_seen,$head,$arg," in
|
||||
*,--,)
|
||||
sep_seen=yes
|
||||
;;
|
||||
,yes,,*)
|
||||
head=$arg
|
||||
;;
|
||||
,yes,*)
|
||||
remotes="$remotes$arg "
|
||||
;;
|
||||
*)
|
||||
bases="$bases$arg "
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Give up if we are given two or more remotes -- not handling octopus.
|
||||
case "$remotes" in
|
||||
?*' '?*)
|
||||
exit 2 ;;
|
||||
esac
|
||||
|
||||
# Give up if this is a baseless merge.
|
||||
if test '' = "$bases"
|
||||
then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
git update-index -q --refresh
|
||||
git read-tree -u -m --aggressive $bases $head $remotes || exit 2
|
||||
echo "Trying simple merge."
|
||||
if result_tree=$(git write-tree 2>/dev/null)
|
||||
then
|
||||
exit 0
|
||||
else
|
||||
echo "Simple merge failed, trying Automatic merge."
|
||||
if git merge-index -o git-merge-one-file -a
|
||||
then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,516 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This program resolves merge conflicts in git
|
||||
#
|
||||
# Copyright (c) 2006 Theodore Y. Ts'o
|
||||
# Copyright (c) 2009-2016 David Aguilar
|
||||
#
|
||||
# This file is licensed under the GPL v2, or a later version
|
||||
# at the discretion of Junio C Hamano.
|
||||
#
|
||||
|
||||
USAGE='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [-g|--gui|--no-gui] [-O<orderfile>] [file to merge] ...'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
NONGIT_OK=Yes
|
||||
OPTIONS_SPEC=
|
||||
TOOL_MODE=merge
|
||||
. git-sh-setup
|
||||
. git-mergetool--lib
|
||||
|
||||
# Returns true if the mode reflects a symlink
|
||||
is_symlink () {
|
||||
test "$1" = 120000
|
||||
}
|
||||
|
||||
is_submodule () {
|
||||
test "$1" = 160000
|
||||
}
|
||||
|
||||
local_present () {
|
||||
test -n "$local_mode"
|
||||
}
|
||||
|
||||
remote_present () {
|
||||
test -n "$remote_mode"
|
||||
}
|
||||
|
||||
base_present () {
|
||||
test -n "$base_mode"
|
||||
}
|
||||
|
||||
mergetool_tmpdir_init () {
|
||||
if test "$(git config --bool mergetool.writeToTemp)" != true
|
||||
then
|
||||
MERGETOOL_TMPDIR=.
|
||||
return 0
|
||||
fi
|
||||
if MERGETOOL_TMPDIR=$(mktemp -d -t "git-mergetool-XXXXXX" 2>/dev/null)
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
die "error: mktemp is needed when 'mergetool.writeToTemp' is true"
|
||||
}
|
||||
|
||||
cleanup_temp_files () {
|
||||
if test "$1" = --save-backup
|
||||
then
|
||||
rm -rf -- "$MERGED.orig"
|
||||
test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
|
||||
rm -f -- "$LOCAL" "$REMOTE" "$BASE"
|
||||
else
|
||||
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
|
||||
fi
|
||||
if test "$MERGETOOL_TMPDIR" != "."
|
||||
then
|
||||
rmdir "$MERGETOOL_TMPDIR"
|
||||
fi
|
||||
}
|
||||
|
||||
describe_file () {
|
||||
mode="$1"
|
||||
branch="$2"
|
||||
file="$3"
|
||||
|
||||
printf " {%s}: " "$branch"
|
||||
if test -z "$mode"
|
||||
then
|
||||
echo "deleted"
|
||||
elif is_symlink "$mode"
|
||||
then
|
||||
echo "a symbolic link -> '$(cat "$file")'"
|
||||
elif is_submodule "$mode"
|
||||
then
|
||||
echo "submodule commit $file"
|
||||
elif base_present
|
||||
then
|
||||
echo "modified file"
|
||||
else
|
||||
echo "created file"
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_symlink_merge () {
|
||||
while true
|
||||
do
|
||||
printf "Use (l)ocal or (r)emote, or (a)bort? "
|
||||
read ans || return 1
|
||||
case "$ans" in
|
||||
[lL]*)
|
||||
git checkout-index -f --stage=2 -- "$MERGED"
|
||||
git add -- "$MERGED"
|
||||
cleanup_temp_files --save-backup
|
||||
return 0
|
||||
;;
|
||||
[rR]*)
|
||||
git checkout-index -f --stage=3 -- "$MERGED"
|
||||
git add -- "$MERGED"
|
||||
cleanup_temp_files --save-backup
|
||||
return 0
|
||||
;;
|
||||
[aA]*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
resolve_deleted_merge () {
|
||||
while true
|
||||
do
|
||||
if base_present
|
||||
then
|
||||
printf "Use (m)odified or (d)eleted file, or (a)bort? "
|
||||
else
|
||||
printf "Use (c)reated or (d)eleted file, or (a)bort? "
|
||||
fi
|
||||
read ans || return 1
|
||||
case "$ans" in
|
||||
[mMcC]*)
|
||||
git add -- "$MERGED"
|
||||
if test "$merge_keep_backup" = "true"
|
||||
then
|
||||
cleanup_temp_files --save-backup
|
||||
else
|
||||
cleanup_temp_files
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
[dD]*)
|
||||
git rm -- "$MERGED" > /dev/null
|
||||
cleanup_temp_files
|
||||
return 0
|
||||
;;
|
||||
[aA]*)
|
||||
if test "$merge_keep_temporaries" = "false"
|
||||
then
|
||||
cleanup_temp_files
|
||||
fi
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
resolve_submodule_merge () {
|
||||
while true
|
||||
do
|
||||
printf "Use (l)ocal or (r)emote, or (a)bort? "
|
||||
read ans || return 1
|
||||
case "$ans" in
|
||||
[lL]*)
|
||||
if ! local_present
|
||||
then
|
||||
if test -n "$(git ls-tree HEAD -- "$MERGED")"
|
||||
then
|
||||
# Local isn't present, but it's a subdirectory
|
||||
git ls-tree --full-name -r HEAD -- "$MERGED" |
|
||||
git update-index --index-info || exit $?
|
||||
else
|
||||
test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
|
||||
git update-index --force-remove "$MERGED"
|
||||
cleanup_temp_files --save-backup
|
||||
fi
|
||||
elif is_submodule "$local_mode"
|
||||
then
|
||||
stage_submodule "$MERGED" "$local_sha1"
|
||||
else
|
||||
git checkout-index -f --stage=2 -- "$MERGED"
|
||||
git add -- "$MERGED"
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
[rR]*)
|
||||
if ! remote_present
|
||||
then
|
||||
if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"
|
||||
then
|
||||
# Remote isn't present, but it's a subdirectory
|
||||
git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" |
|
||||
git update-index --index-info || exit $?
|
||||
else
|
||||
test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
|
||||
git update-index --force-remove "$MERGED"
|
||||
fi
|
||||
elif is_submodule "$remote_mode"
|
||||
then
|
||||
! is_submodule "$local_mode" &&
|
||||
test -e "$MERGED" &&
|
||||
mv -- "$MERGED" "$BACKUP"
|
||||
stage_submodule "$MERGED" "$remote_sha1"
|
||||
else
|
||||
test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
|
||||
git checkout-index -f --stage=3 -- "$MERGED"
|
||||
git add -- "$MERGED"
|
||||
fi
|
||||
cleanup_temp_files --save-backup
|
||||
return 0
|
||||
;;
|
||||
[aA]*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
stage_submodule () {
|
||||
path="$1"
|
||||
submodule_sha1="$2"
|
||||
mkdir -p "$path" ||
|
||||
die "fatal: unable to create directory for module at $path"
|
||||
# Find $path relative to work tree
|
||||
work_tree_root=$(cd_to_toplevel && pwd)
|
||||
work_rel_path=$(cd "$path" &&
|
||||
GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix
|
||||
)
|
||||
test -n "$work_rel_path" ||
|
||||
die "fatal: unable to get path of module $path relative to work tree"
|
||||
git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
|
||||
}
|
||||
|
||||
checkout_staged_file () {
|
||||
tmpfile=$(expr \
|
||||
"$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
|
||||
: '\([^ ]*\) ')
|
||||
|
||||
if test $? -eq 0 && test -n "$tmpfile"
|
||||
then
|
||||
mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
|
||||
else
|
||||
>"$3"
|
||||
fi
|
||||
}
|
||||
|
||||
merge_file () {
|
||||
MERGED="$1"
|
||||
|
||||
f=$(git ls-files -u -- "$MERGED")
|
||||
if test -z "$f"
|
||||
then
|
||||
if test ! -f "$MERGED"
|
||||
then
|
||||
echo "$MERGED: file not found"
|
||||
else
|
||||
echo "$MERGED: file does not need merging"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
if BASE=$(expr "$MERGED" : '\(.*\)\.[^/]*$')
|
||||
then
|
||||
ext=$(expr "$MERGED" : '.*\(\.[^/]*\)$')
|
||||
else
|
||||
BASE=$MERGED
|
||||
ext=
|
||||
fi
|
||||
|
||||
mergetool_tmpdir_init
|
||||
|
||||
if test "$MERGETOOL_TMPDIR" != "."
|
||||
then
|
||||
# If we're using a temporary directory then write to the
|
||||
# top-level of that directory.
|
||||
BASE=${BASE##*/}
|
||||
fi
|
||||
|
||||
BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
|
||||
LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
|
||||
REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
|
||||
BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
|
||||
|
||||
base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
|
||||
local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
|
||||
remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
|
||||
|
||||
if is_submodule "$local_mode" || is_submodule "$remote_mode"
|
||||
then
|
||||
echo "Submodule merge conflict for '$MERGED':"
|
||||
local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
|
||||
remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
|
||||
describe_file "$local_mode" "local" "$local_sha1"
|
||||
describe_file "$remote_mode" "remote" "$remote_sha1"
|
||||
resolve_submodule_merge
|
||||
return
|
||||
fi
|
||||
|
||||
if test -f "$MERGED"
|
||||
then
|
||||
mv -- "$MERGED" "$BACKUP"
|
||||
cp -- "$BACKUP" "$MERGED"
|
||||
fi
|
||||
# Create a parent directory to handle delete/delete conflicts
|
||||
# where the base's directory no longer exists.
|
||||
mkdir -p "$(dirname "$MERGED")"
|
||||
|
||||
checkout_staged_file 1 "$MERGED" "$BASE"
|
||||
checkout_staged_file 2 "$MERGED" "$LOCAL"
|
||||
checkout_staged_file 3 "$MERGED" "$REMOTE"
|
||||
|
||||
if test -z "$local_mode" || test -z "$remote_mode"
|
||||
then
|
||||
echo "Deleted merge conflict for '$MERGED':"
|
||||
describe_file "$local_mode" "local" "$LOCAL"
|
||||
describe_file "$remote_mode" "remote" "$REMOTE"
|
||||
resolve_deleted_merge
|
||||
status=$?
|
||||
rmdir -p "$(dirname "$MERGED")" 2>/dev/null
|
||||
return $status
|
||||
fi
|
||||
|
||||
if is_symlink "$local_mode" || is_symlink "$remote_mode"
|
||||
then
|
||||
echo "Symbolic link merge conflict for '$MERGED':"
|
||||
describe_file "$local_mode" "local" "$LOCAL"
|
||||
describe_file "$remote_mode" "remote" "$REMOTE"
|
||||
resolve_symlink_merge
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Normal merge conflict for '$MERGED':"
|
||||
describe_file "$local_mode" "local" "$LOCAL"
|
||||
describe_file "$remote_mode" "remote" "$REMOTE"
|
||||
if test "$guessed_merge_tool" = true || test "$prompt" = true
|
||||
then
|
||||
printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
|
||||
read ans || return 1
|
||||
fi
|
||||
|
||||
if base_present
|
||||
then
|
||||
present=true
|
||||
else
|
||||
present=false
|
||||
fi
|
||||
|
||||
if ! run_merge_tool "$merge_tool" "$present"
|
||||
then
|
||||
echo "merge of $MERGED failed" 1>&2
|
||||
mv -- "$BACKUP" "$MERGED"
|
||||
|
||||
if test "$merge_keep_temporaries" = "false"
|
||||
then
|
||||
cleanup_temp_files
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
if test "$merge_keep_backup" = "true"
|
||||
then
|
||||
mv -- "$BACKUP" "$MERGED.orig"
|
||||
else
|
||||
rm -- "$BACKUP"
|
||||
fi
|
||||
|
||||
git add -- "$MERGED"
|
||||
cleanup_temp_files
|
||||
return 0
|
||||
}
|
||||
|
||||
prompt_after_failed_merge () {
|
||||
while true
|
||||
do
|
||||
printf "Continue merging other unresolved paths [y/n]? "
|
||||
read ans || return 1
|
||||
case "$ans" in
|
||||
[yY]*)
|
||||
return 0
|
||||
;;
|
||||
[nN]*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
print_noop_and_exit () {
|
||||
echo "No files need merging"
|
||||
exit 0
|
||||
}
|
||||
|
||||
main () {
|
||||
prompt=$(git config --bool mergetool.prompt)
|
||||
gui_tool=false
|
||||
guessed_merge_tool=false
|
||||
orderfile=
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--tool-help=*)
|
||||
TOOL_MODE=${1#--tool-help=}
|
||||
show_tool_help
|
||||
;;
|
||||
--tool-help)
|
||||
show_tool_help
|
||||
;;
|
||||
-t|--tool*)
|
||||
case "$#,$1" in
|
||||
*,*=*)
|
||||
merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
|
||||
;;
|
||||
1,*)
|
||||
usage ;;
|
||||
*)
|
||||
merge_tool="$2"
|
||||
shift ;;
|
||||
esac
|
||||
;;
|
||||
--no-gui)
|
||||
gui_tool=false
|
||||
;;
|
||||
-g|--gui)
|
||||
gui_tool=true
|
||||
;;
|
||||
-y|--no-prompt)
|
||||
prompt=false
|
||||
;;
|
||||
--prompt)
|
||||
prompt=true
|
||||
;;
|
||||
-O*)
|
||||
orderfile="${1#-O}"
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
git_dir_init
|
||||
require_work_tree
|
||||
|
||||
if test -z "$merge_tool"
|
||||
then
|
||||
# Check if a merge tool has been configured
|
||||
merge_tool=$(get_configured_merge_tool $gui_tool)
|
||||
# Try to guess an appropriate merge tool if no tool has been set.
|
||||
if test -z "$merge_tool"
|
||||
then
|
||||
merge_tool=$(guess_merge_tool) || exit
|
||||
guessed_merge_tool=true
|
||||
fi
|
||||
fi
|
||||
merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
|
||||
merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
|
||||
|
||||
prefix=$(git rev-parse --show-prefix) || exit 1
|
||||
cd_to_toplevel
|
||||
|
||||
if test -n "$orderfile"
|
||||
then
|
||||
orderfile=$(
|
||||
git rev-parse --prefix "$prefix" -- "$orderfile" |
|
||||
sed -e 1d
|
||||
)
|
||||
fi
|
||||
|
||||
if test $# -eq 0 && test -e "$GIT_DIR/MERGE_RR"
|
||||
then
|
||||
set -- $(git rerere remaining)
|
||||
if test $# -eq 0
|
||||
then
|
||||
print_noop_and_exit
|
||||
fi
|
||||
elif test $# -ge 0
|
||||
then
|
||||
# rev-parse provides the -- needed for 'set'
|
||||
eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
|
||||
fi
|
||||
|
||||
files=$(git -c core.quotePath=false \
|
||||
diff --name-only --diff-filter=U \
|
||||
${orderfile:+"-O$orderfile"} -- "$@")
|
||||
|
||||
if test -z "$files"
|
||||
then
|
||||
print_noop_and_exit
|
||||
fi
|
||||
|
||||
printf "Merging:\n"
|
||||
printf "%s\n" "$files"
|
||||
|
||||
rc=0
|
||||
set -- $files
|
||||
while test $# -ne 0
|
||||
do
|
||||
printf "\n"
|
||||
if ! merge_file "$1"
|
||||
then
|
||||
rc=1
|
||||
test $# -ne 1 && prompt_after_failed_merge || exit 1
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
exit $rc
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,439 @@
|
||||
# git-mergetool--lib is a shell library for common merge tool functions
|
||||
|
||||
: ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools}
|
||||
|
||||
IFS='
|
||||
'
|
||||
|
||||
mode_ok () {
|
||||
if diff_mode
|
||||
then
|
||||
can_diff
|
||||
elif merge_mode
|
||||
then
|
||||
can_merge
|
||||
else
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
is_available () {
|
||||
merge_tool_path=$(translate_merge_tool_path "$1") &&
|
||||
type "$merge_tool_path" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
list_config_tools () {
|
||||
section=$1
|
||||
line_prefix=${2:-}
|
||||
|
||||
git config --get-regexp $section'\..*\.cmd' |
|
||||
while read -r key value
|
||||
do
|
||||
toolname=${key#$section.}
|
||||
toolname=${toolname%.cmd}
|
||||
|
||||
printf "%s%s\n" "$line_prefix" "$toolname"
|
||||
done
|
||||
}
|
||||
|
||||
show_tool_names () {
|
||||
condition=${1:-true} per_line_prefix=${2:-} preamble=${3:-}
|
||||
not_found_msg=${4:-}
|
||||
extra_content=${5:-}
|
||||
|
||||
shown_any=
|
||||
( cd "$MERGE_TOOLS_DIR" && ls ) | {
|
||||
while read toolname
|
||||
do
|
||||
if setup_tool "$toolname" 2>/dev/null &&
|
||||
(eval "$condition" "$toolname")
|
||||
then
|
||||
if test -n "$preamble"
|
||||
then
|
||||
printf "%s\n" "$preamble"
|
||||
preamble=
|
||||
fi
|
||||
shown_any=yes
|
||||
printf "%s%s\n" "$per_line_prefix" "$toolname"
|
||||
fi
|
||||
done
|
||||
|
||||
if test -n "$extra_content"
|
||||
then
|
||||
if test -n "$preamble"
|
||||
then
|
||||
# Note: no '\n' here since we don't want a
|
||||
# blank line if there is no initial content.
|
||||
printf "%s" "$preamble"
|
||||
preamble=
|
||||
fi
|
||||
shown_any=yes
|
||||
printf "\n%s\n" "$extra_content"
|
||||
fi
|
||||
|
||||
if test -n "$preamble" && test -n "$not_found_msg"
|
||||
then
|
||||
printf "%s\n" "$not_found_msg"
|
||||
fi
|
||||
|
||||
test -n "$shown_any"
|
||||
}
|
||||
}
|
||||
|
||||
diff_mode() {
|
||||
test "$TOOL_MODE" = diff
|
||||
}
|
||||
|
||||
merge_mode() {
|
||||
test "$TOOL_MODE" = merge
|
||||
}
|
||||
|
||||
translate_merge_tool_path () {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check_unchanged () {
|
||||
if test "$MERGED" -nt "$BACKUP"
|
||||
then
|
||||
return 0
|
||||
else
|
||||
while true
|
||||
do
|
||||
echo "$MERGED seems unchanged."
|
||||
printf "Was the merge successful [y/n]? "
|
||||
read answer || return 1
|
||||
case "$answer" in
|
||||
y*|Y*) return 0 ;;
|
||||
n*|N*) return 1 ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
valid_tool () {
|
||||
setup_tool "$1" && return 0
|
||||
cmd=$(get_merge_tool_cmd "$1")
|
||||
test -n "$cmd"
|
||||
}
|
||||
|
||||
setup_user_tool () {
|
||||
merge_tool_cmd=$(get_merge_tool_cmd "$tool")
|
||||
test -n "$merge_tool_cmd" || return 1
|
||||
|
||||
diff_cmd () {
|
||||
( eval $merge_tool_cmd )
|
||||
}
|
||||
|
||||
merge_cmd () {
|
||||
( eval $merge_tool_cmd )
|
||||
}
|
||||
}
|
||||
|
||||
setup_tool () {
|
||||
tool="$1"
|
||||
|
||||
# Fallback definitions, to be overridden by tools.
|
||||
can_merge () {
|
||||
return 0
|
||||
}
|
||||
|
||||
can_diff () {
|
||||
return 0
|
||||
}
|
||||
|
||||
diff_cmd () {
|
||||
return 1
|
||||
}
|
||||
|
||||
merge_cmd () {
|
||||
return 1
|
||||
}
|
||||
|
||||
translate_merge_tool_path () {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
# Most tools' exit codes cannot be trusted, so By default we ignore
|
||||
# their exit code and check the merged file's modification time in
|
||||
# check_unchanged() to determine whether or not the merge was
|
||||
# successful. The return value from run_merge_cmd, by default, is
|
||||
# determined by check_unchanged().
|
||||
#
|
||||
# When a tool's exit code can be trusted then the return value from
|
||||
# run_merge_cmd is simply the tool's exit code, and check_unchanged()
|
||||
# is not called.
|
||||
#
|
||||
# The return value of exit_code_trustable() tells us whether or not we
|
||||
# can trust the tool's exit code.
|
||||
#
|
||||
# User-defined and built-in tools default to false.
|
||||
# Built-in tools advertise that their exit code is trustable by
|
||||
# redefining exit_code_trustable() to true.
|
||||
|
||||
exit_code_trustable () {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
if ! test -f "$MERGE_TOOLS_DIR/$tool"
|
||||
then
|
||||
setup_user_tool
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Load the redefined functions
|
||||
. "$MERGE_TOOLS_DIR/$tool"
|
||||
# Now let the user override the default command for the tool. If
|
||||
# they have not done so then this will return 1 which we ignore.
|
||||
setup_user_tool
|
||||
|
||||
if merge_mode && ! can_merge
|
||||
then
|
||||
echo "error: '$tool' can not be used to resolve merges" >&2
|
||||
return 1
|
||||
elif diff_mode && ! can_diff
|
||||
then
|
||||
echo "error: '$tool' can only be used to resolve merges" >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
get_merge_tool_cmd () {
|
||||
merge_tool="$1"
|
||||
if diff_mode
|
||||
then
|
||||
git config "difftool.$merge_tool.cmd" ||
|
||||
git config "mergetool.$merge_tool.cmd"
|
||||
else
|
||||
git config "mergetool.$merge_tool.cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
trust_exit_code () {
|
||||
if git config --bool "mergetool.$1.trustExitCode"
|
||||
then
|
||||
:; # OK
|
||||
elif exit_code_trustable
|
||||
then
|
||||
echo true
|
||||
else
|
||||
echo false
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Entry point for running tools
|
||||
run_merge_tool () {
|
||||
# If GIT_PREFIX is empty then we cannot use it in tools
|
||||
# that expect to be able to chdir() to its value.
|
||||
GIT_PREFIX=${GIT_PREFIX:-.}
|
||||
export GIT_PREFIX
|
||||
|
||||
merge_tool_path=$(get_merge_tool_path "$1") || exit
|
||||
base_present="$2"
|
||||
|
||||
# Bring tool-specific functions into scope
|
||||
setup_tool "$1" || return 1
|
||||
|
||||
if merge_mode
|
||||
then
|
||||
run_merge_cmd "$1"
|
||||
else
|
||||
run_diff_cmd "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run a either a configured or built-in diff tool
|
||||
run_diff_cmd () {
|
||||
diff_cmd "$1"
|
||||
}
|
||||
|
||||
# Run a either a configured or built-in merge tool
|
||||
run_merge_cmd () {
|
||||
mergetool_trust_exit_code=$(trust_exit_code "$1")
|
||||
if test "$mergetool_trust_exit_code" = "true"
|
||||
then
|
||||
merge_cmd "$1"
|
||||
else
|
||||
touch "$BACKUP"
|
||||
merge_cmd "$1"
|
||||
check_unchanged
|
||||
fi
|
||||
}
|
||||
|
||||
list_merge_tool_candidates () {
|
||||
if merge_mode
|
||||
then
|
||||
tools="tortoisemerge"
|
||||
else
|
||||
tools="kompare"
|
||||
fi
|
||||
if test -n "$DISPLAY"
|
||||
then
|
||||
if test -n "$GNOME_DESKTOP_SESSION_ID"
|
||||
then
|
||||
tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
|
||||
else
|
||||
tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
|
||||
fi
|
||||
tools="$tools gvimdiff diffuse diffmerge ecmerge"
|
||||
tools="$tools p4merge araxis bc codecompare"
|
||||
fi
|
||||
case "${VISUAL:-$EDITOR}" in
|
||||
*vim*)
|
||||
tools="$tools vimdiff emerge"
|
||||
;;
|
||||
*)
|
||||
tools="$tools emerge vimdiff"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
show_tool_help () {
|
||||
tool_opt="'git ${TOOL_MODE}tool --tool=<tool>'"
|
||||
|
||||
tab=' '
|
||||
LF='
|
||||
'
|
||||
any_shown=no
|
||||
|
||||
cmd_name=${TOOL_MODE}tool
|
||||
config_tools=$({
|
||||
diff_mode && list_config_tools difftool "$tab$tab"
|
||||
list_config_tools mergetool "$tab$tab"
|
||||
} | sort)
|
||||
extra_content=
|
||||
if test -n "$config_tools"
|
||||
then
|
||||
extra_content="${tab}user-defined:${LF}$config_tools"
|
||||
fi
|
||||
|
||||
show_tool_names 'mode_ok && is_available' "$tab$tab" \
|
||||
"$tool_opt may be set to one of the following:" \
|
||||
"No suitable tool for 'git $cmd_name --tool=<tool>' found." \
|
||||
"$extra_content" &&
|
||||
any_shown=yes
|
||||
|
||||
show_tool_names 'mode_ok && ! is_available' "$tab$tab" \
|
||||
"${LF}The following tools are valid, but not currently available:" &&
|
||||
any_shown=yes
|
||||
|
||||
if test "$any_shown" = yes
|
||||
then
|
||||
echo
|
||||
echo "Some of the tools listed above only work in a windowed"
|
||||
echo "environment. If run in a terminal-only session, they will fail."
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
guess_merge_tool () {
|
||||
list_merge_tool_candidates
|
||||
cat >&2 <<-EOF
|
||||
|
||||
This message is displayed because '$TOOL_MODE.tool' is not configured.
|
||||
See 'git ${TOOL_MODE}tool --tool-help' or 'git help config' for more details.
|
||||
'git ${TOOL_MODE}tool' will now attempt to use one of the following tools:
|
||||
$tools
|
||||
EOF
|
||||
|
||||
# Loop over each candidate and stop when a valid merge tool is found.
|
||||
IFS=' '
|
||||
for tool in $tools
|
||||
do
|
||||
is_available "$tool" && echo "$tool" && return 0
|
||||
done
|
||||
|
||||
echo >&2 "No known ${TOOL_MODE} tool is available."
|
||||
return 1
|
||||
}
|
||||
|
||||
get_configured_merge_tool () {
|
||||
# If first argument is true, find the guitool instead
|
||||
if test "$1" = true
|
||||
then
|
||||
gui_prefix=gui
|
||||
fi
|
||||
|
||||
# Diff mode first tries diff.(gui)tool and falls back to merge.(gui)tool.
|
||||
# Merge mode only checks merge.(gui)tool
|
||||
if diff_mode
|
||||
then
|
||||
merge_tool=$(git config diff.${gui_prefix}tool || git config merge.${gui_prefix}tool)
|
||||
else
|
||||
merge_tool=$(git config merge.${gui_prefix}tool)
|
||||
fi
|
||||
if test -n "$merge_tool" && ! valid_tool "$merge_tool"
|
||||
then
|
||||
echo >&2 "git config option $TOOL_MODE.${gui_prefix}tool set to unknown tool: $merge_tool"
|
||||
echo >&2 "Resetting to default..."
|
||||
return 1
|
||||
fi
|
||||
echo "$merge_tool"
|
||||
}
|
||||
|
||||
get_merge_tool_path () {
|
||||
# A merge tool has been set, so verify that it's valid.
|
||||
merge_tool="$1"
|
||||
if ! valid_tool "$merge_tool"
|
||||
then
|
||||
echo >&2 "Unknown merge tool $merge_tool"
|
||||
exit 1
|
||||
fi
|
||||
if diff_mode
|
||||
then
|
||||
merge_tool_path=$(git config difftool."$merge_tool".path ||
|
||||
git config mergetool."$merge_tool".path)
|
||||
else
|
||||
merge_tool_path=$(git config mergetool."$merge_tool".path)
|
||||
fi
|
||||
if test -z "$merge_tool_path"
|
||||
then
|
||||
merge_tool_path=$(translate_merge_tool_path "$merge_tool")
|
||||
fi
|
||||
if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
|
||||
! type "$merge_tool_path" >/dev/null 2>&1
|
||||
then
|
||||
echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
|
||||
"'$merge_tool_path'"
|
||||
exit 1
|
||||
fi
|
||||
echo "$merge_tool_path"
|
||||
}
|
||||
|
||||
get_merge_tool () {
|
||||
# Check if a merge tool has been configured
|
||||
merge_tool=$(get_configured_merge_tool)
|
||||
# Try to guess an appropriate merge tool if no tool has been set.
|
||||
if test -z "$merge_tool"
|
||||
then
|
||||
merge_tool=$(guess_merge_tool) || exit
|
||||
fi
|
||||
echo "$merge_tool"
|
||||
}
|
||||
|
||||
mergetool_find_win32_cmd () {
|
||||
executable=$1
|
||||
sub_directory=$2
|
||||
|
||||
# Use $executable if it exists in $PATH
|
||||
if type -p "$executable" >/dev/null 2>&1
|
||||
then
|
||||
printf '%s' "$executable"
|
||||
return
|
||||
fi
|
||||
|
||||
# Look for executable in the typical locations
|
||||
for directory in $(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' |
|
||||
cut -d '=' -f 2- | sort -u)
|
||||
do
|
||||
if test -n "$directory" && test -x "$directory/$sub_directory/$executable"
|
||||
then
|
||||
printf '%s' "$directory/$sub_directory/$executable"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
printf '%s' "$executable"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user