git-wad

Manage files via git but not their content
git clone git://git.meso-star.fr/git-wad.git
Log | Files | Refs | README | LICENSE

commit 01447982a2fa4c275f8bc6a6f278c3106453b7c7
parent cf5d2216d5bbf838df35c8a605eca3eb1fddd8ba
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Sun, 19 Jan 2025 15:10:30 +0100

Fix the checkout/pull command

The smudge filter might not be executed properly to restore the contents
of WAD files. This was due to a bug in the way the file timestamp was
updated. It had to be set one second in the future to force execution of
the smudge filter when the checkout was invoked. And this was the case
until commit 7e6d417 which, while attempting to improve portability, did
not update the timestamp correctly: the file date was the current date.
As a result, a file recovered the very second it was fetched was not
restored. Hence the bug.

The problem is that, in shell, there's no simple way of converting a
date expressed in the number of seconds since midnight on January 1,
1970, into the format expected by the touch command used to update the
file's timestamp. However, the c99 compiler is standard (since POSIX
2001), as is the libc strftime function for converting a date. This
commit therefore simply compiles on demand a C program that calculates
and converts the date one second into the future. The design remains
simple and concise (everything is done in the same place), while the C
compilation overhead should remain negligible overall.

Finally, since this commit is aimed at portability, it replaces the -d
option of the touch command with the -t option. Both are POSIX options,
but -t is older (POSIX 2001 instead of POSIX 2008 for the -d option) and
therefore more widely supported.

Diffstat:
Mgit-wad | 57+++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 49 insertions(+), 8 deletions(-)

diff --git a/git-wad b/git-wad @@ -46,6 +46,9 @@ fi working_tree="$(git rev-parse --show-toplevel)" || die "$?" git_wad_tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/git_wad_XXXXXX")" || die "$?" +# Path toward the next_timestamp command +next_timestamp="${git_wad_tmpdir}/next_timestamp" + # Load local config file config_file="${working_tree}/.git_wad.cfg" if [ -f "${config_file}" ]; then @@ -230,13 +233,49 @@ objects_to_fetch() # [-1a] "if [ ! -f \"${GIT_WAD_OBJDIR}/{}\" ]; then echo \"{}\"; fi" < "${wads}" } -# Return the current timestamp plus one second -next_timestamp() +# Build the next_timestamp command, which returns the timestamp one +# second into the future. The date is formatted as expected by the +# restore function (see below), i.e. as the date "+%Y%m%D%H%M.%S" does +# for the current date. +restore_init() +{ + + # In shell, there is no standard method for converting a number of + # seconds from January 1, 1970 to midnight, into a specific format + # like the date command does for the current time. Some awk + # implementations provide the strftime function, but others do not. + # That said, the c99 C compiler has been standard since POSIX 2001, + # and the strftime function in charge of converting a date is a + # function of the standard C library. A C program can therefore be + # used to format a date on any POSIX-compatible system. This is the + # purpose of the program below. + # + # Note that it would be more efficient to pre-build the program and + # install it with git-wad. Its on-demand compilation nevertheless + # keeps the design more concise (everything is done in one place), + # while the compilation overhead should remain negligible overall. All + # the more so with several files to restore. Hence this choice of + # implementation, until performance problems really came to light. + cat << EOF > "${next_timestamp}.c" +#define _POSIX_C_SOURCE 200809L +#include <time.h> +#include <stdio.h> + +int main(void) { - awk 'BEGIN { - srand(); # Use the current epoch as seed - epoch = srand() + 1; # Return the current seed + 1, i.e. "epoch + 1" - }' | date +%Y-%m-%dT%H:%M:%S + char buf[32]; + time_t epoch; + struct tm* tm; + + if((epoch = time(NULL)) == (time_t)-1) return 1; + ++epoch; + if((tm = localtime(&epoch)) == NULL) return 1; + if((strftime(buf, sizeof(buf), "%Y%m%d%H%M.%S", tm)) == 0) return 1; + printf("%s\n", buf); + return 0; +} +EOF + c99 "${next_timestamp}.c" -o "${next_timestamp}" > /dev/null 2>&1 } restore() # WAD file @@ -262,8 +301,8 @@ restore() # WAD file # occur in the same second as the previous one. This is why we # explicitly set the file's timestamp to the next timestamp, i.e. # the timestamp one second in the future. - timestamp="$(next_timestamp)" - touch -d "${timestamp}" "${wad}" + timestamp="$("${next_timestamp}")" + touch -t "${timestamp}" "${wad}" git checkout-index --index --force "${wad}" fi } @@ -504,6 +543,8 @@ checkout() die 1 fi + restore_init + git ls-files "${working_tree}" \ | xargs grep -le "^${GIT_WAD_HEADER} [0-9a-z]\{64\} [0-9]\{1,\}$" \ | while read -r i; do