3 #==============================================================================
5 # File ID: ae2451d2-43a1-11e6-86fa-175746503fe8
7 # Execute a command with only the staged changes in Git applied.
9 # Author: Øyvind A. Holm <sunny@sunbase.org>
10 # License: GNU General Public License version 2 or later.
11 #==============================================================================
25 while test -n "$1"; do
27 -g|
--git) opt_git
="$2"; shift 2 ;;
28 -h|
--help) opt_help
=1; shift ;;
29 -l|
--label) opt_label
="$2"; shift 2 ;;
30 -n|
--dry-run) opt_dry_run
=1; shift ;;
31 -p|
--pristine) opt_pristine
=1; shift ;;
32 -q|
--quiet) opt_quiet
=$
(($opt_quiet + 1)); shift ;;
33 -r|
--ref) opt_ref
="$2"; shift 2 ;;
34 -u|
--unmodified) opt_unmodified
=1; shift ;;
35 -v|
--verbose) opt_verbose
=$
(($opt_verbose + 1)); shift ;;
36 --version) echo $progname $VERSION; exit 0 ;;
39 if printf '%s\n' "$1" |
grep -q ^
-; then
40 echo "$progname: $1: Unknown option" >&2
48 opt_verbose
=$
(($opt_verbose - $opt_quiet))
51 test $1 -gt $opt_verbose && return;
53 echo "$progname: $*" >&2
58 $sim cd "$tmpdir" ||
{
59 echo $progname: $tmpdir: chdir error
>&2
62 $sim $GIT checkout
$opt_ref ||
{
63 echo $progname: Cannot check out ref
\"$opt_ref\" >&2
71 if test "$opt_help" = "1"; then
72 test $opt_verbose -gt 0 && { echo; echo $progname $VERSION; }
75 Execute a command with only the staged changes in Git applied.
77 Usage: $progname [options] [--] COMMAND
79 If you have lots of unrelated uncommitted changes in the current
80 repository and want to split up the commit, how can you easily check if
81 the changes passes the test suite? With all the other unrelated changes
82 it can be hard to make sure that only relevant changes becomes part of
83 the commit, and that they don't result in regressions. This script
84 clones the repository to the directory "$tmpdir" in the current
85 directory and applies the staged changes there (unless -u/--unmodified
86 or -p/--pristine is specified), chdirs to the same relative directory in
87 the clone and executes the command specified on the command line there.
89 If COMMAND contains any options starting with '-', add "--" in front of
90 the command so they won't be parsed by $progname, or surround it with
95 user@host:~/src/myproject/src/t \$ $progname make test
97 The command will be executed in ~/src/myproject/src/t/$tmpdir/ with
98 only the staged changes applied.
100 user@host:~/src/myproject/src/t \$ $progname -- ./run-tests -v
102 This command contains a "-v" option. Disable option parsing of the
103 command by prefixing it with "--".
105 $ $progname "make && cd t && make 2>&1 | less || echo fail"
107 With quotes, even control operators and pipes can be used.
112 Specify alternative git executable to use. It can also be set with
113 the TESTADD_GIT environment variable.
116 -l LABEL, --label LABEL
117 Add LABEL to the name of the destination directory, specify
118 destination directory to use.
120 Don't do anything, only print all commands to stdout.
122 Don't apply the staged changes, run the command with a clean HEAD.
124 Be more quiet. Can be repeated to increase silence.
126 Check out REF (a commit, branch or tag) before the patch is applied
127 and the command is executed. The script will abort if the patch
128 couldn't be applied to this ref.
130 Don't remove $tmpdir/, clone the repo and apply the patch, only
131 chdir to $tmpdir/ and run the command there as it was left from the
132 previous run. For example:
133 $ $progname make # This takes a long time, won't repeat
134 $ $progname -u make test # Run the tests in a finished build
136 Increase level of verbosity. Can be repeated.
138 Print version information.
144 if test "$opt_dry_run" = "1"; then
150 if test -n "$opt_label"; then
151 tmpdir
=".testadd-$opt_label.tmp"
154 if test -n "$opt_git"; then
156 elif test -n "$TESTADD_GIT"; then
162 if test -z "$1"; then
163 echo $progname: No
command specified
>&2
167 topdir
="$($GIT rev-parse --show-toplevel)"
168 subdir
="$($GIT rev-parse --show-prefix)"
170 if test -z "$topdir"; then
171 echo $progname: Could not get root dir of repo
>&2
175 if test -n "$opt_ref"; then
176 if test -z "$($GIT rev-parse --verify $opt_ref^{commit})"; then
177 echo $progname: $opt_ref: Invalid Git ref
>&2
182 msg
0 Using
\"$tmpdir\" as destination directory
184 if test "$opt_unmodified" = "1"; then
185 if test ! -d "$tmpdir"; then
186 echo $progname: $tmpdir not found
, -u/--unmodified needs it
>&2
189 msg
0 "No changes made in $tmpdir/"
191 $sim rm -rf "$tmpdir"
192 $sim $GIT clone
"$topdir" "$tmpdir"
193 test -n "$opt_ref" && checkout_ref
"$opt_ref"
195 if test "$opt_pristine" = "1"; then
196 msg
0 Running
command with unmodified HEAD
197 elif $GIT diff --cached --binary --no-textconv |
grep -q .
; then
198 msg
0 Applying staged changes
199 ap
="$GIT diff --cached --binary --no-textconv | "
200 ap
="$ap(cd \"$tmpdir\" && $GIT apply)"
202 echo $progname: Could not apply
patch >&2
206 msg
0 No staged changes
, running
command with clean HEAD
210 newdir
="$tmpdir/$subdir"
211 $sim cd "$newdir" ||
{
212 echo "$progname: $newdir: Cannot chdir" >&2
217 msg
0 "Executing \"$*\" in $(pwd)"
220 # vim: set ts=8 sw=8 sts=8 noet fo+=w tw=79 fenc=UTF-8 :