2 # make-srpm: srpm builder, helps build srpm out of rcs sources
3 # git helper; export correct git release and patches ready to
6 # Copyright (C) 2008 Sam Liddicott <sam@liddicott.com>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # git checkout $ORIGIN (or HEAD) as a tarfile $2 with path prefix $1
25 # but only checkout $SOURCE_PATHS if specified
27 # by HEAD here we mean to select current checkout of current branch
28 git archive
--format=tar --prefix="$1/" "${ORIGIN:-HEAD}" ${SOURCE_PATHS} |\
32 # git checkout $ORIGIN (or HEAD) as directory $1
33 # but only checkout $SOURCE_PATHS if specified
37 git archive
--format=tar "${ORIGIN:-HEAD}" ${SOURCE_PATHS} |\
44 awk -f /dev
/stdin
"$@" <<'#AWK'
47 # find-longest-path PARENT CHILD
48 # outputs the set of commits, choosing branches that result in the most number
49 # of commits (longest path) between PARENT and CHILD in that order
50 # Uses git rev-parse and git rev-list
52 # recursively find longest path from $child to $parent
53 # return answer in $path.
54 # There are no branches that dont lead to parent because of input filtering
55 function paths
(rev, branches
, branch
, best_path
, best_path_len
, path
, n
) {
56 if (rev == _parent
) return _parent
;
58 if (rev in seen
) return seen
[rev];
60 split(nodes
[rev], branches
);
61 for(n
=1; (n
) in branches
; n
++) {
64 if (length
(path
) > best_path_len
) {
65 best_path_len
=length
(path
);
70 # remember best answer for next time if we try this branch again as we
71 # may if branches re-merge
73 seen
[rev]=best_path
"\n" rev;
82 ("git rev-parse " ARGV
[1] "^{commit}") | getline _parent
;
85 ("git rev-parse " ARGV
[2] "^{commit}") | getline _child
;
88 # initialize parent to exist so we dont filter it out
91 # read in a --topo-order --reverse tree but filter out anything whose
92 # parent is not already in (nodes) which will be primed by our top parent
93 while(("git rev-list --topo-order --reverse --parents ^" _parent
" " _child
) | getline
) {
94 for(i
=2; i
<=NF
; i
++) {
96 nodes
[$1]=nodes
[$1] " " $i;
100 printf("%s\n",paths
(_child
));
107 # Takes a git url and returns the name of the repo probably using the same rules git clone does
108 # If URL ends in /.git then use parent folder name
109 # otherwise use basename with .git removed
112 */.git
) basename `dirname $1` .git |
sed -e "s/.*://";;
113 *) basename $1 .git
;;
117 # thanks to thiago and pasky (#git 19th August 2008) for this:
118 # output tags that are parent-ish to $1 (or HEAD).
119 # restrict to tags matching glob ORIGINAL_PATTERN if specified
121 # sed presumes that multiple tags are like (tag: ...) (tag: ...)
122 # git log --decorate --reverse --pretty=oneline "${1:-$RELEASE}" |\
123 # sed -ne 'There;:here;s/^(tag: refs\/tags\///;Tlook;{s/)/\n/;P;D};:look;s/\((tag: refs\/tags\/\)/\n\1/;D;'
125 # more reliable but slower
126 git tag
-l ${ORIGIN_PATTERN:+"$ORIGIN_PATTERN"} |
sort |
while read tag
128 test "$(git merge-base "$tag" "${1:-HEAD}")" = "$(git rev-parse "$tag^
{commit
}")" && echo $tag;
132 # do a diff between two git revisions, also output the log of the commits included
133 # output to stdout, return true unless the patch was empty
134 # probably invoke as:
135 # git_rev_diff old_tag..new_tag -- file/path other/file/path
137 # patch v2.5.4 complains when it finds patches in the pre-amble even if they are indented!
138 # and some of the samba comments contain indented patches
139 git log
--pretty=fuller
"$@" |
sed -e "s/^/|/"
141 echo "Combined summary"
143 # pgas on irc #bash isn't sure how he helped, but grep is great at passing
144 # text verbatim while showing in the exit code if the text was empty!
145 git
diff -p --no-renames --stat --summary "$@" | git_fix_empty_files | check_empty_patch
148 # Patch requires some file content difference, but git also generates output
149 # when metadata changes. This function checks that the patch has material changes
150 function check_empty_patch
() {
165 # Because unified diff's need at least some context, they can't create or delete
166 # empty files, so git_patch doesn't try to represent creation or deletion through
168 # git_fix_empty_files looks for such creation and deletion and manages it as a
169 # two stage process, making a 1 line first and then an empty file
170 git_fix_empty_files
() {
175 function do_empty() {
176 if (diff!="" && deleted!="" && indx!="") {
177 printf("Make patch give the file 1 line\n");
178 printf("--- %s\n",diff);
179 printf("+++ %s\n",diff);
180 printf("@@ -0,0 +1 @@\n+\n");
181 printf("Make patch delete the 1 line file\n");
182 printf("--- %s\t\n",diff);
183 printf("+++ /dev/null\t1970-01-01 01:00:00.000000000\n");
184 printf("@@ -1 +0,0 @@\n-\n");
186 if (diff!="" && created!="" && indx!="") {
187 printf("Make patch create the file with 1 line\n");
188 printf("--- %s\n",diff);
189 printf("+++ %s\n",diff);
190 printf("@@ -0,0 +1 @@\n+\n");
191 printf("Make patch create delete the line but keep the file\n");
192 printf("--- %s\n",diff);
193 printf("+++ %s\n",diff);
194 printf("@@ -1 +0,0 @@\n-\n");
199 function no_empty() {
207 if (in_hunk > 0) in_hunk--;
212 if (! in_hunk) diff=$3;
215 /^deleted file mode / {
216 if (! in_hunk && diff!="") deleted=$0;
220 if (! in_hunk && diff!="") created=$0;
224 if (! in_hunk && diff!="") indx=$0;
235 else c1=strtonum(substr($2, p+1));
239 else in_hunk=strtonum(substr($3, p+1));
241 if (c1 > in_hunk) in_hunk=c1;
254 # like git format-patch except iterates a revision path between $1 and $2 and
255 # outputs diff's between all trees. Takes extra -- file/path arguments too,
256 # or in fact any argument accepted by git diff AND git log, but must appear
261 local SUFFIX
=".patch"
262 while getopts "o:s:" OPT
265 o
) OUTDIR
="$OPTARG";;
266 s
) SUFFIX
="$OPTARG";;
269 shift $
(($OPTIND - 1))
273 local REV
="`git rev-parse "$1^
{commit
}"`"
274 local END
="`git rev-parse "$2^
{commit
}"`"
277 PATCH_NO
=${START_PATCH_NO:-0}
280 printf -v _PATCH_NO
"%04d" $PATCH_NO
281 if git_diff_rev
"$REV"..
"$sha" "$@" > "$OUTDIR/$_PATCH_NO-$sha.$SUFFIX"
283 echo "$OUTDIR/$_PATCH_NO-$sha.$SUFFIX"
284 PATCH_NO
=$
(( PATCH_NO
+ 1 ))
285 else rm -f "$OUTDIR/$_PATCH_NO-$sha.$SUFFIX"
288 done < <( git_linearize
"$REV" "$END" )
293 # 2. and some patches,
294 # 3. and patches.list and patches.apply
296 # ORIGIN which we will try and guess at using
297 # ORIGIN_PATTERN if we don't know ORIGIN and can't guess from RELEASE
298 # RELEASE which will default to THIS+
302 : ${TMPDIR:=$SRC_EXPORT}
305 THIS|LOCAL|
"") _RELEASE
=HEAD
; LOCAL
=1;;
306 ?
*) _RELEASE
="$RELEASE"; LOCAL
="";;
308 RELEASE_COMMIT
="$(git rev-parse "$_RELEASE^
{commit
}")"
310 # remember this and provide it during rpm config and build time
311 VERSION_INFO
="`git show --pretty=format:"%h
%ct
%H
%cd" $RELEASE_COMMIT 2>/dev/null | head -1`"
313 RELEASE_TAG
="`git_parent_tags "$_RELEASE" | sort | tail -n 1 | grep '^' || echo HEAD`"
314 if [ -n "$VERSION_PATTERN" -a -z "$VERSION" ]
316 VERSION
=`glob_match "$VERSION_PATTERN" "$RELEASE_TAG"`
318 VERSION
="${2:-${VERSION:-$Version}}"
320 : ${ORIGIN:=$RELEASE_TAG}
321 ORIGIN_COMMIT
="`git rev-parse $ORIGIN^{commit}`"
322 test -z "$ORIGIN" -o "$(git merge-base "$ORIGIN_COMMIT" "$RELEASE_COMMIT")" != "$ORIGIN_COMMIT" && \
323 die
"Can't find origin $ORIGIN to use as pristine source for $_RELEASE"
325 # append date if we are including uncomitted files
328 RELEASE_SUFFIX
="${RELEASE_TAG//-/_}_`date +%Y%m%d`_g`git show --pretty=format:"%h
" "$RELEASE_COMMIT" | head -n 1`_local"
330 RELEASE_SUFFIX
=`git describe "$_RELEASE" 2>/dev/null | sed -e 's/^.*\///;s/-/_/g'`
331 RELEASE_SUFFIX
="${RELEASE_SUFFIX#${RELEASE_TAG}_}"
332 if ! test -n "$RELEASE_SUFFIX"
333 then # git describe failed
334 RELEASE_SUFFIX
="GIT_`git show --pretty=format:"%h
" "$RELEASE_COMMIT" | head -n 1`"
338 # release tag is now part of release_suffix because of "git description" command
339 RELEASE_NAME
="$NAME-$VERSION${RELEASE_SUFFIX:+_$RELEASE_SUFFIX}"
341 # if GIT_ORIGIN is like origin/blah then we can't use it as a filename part
342 ORIGIN_NAME
="$NAME-${ORIGIN//\//-}"
344 TAR_PREFIX
="$ORIGIN_NAME"
345 TAR_NAME
="$TAR_PREFIX.tar.gz"
347 SPEC_VERSION
="$VERSION"
348 git_archive
"$ORIGIN_NAME" "$TMPDIR/$TAR_NAME"
350 # generate patches, also restrict patches to $SOURCE_PATHS
351 test -d "$TMPDIR" || die
"Can't prepare srpm files in $TMPDIR"
354 > "$TMPDIR/patches.list"
356 # if [ "$ORIGIN_COMMIT" != "$RELEASE_COMMIT" ]
358 git_format_diff
-o "$TMPDIR" -s "$Name-$VERSION.patch" \
359 $ORIGIN $_RELEASE ${SOURCE_PATHS:+-- $SOURCE_PATHS} |\
360 sed -e "s/^.*\///" -e "s/^\(0*\)\([0-9]*\)/Patch\2: \1\2/">> "$TMPDIR/patches.list"
363 # also uncomitted changes?
366 # also local uncomitted patches
367 set `wc -l "$TMPDIR/patches.list"`
368 PATCH_NO
="$(( $1 + ${START_PATCH_NO:-0} ))"
370 # any local files in the current change set
371 PATCH_NAME
="git-local-cached.patch"
372 git
diff --cached ${SOURCE_PATHS:+-- $SOURCE_PATHS} > "$TMPDIR/$PATCH_NAME"
373 if test -s "$TMPDIR/$PATCH_NAME"
375 PATCH_NO
="`expr "${PATCH_NO:-0}" \+ 1`"
376 echo "Patch$PATCH_NO: $PATCH_NAME" >> "$TMPDIR/patches.list"
378 rm "$TMPDIR/$PATCH_NAME"
381 # any local files NOT in the current change set
382 PATCH_NAME
="git-local.patch"
383 git
diff ${SOURCE_PATHS:+-- $SOURCE_PATHS} > "$TMPDIR/$PATCH_NAME"
384 if test -s "$TMPDIR/$PATCH_NAME"
386 PATCH_NO
="`expr "${PATCH_NO:-0}" \+ 1`"
387 echo "Patch$PATCH_NO: $PATCH_NAME" >> "$TMPDIR/patches.list"
389 rm "$TMPDIR/$PATCH_NAME"
393 # generate patches.apply
394 sed -e "s/^Patch\([0-9]*\):.*/%patch\1 -p$PATCH_LEVEL/" < "$TMPDIR/patches.list" > "$TMPDIR/patches.apply"
396 SPEC_RELEASE
="$RELEASE_SUFFIX"