git-squash is a simple command that, given the parent commit of X, squashes the commits X..HEAD into one. Signed-off-by: Stephan Beyer <s-beyer@xxxxxxx> --- Hi, perhaps this is a little surprising, but because of: > This could be done by providing a stand-alone git-squash command; I thought it could be right to quickly write this as a script. Here it is as RFC, yet without documentation. Junio, this could be a backend for the zucchini instruction, by just zucchini <n> => git-squash "HEAD~$(expr $n + 1)" ;-) Makefile | 1 + command-list.txt | 1 + git-squash.sh | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ t/t3150-squash.sh | 66 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 0 deletions(-) create mode 100755 git-squash.sh create mode 100755 t/t3150-squash.sh diff --git a/Makefile b/Makefile index 1937507..863004b 100644 --- a/Makefile +++ b/Makefile @@ -251,6 +251,7 @@ SCRIPT_SH += git-rebase.sh SCRIPT_SH += git-repack.sh SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-sh-setup.sh +SCRIPT_SH += git-squash.sh SCRIPT_SH += git-stash.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh diff --git a/command-list.txt b/command-list.txt index 3583a33..e342da3 100644 --- a/command-list.txt +++ b/command-list.txt @@ -108,6 +108,7 @@ git-show-branch ancillaryinterrogators git-show-index plumbinginterrogators git-show-ref plumbinginterrogators git-sh-setup purehelpers +git-squash mainporcelain git-stash mainporcelain git-status mainporcelain common git-stripspace purehelpers diff --git a/git-squash.sh b/git-squash.sh new file mode 100755 index 0000000..64b5949 --- /dev/null +++ b/git-squash.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# +# Squash commits from HEAD up to ... into one commit. + +SUBDIRECTORY_OK=Yes +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-squash [options] <commit-ish> +-- +author= use author <author> +C,reuse-message= use commit data from <commit-ish> +F,file= use commit message in <file> +m,message= use commit message <msg> +e,edit force edit of commit message +s,signoff add Signed-off-by: +include-merges don't fail if merge is in between +" + +. git-sh-setup +require_work_tree + +# test if working tree is clean +git rev-parse --verify HEAD >/dev/null && +git update-index --refresh && +git diff-files --quiet && +git diff-index --cached --quiet HEAD -- || + die "Working tree is dirty" + +git var GIT_COMMITTER_IDENT >/dev/null || + die "You need to set your committer info first" + +HEAD=$(git rev-parse --verify HEAD) || + die "No HEAD?" + +commitstr="git commit " +merges= +while test $# -gt 1 +do + case "$1" in + -m|-F|-C|--author) + commitstr="$commitstr $1 '$2'" + shift + ;; + -e|-s) + commitstr="$commitstr $1" + ;; + --include-merges) + merges=y + ;; + --) + break + ;; + esac + shift +done + +test $# -gt 2 && + die "Wrong number of arguments." +test $# -lt 2 && + die "No commit given." +shift + +commit="$1" +git rev-parse --verify "$commit" >/dev/null || + die "$commit is no valid commit." + +# check for merges if no --include-merges given +test -z "$merges" && +git log "$commit.." | grep -q "^Merge:" && + die "$commit..HEAD contains a merge commit. You may try --include-merges." + +# reset and commit +git reset --soft "$commit" +eval "$commitstr" || { + git reset --hard $HEAD + die "Commit failed" +} diff --git a/t/t3150-squash.sh b/t/t3150-squash.sh new file mode 100755 index 0000000..9f18995 --- /dev/null +++ b/t/t3150-squash.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +test_description='git-squash' + +. ./test-lib.sh + +test_expect_success setup ' + : >file1 && + : >file2 && + : >file3 && + : >file4 && + : >file5 && + : >file6 && + git add file1 && + git commit -m file1 && + git add file2 && + git commit -m file2 && + git checkout -b master2 HEAD^ && + git add file3 && + git commit -m file3 && + git checkout master && + git merge master2 && + git add file4 && + git commit -m file4 && + git add file5 && + git commit -m file5 && + git add file6 && + git commit -m file6 +' + +test_expect_success 'git-squash HEAD^ is a nop and fails' ' + git checkout -b first master && + ! git squash HEAD^ && + test "$(git rev-parse HEAD)" = "$(git rev-parse first)" && + test "$(git rev-parse master)" = "$(git rev-parse first)" +' + +test_expect_success 'git-squash works' ' + git checkout -b second master && + git squash -m "file5 and file6" --signoff HEAD^^ && + test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && + test -f file1 -a -f file2 -a -f file3 -a \ + -f file4 -a -f file5 -a -f file6 +' + +test_expect_success 'git-squash fails if merge in between' ' + git checkout -b third master && + ! git squash -C master master2^ && + test "$(git rev-parse HEAD)" = "$(git rev-parse third)" && + test "$(git rev-parse master)" = "$(git rev-parse third)" +' + +test_expect_success 'git-squash works with --include-merges if merge in between' ' + git checkout -b fourth master && + git squash -C master --include-merges master2^ && + test "$(git rev-parse HEAD)" = "$(git rev-parse fourth)" +' + +test_expect_success 'git-squash aborts correctly if no commit message given' ' + git checkout -b fifth master && + ! git squash HEAD~2 && + test "$(git rev-parse HEAD)" = "$(git rev-parse fifth)" && + test "$(git rev-parse master)" = "$(git rev-parse fifth)" +' + +test_done -- 1.5.5.3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html