std sh ga-infounused
[sunny256-utils.git] / git-testadd
blob9a7cec721356ce0ce83cf3f56f1c768e46c99593
1 #!/bin/sh
3 #==============================================================================
4 # git-testadd
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 #==============================================================================
13 progname=git-testadd
14 VERSION=0.11.1
16 opt_git=''
17 opt_help=0
18 opt_label=''
19 opt_dry_run=0
20 opt_pristine=0
21 opt_quiet=0
22 opt_ref=''
23 opt_unmodified=0
24 opt_verbose=0
25 while test -n "$1"; do
26 case "$1" in
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 ;;
37 --) shift; break ;;
39 if printf '%s\n' "$1" | grep -q ^-; then
40 echo "$progname: $1: Unknown option" >&2
41 exit 1
42 else
43 break
45 break ;;
46 esac
47 done
48 opt_verbose=$(($opt_verbose - $opt_quiet))
50 msg() {
51 test $1 -gt $opt_verbose && return;
52 shift
53 echo "$progname: $*" >&2
56 checkout_ref() {
58 $sim cd "$tmpdir" || {
59 echo $progname: $tmpdir: chdir error >&2
60 exit 1
62 $sim $GIT checkout $opt_ref || {
63 echo $progname: Cannot check out ref \"$opt_ref\" >&2
64 exit 1
69 tmpdir=".testadd.tmp"
71 if test "$opt_help" = "1"; then
72 test $opt_verbose -gt 0 && { echo; echo $progname $VERSION; }
73 cat <<END
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
91 quotes.
93 Examples:
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.
109 Options:
111 -g X, --git X
112 Specify alternative git executable to use. It can also be set with
113 the TESTADD_GIT environment variable.
114 -h, --help
115 Show this help.
116 -l LABEL, --label LABEL
117 Add LABEL to the name of the destination directory, specify
118 destination directory to use.
119 -n, --dry-run
120 Don't do anything, only print all commands to stdout.
121 -p, --pristine
122 Don't apply the staged changes, run the command with a clean HEAD.
123 -q, --quiet
124 Be more quiet. Can be repeated to increase silence.
125 -r REF, --ref REF
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.
129 -u, --unmodified
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
135 -v, --verbose
136 Increase level of verbosity. Can be repeated.
137 --version
138 Print version information.
141 exit 0
144 if test "$opt_dry_run" = "1"; then
145 sim=echo
146 else
147 sim=''
150 if test -n "$opt_label"; then
151 tmpdir=".testadd-$opt_label.tmp"
154 if test -n "$opt_git"; then
155 GIT="$opt_git"
156 elif test -n "$TESTADD_GIT"; then
157 GIT="$TESTADD_GIT"
158 else
159 GIT=git
162 if test -z "$1"; then
163 echo $progname: No command specified >&2
164 exit 1
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
172 exit 1
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
178 exit 1
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
187 exit 1
189 msg 0 "No changes made in $tmpdir/"
190 else
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)"
201 $sim eval "$ap" || {
202 echo $progname: Could not apply patch >&2
203 exit 1
205 else
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
213 exit 1
216 echo >&2
217 msg 0 "Executing \"$*\" in $(pwd)"
218 $sim eval "$@"
220 # vim: set ts=8 sw=8 sts=8 noet fo+=w tw=79 fenc=UTF-8 :