git-status -v

This revamps the git-status command to take the same set of
parameters as git commit.  It gives a preview of what is being
committed with that command.  With -v flag, it shows the diff
output between the HEAD commit and the index that would be
committed if these flags were given to git-commit command.

git-commit also acquires -v flag (it used to mean "verify" but
that is the default anyway and there is --no-verify to turn it
off, so not much is lost), which uses the updated git-status -v
to seed the commit log buffer.  This is handy for writing a log
message while reviewing the changes one last time.

Now, git-commit and git-status are internally share the same
implementation.

Unlike previous git-commit change, this uses a temporary index
to prepare the index file that would become the real index file
after a successful commit, and moves it to the real index file
once the commit is actually made.  This makes it safer than the
previous scheme, which stashed away the original index file and
restored it after an aborted commit.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2006-02-10 00:45:59 -08:00
parent 4dc870d90c
commit cf7bb589af
3 changed files with 255 additions and 177 deletions

View File

@ -107,7 +107,7 @@ SCRIPT_SH = \
git-merge-one-file.sh git-parse-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \
git-prune.sh git-pull.sh git-push.sh git-rebase.sh \ git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \ git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \ git-resolve.sh git-revert.sh git-sh-setup.sh \
git-tag.sh git-verify-tag.sh git-whatchanged.sh \ git-tag.sh git-verify-tag.sh git-whatchanged.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \ git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@ -125,7 +125,7 @@ SCRIPT_PYTHON = \
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
git-cherry-pick git-show git-cherry-pick git-show git-status
# The ones that do not have to link with lcrypto nor lz. # The ones that do not have to link with lcrypto nor lz.
SIMPLE_PROGRAMS = \ SIMPLE_PROGRAMS = \
@ -443,6 +443,9 @@ git-cherry-pick: git-revert
git-show: git-whatchanged git-show: git-whatchanged
cp $< $@ cp $< $@
git-status: git-commit
cp $< $@
# These can record GIT_VERSION # These can record GIT_VERSION
git$X git.spec \ git$X git.spec \
$(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.sh,%,$(SCRIPT_SH)) \

View File

@ -3,13 +3,21 @@
# Copyright (c) 2005 Linus Torvalds # Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2006 Junio C Hamano # Copyright (c) 2006 Junio C Hamano
USAGE='[-a] [-i] [-s] [-v | --no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [--author <author>] [<path>...]' USAGE='[-a] [-i] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [--author <author>] [<path>...]'
SUBDIRECTORY_OK=Yes SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
git-rev-parse --verify HEAD >/dev/null 2>&1 || git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
initial_commit=t branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
case "$0" in
*status)
status_only=t
unmerged_ok_if_status=--unmerged ;;
*commit)
status_only=
unmerged_ok_if_status= ;;
esac
refuse_partial () { refuse_partial () {
echo >&2 "$1" echo >&2 "$1"
@ -17,23 +25,155 @@ refuse_partial () {
exit 1 exit 1
} }
SAVE_INDEX="$GIT_DIR/save-index$$" THIS_INDEX="$GIT_DIR/index"
NEXT_INDEX="$GIT_DIR/next-index$$"
rm -f "$NEXT_INDEX"
save_index () { save_index () {
cp "$GIT_DIR/index" "$SAVE_INDEX" cp "$THIS_INDEX" "$NEXT_INDEX"
}
report () {
header="#
# $1:
# ($2)
#
"
trailer=""
while read status name newname
do
printf '%s' "$header"
header=""
trailer="#
"
case "$status" in
M ) echo "# modified: $name";;
D*) echo "# deleted: $name";;
T ) echo "# typechange: $name";;
C*) echo "# copied: $name -> $newname";;
R*) echo "# renamed: $name -> $newname";;
A*) echo "# new file: $name";;
U ) echo "# unmerged: $name";;
esac
done
printf '%s' "$trailer"
[ "$header" ]
} }
run_status () { run_status () {
( (
# We always show status for the whole tree.
cd "$TOP" cd "$TOP"
# If TMP_INDEX is defined, that means we are doing
# "--only" partial commit, and that index file is used
# to build the tree for the commit. Otherwise, if
# NEXT_INDEX exists, that is the index file used to
# make the commit. Otherwise we are using as-is commit
# so the regular index file is what we use to compare.
if test '' != "$TMP_INDEX" if test '' != "$TMP_INDEX"
then then
GIT_INDEX_FILE="$TMP_INDEX" git-status GIT_INDEX_FILE="$TMP_INDEX"
else export GIT_INDEX_FILE
git-status elif test -f "$NEXT_INDEX"
then
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
fi fi
case "$branch" in
refs/heads/master) ;;
*) echo "# On branch $branch" ;;
esac
if test -z "$initial_commit"
then
if test -z "$verbose"
then
git-diff-index -M --cached --name-status \
--diff-filter=MDTCRA HEAD |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
' |
report "Updated but not checked in" "will commit"
else
if git-diff-index --cached -M -p --diff-filter=MDTCRA HEAD |
grep .
then
false
else
true
fi
fi
committable="$?"
else
echo '#
# Initial commit
#'
git-ls-files |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
s/^/A /
' |
report "Updated but not checked in" "will commit"
committable="$?"
fi
git-diff-files --name-status |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
' |
report "Changed but not updated" \
"use git-update-index to mark for commit"
if test -f "$GIT_DIR/info/exclude"
then
git-ls-files -z --others --directory \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore
else
git-ls-files -z --others --directory \
--exclude-per-directory=.gitignore
fi |
perl -e '$/ = "\0";
my $shown = 0;
while (<>) {
chomp;
s|\\|\\\\|g;
s|\t|\\t|g;
s|\n|\\n|g;
s/^/# /;
if (!$shown) {
print "#\n# Untracked files:\n";
print "# (use \"git add\" to add to commit)\n";
print "#\n";
$shown = 1;
}
print "$_\n";
}
'
case "$committable" in
0)
echo "nothing to commit"
exit 1
esac
exit 0
) )
} }
trap '
test -z "$TMP_INDEX" || {
test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
}
rm -f "$NEXT_INDEX"
' 0
################################################################
# Command line argument parsing and sanity checking
all= all=
also= also=
only= only=
@ -43,6 +183,7 @@ no_edit=
log_given= log_given=
log_message= log_message=
verify=t verify=t
verbose=
signoff= signoff=
force_author= force_author=
while case "$#" in 0) break;; esac while case "$#" in 0) break;; esac
@ -172,8 +313,8 @@ do
signoff=t signoff=t
shift shift
;; ;;
-v|--v|--ve|--ver|--veri|--verif|--verify) -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
verify=t verbose=t
shift shift
;; ;;
--) --)
@ -189,6 +330,9 @@ do
esac esac
done done
################################################################
# Sanity check options
case "$log_given" in case "$log_given" in
tt*) tt*)
die "Only one of -c/-C/-F/-m can be used." ;; die "Only one of -c/-C/-F/-m can be used." ;;
@ -207,9 +351,24 @@ case "$#,$also$only" in
# Later when switch the defaults, we will replace them with these: # Later when switch the defaults, we will replace them with these:
# echo >&2 "assuming --only paths..." # echo >&2 "assuming --only paths..."
# also= # also=
# If we are going to launch an editor, the message won't be
# shown without this...
test -z "$log_given$status_only" && sleep 1
;; ;;
esac esac
unset only unset only
case "$all,$also,$#" in
t,t,*)
die "Cannot use -a and -i at the same time." ;;
t,,[1-9]*)
die "Paths with -a does not make sense." ;;
,t,0)
die "No paths with -i does not make sense." ;;
esac
################################################################
# Prepare index to have a tree to be committed
TOP=`git-rev-parse --show-cdup` TOP=`git-rev-parse --show-cdup`
if test -z "$TOP" if test -z "$TOP"
@ -218,29 +377,25 @@ then
fi fi
case "$all,$also" in case "$all,$also" in
t,t)
die "Cannot use -a and -i at the same time." ;;
t,) t,)
case "$#" in
0) ;;
*) die "Paths with -a does not make sense." ;;
esac
save_index && save_index &&
( (
cd "$TOP" cd "$TOP"
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
git-diff-files --name-only -z | git-diff-files --name-only -z |
git-update-index --remove -z --stdin git-update-index --remove -z --stdin
) )
;; ;;
,t) ,t)
case "$#" in
0) die "No paths with -i does not make sense." ;;
esac
save_index && save_index &&
git-diff-files --name-only -z -- "$@" | git-diff-files --name-only -z -- "$@" |
(cd "$TOP" && git-update-index --remove -z --stdin) (
cd "$TOP"
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
git-update-index --remove -z --stdin
)
;; ;;
,) ,)
case "$#" in case "$#" in
@ -262,43 +417,68 @@ t,)
refuse_partial "Different in index and the last commit: refuse_partial "Different in index and the last commit:
$dirty_in_index" $dirty_in_index"
fi fi
commit_only=`git-ls-files -- "$@"` ;; commit_only=`git-ls-files -- "$@"`
# Build the temporary index and update the real index
# the same way.
if test -z "$initial_commit"
then
cp "$THIS_INDEX" "$TMP_INDEX"
GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
else
rm -f "$TMP_INDEX"
fi || exit
echo "$commit_only" |
GIT_INDEX_FILE="$TMP_INDEX" \
git-update-index --add --remove --stdin &&
save_index &&
echo "$commit_only" |
(
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
git-update-index --remove --stdin
) || exit
;;
esac esac
;; ;;
esac esac
git-update-index -q --refresh || exit 1 ################################################################
# If we do as-is commit, the index file will be THIS_INDEX,
# otherwise NEXT_INDEX after we make this commit. We leave
# the index as is if we abort.
trap ' if test -f "$NEXT_INDEX"
test -z "$TMP_INDEX" || {
test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
}
test -f "$SAVE_INDEX" && mv -f "$SAVE_INDEX" "$GIT_DIR/index"
' 0
if test "$TMP_INDEX"
then then
if test -z "$initial_commit" USE_INDEX="$NEXT_INDEX"
then else
GIT_INDEX_FILE="$TMP_INDEX" git-read-tree HEAD USE_INDEX="$THIS_INDEX"
else
rm -f "$TMP_INDEX"
fi || exit
echo "$commit_only" |
GIT_INDEX_FILE="$TMP_INDEX" git-update-index --add --remove --stdin &&
save_index &&
echo "$commit_only" |
git-update-index --remove --stdin ||
exit
fi fi
GIT_INDEX_FILE="$USE_INDEX" \
git-update-index -q $unmerged_ok_if_status --refresh || exit
################################################################
# If the request is status, just show it and exit.
case "$0" in
*status)
run_status
exit $?
esac
################################################################
# Grab commit message, write out tree and make commit.
if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
then then
if test "$TMP_INDEX" if test "$TMP_INDEX"
then then
GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
else else
"$GIT_DIR"/hooks/pre-commit GIT_INDEX_FILE="$USE_INDEX" "$GIT_DIR"/hooks/pre-commit
fi || exit fi || exit
fi fi
@ -398,8 +578,10 @@ else
PARENTS="" PARENTS=""
fi fi
{
run_status >>"$GIT_DIR"/COMMIT_EDITMSG test -z "$verbose" || echo '---'
run_status
} >>"$GIT_DIR"/COMMIT_EDITMSG
if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ] if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
then then
rm -f "$GIT_DIR/COMMIT_EDITMSG" rm -f "$GIT_DIR/COMMIT_EDITMSG"
@ -429,8 +611,14 @@ t)
fi fi
esac esac
grep -v '^#' < "$GIT_DIR"/COMMIT_EDITMSG | sed -e '
git-stripspace > "$GIT_DIR"/COMMIT_MSG /^---$/{
s///
q
}
/^#/d
' "$GIT_DIR"/COMMIT_EDITMSG |
git-stripspace >"$GIT_DIR"/COMMIT_MSG
if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG | if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
git-stripspace | git-stripspace |
@ -439,14 +627,20 @@ if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
then then
if test -z "$TMP_INDEX" if test -z "$TMP_INDEX"
then then
tree=$(git-write-tree) tree=$(GIT_INDEX_FILE="$USE_INDEX" git-write-tree)
else else
tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) && tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) &&
rm -f "$TMP_INDEX" rm -f "$TMP_INDEX"
fi && fi &&
commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) && commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
git-update-ref HEAD $commit $current && git-update-ref HEAD $commit $current &&
rm -f -- "$GIT_DIR/MERGE_HEAD" rm -f -- "$GIT_DIR/MERGE_HEAD" &&
if test -f "$NEXT_INDEX"
then
mv "$NEXT_INDEX" "$THIS_INDEX"
else
: ;# happy
fi
else else
echo >&2 "* no commit message? aborting commit." echo >&2 "* no commit message? aborting commit."
false false
@ -459,8 +653,4 @@ if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
then then
"$GIT_DIR"/hooks/post-commit "$GIT_DIR"/hooks/post-commit
fi fi
if test 0 -eq "$ret"
then
rm -f "$SAVE_INDEX"
fi
exit "$ret" exit "$ret"

View File

@ -1,115 +0,0 @@
#!/bin/sh
#
# Copyright (c) 2005 Linus Torvalds
#
USAGE=''
SUBDIRECTORY_OK='Yes'
. git-sh-setup
if [ "$#" != "0" ]
then
usage
fi
report () {
header="#
# $1:
# ($2)
#
"
trailer=""
while read status name newname
do
printf '%s' "$header"
header=""
trailer="#
"
case "$status" in
M ) echo "# modified: $name";;
D*) echo "# deleted: $name";;
T ) echo "# typechange: $name";;
C*) echo "# copied: $name -> $newname";;
R*) echo "# renamed: $name -> $newname";;
A*) echo "# new file: $name";;
U ) echo "# unmerged: $name";;
esac
done
printf '%s' "$trailer"
[ "$header" ]
}
branch=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD)
case "$branch" in
refs/heads/master) ;;
*) echo "# On branch $branch" ;;
esac
git-update-index -q --unmerged --refresh || exit
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
then
git-diff-index -M --cached --name-status --diff-filter=MDTCRA HEAD |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
' |
report "Updated but not checked in" "will commit"
committable="$?"
else
echo '#
# Initial commit
#'
git-ls-files |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
s/^/A /
' |
report "Updated but not checked in" "will commit"
committable="$?"
fi
git-diff-files --name-status |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
' |
report "Changed but not updated" "use git-update-index to mark for commit"
if test -f "$GIT_DIR/info/exclude"
then
git-ls-files -z --others --directory \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore
else
git-ls-files -z --others --directory \
--exclude-per-directory=.gitignore
fi |
perl -e '$/ = "\0";
my $shown = 0;
while (<>) {
chomp;
s|\\|\\\\|g;
s|\t|\\t|g;
s|\n|\\n|g;
s/^/# /;
if (!$shown) {
print "#\n# Untracked files:\n";
print "# (use \"git add\" to add to commit)\n#\n";
$shown = 1;
}
print "$_\n";
}
'
case "$committable" in
0)
echo "nothing to commit"
exit 1
esac
exit 0