add a script to diff rendered documentation
authorJeff King <peff@peff.net>
Mon, 6 Aug 2018 17:37:20 +0000 (13:37 -0400)
committerJunio C Hamano <gitster@pobox.com>
Mon, 6 Aug 2018 19:30:23 +0000 (12:30 -0700)
After making a change to the documentation, it's easy to
forget to check the rendered version to make sure it was
formatted as you intended. And simply doing a diff between
the two built versions is less trivial than you might hope:

- diffing the roff or html output isn't particularly
readable; what we really care about is what the end user
will see

- you have to tweak a few build variables to avoid
spurious differences (e.g., version numbers, build
times)

Let's provide a script that builds and installs the manpages
for two commits, renders the results using "man", and diffs
the result. Since this is time-consuming, we'll also do our
best to avoid repeated work, keeping intermediate results
between runs.

Some of this could probably be made a little less ugly if we
built support into Documentation/Makefile. But by relying
only on "make install-man" working, this script should work
for generating a diff between any two versions, whether they
include this script or not.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/.gitignore
Documentation/doc-diff [new file with mode: 0755]
index c7096f11f1e03f5eae463fa23dc7802136097f99..3ef54e0adbad5fbfd4468581037a2dcb9a9a4681 100644 (file)
@@ -12,3 +12,4 @@ cmds-*.txt
 mergetools-*.txt
 manpage-base-url.xsl
 SubmittingPatches.txt
+tmp-doc-diff/
diff --git a/Documentation/doc-diff b/Documentation/doc-diff
new file mode 100755 (executable)
index 0000000..f483fe4
--- /dev/null
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+OPTIONS_SPEC="\
+doc-diff [options] <from> <to> [-- <diff-options>]
+--
+j=n    parallel argument to pass to make
+f      force rebuild; do not rely on cached results
+"
+SUBDIRECTORY_OK=1
+. "$(git --exec-path)/git-sh-setup"
+
+parallel=
+force=
+while test $# -gt 0
+do
+       case "$1" in
+       -j)
+               parallel=$2; shift ;;
+       -f)
+               force=t ;;
+       --)
+               shift; break ;;
+       *)
+               usage ;;
+       esac
+       shift
+done
+
+if test -z "$parallel"
+then
+       parallel=$(getconf _NPROCESSORS_ONLN 2>/dev/null)
+       if test $? != 0 || test -z "$parallel"
+       then
+               parallel=1
+       fi
+fi
+
+test $# -gt 1 || usage
+from=$1; shift
+to=$1; shift
+
+from_oid=$(git rev-parse --verify "$from") || exit 1
+to_oid=$(git rev-parse --verify "$to") || exit 1
+
+cd_to_toplevel
+tmp=Documentation/tmp-doc-diff
+
+if test -n "$force"
+then
+       rm -rf "$tmp"
+fi
+
+# We'll do both builds in a single worktree, which lets "make" reuse
+# results that don't differ between the two trees.
+if ! test -d "$tmp/worktree"
+then
+       git worktree add --detach "$tmp/worktree" "$from" &&
+       dots=$(echo "$tmp/worktree" | sed 's#[^/]*#..#g') &&
+       ln -s "$dots/config.mak" "$tmp/worktree/config.mak"
+fi
+
+# generate_render_makefile <srcdir> <dstdir>
+generate_render_makefile () {
+       find "$1" -type f |
+       while read src
+       do
+               dst=$2/${src#$1/}
+               printf 'all:: %s\n' "$dst"
+               printf '%s: %s\n' "$dst" "$src"
+               printf '\t@echo >&2 "  RENDER $(notdir $@)" && \\\n'
+               printf '\tmkdir -p $(dir $@) && \\\n'
+               printf '\tMANWIDTH=80 man -l $< >$@+ && \\\n'
+               printf '\tmv $@+ $@\n'
+       done
+}
+
+# render_tree <dirname> <committish>
+render_tree () {
+       # Skip install-man entirely if we already have an installed directory.
+       # We can't rely on make here, since "install-man" unconditionally
+       # copies the files (spending effort, but also updating timestamps that
+       # we then can't rely on during the render step). We use "mv" to make
+       # sure we don't get confused by a previous run that failed partway
+       # through.
+       if ! test -d "$tmp/installed/$1"
+       then
+               git -C "$tmp/worktree" checkout "$2" &&
+               make -j$parallel -C "$tmp/worktree" \
+                       GIT_VERSION=omitted \
+                       SOURCE_DATE_EPOCH=0 \
+                       DESTDIR="$PWD/$tmp/installed/$1+" \
+                       install-man &&
+               mv "$tmp/installed/$1+" "$tmp/installed/$1"
+       fi &&
+
+       # As with "installed" above, we skip the render if it's already been
+       # done.  So using make here is primarily just about running in
+       # parallel.
+       if ! test -d "$tmp/rendered/$1"
+       then
+               generate_render_makefile "$tmp/installed/$1" "$tmp/rendered/$1+" |
+               make -j$parallel -f - &&
+               mv "$tmp/rendered/$1+" "$tmp/rendered/$1"
+       fi
+}
+
+render_tree $from_oid "$from" &&
+render_tree $to_oid "$to" &&
+git -C $tmp/rendered diff --no-index "$@" $from_oid $to_oid