mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 15:36:21 -04:00
132 lines
2.8 KiB
Bash
Executable file
132 lines
2.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Change files modification time to their last commit date
|
|
#
|
|
|
|
# Bash unofficial strict mode
|
|
set -euo pipefail
|
|
IFS=$'\n\t'
|
|
|
|
if [[ "${1:-}" == "--newer" ]]; then
|
|
op=le
|
|
shift
|
|
else
|
|
op=eq
|
|
fi
|
|
|
|
# BSD systems
|
|
if date -j &>/dev/null; then
|
|
stat_flags="-f %m"
|
|
date_flags="-r"
|
|
else
|
|
# Non-BSD systems
|
|
stat_flags="-c %Y"
|
|
date_flags="-d@"
|
|
fi
|
|
|
|
bash_opts=()
|
|
if bash --help 2>&1 | grep -q -- '--noprofile'; then
|
|
bash_opts+=(--noprofile)
|
|
fi
|
|
if bash --help 2>&1 | grep -q -- '--norc'; then
|
|
bash_opts+=(--norc)
|
|
fi
|
|
|
|
status_opts=(--porcelain --short)
|
|
# %ct: committer date, UNIX timestamp / %at: author date, UNIX timestamp
|
|
whatchanged_opts=(--format='%ct')
|
|
if git status --help 2>&1 | grep -q -- "--no-renames"; then
|
|
status_opts+=(--no-renames)
|
|
whatchanged_opts+=(--no-renames)
|
|
fi
|
|
if git status --help 2>&1 | grep -q -- "--untracked-files"; then
|
|
status_opts+=(--untracked-files=no)
|
|
fi
|
|
if git status --help 2>&1 | grep -q -- "--ignored"; then
|
|
status_opts+=(--ignored=no)
|
|
fi
|
|
|
|
prefix="$(git rev-parse --show-prefix) "
|
|
strip="${#prefix}"
|
|
|
|
tmpfile=$(mktemp)
|
|
# shellcheck disable=SC2064
|
|
trap "rm -f '${tmpfile}'" 0
|
|
|
|
awk_flags=(
|
|
-F'\t'
|
|
-v date_flags="${date_flags}"
|
|
-v op="${op}"
|
|
-v stat_flags="${stat_flags}"
|
|
-v strip="${strip}"
|
|
-v tmpfile="${tmpfile}"
|
|
)
|
|
|
|
# sanity check, not required:
|
|
if awk --help 2>&1 | grep -q -- '--posix'; then
|
|
awk_flags+=(--posix)
|
|
fi
|
|
|
|
read -r -d '' awk_script <<"EOF" || true
|
|
BEGIN {
|
|
seen[""]=1
|
|
print "#!/usr/bin/env bash"
|
|
print "set +e"
|
|
print "t() {"
|
|
print " test -e \"$2\" || return 0"
|
|
printf(" test \"$(stat %s \"$2\" 2>/dev/null)\" -%s \"$1\" && return 0\n", stat_flags, op)
|
|
if (date_flags == "-d@") {
|
|
print " echo \"+ touch -h -d@$1 $2\""
|
|
print " touch -h -d@$1 \"$2\""
|
|
} else {
|
|
print " t=$(date -r$1 \"+%Y%m%d%H%M.%S\")"
|
|
print " echo \"+ touch -h -t $t $2\""
|
|
print " touch -h -t $t \"$2\""
|
|
}
|
|
print "}"
|
|
}
|
|
FILENAME==tmpfile {
|
|
skip[$1]=1
|
|
next
|
|
}
|
|
# skip blank lines
|
|
!/^$/ {
|
|
# skip deletes
|
|
if (substr($1, length($1), 1) ~ /D/) {
|
|
next
|
|
}
|
|
if (NF == 1) {
|
|
ct=$1
|
|
next
|
|
}
|
|
$2 = substr($2, strip, length($2)- strip + 1)
|
|
if ($2 in seen) {
|
|
next
|
|
}
|
|
if ($2 in skip) {
|
|
next
|
|
}
|
|
seen[$2]=1
|
|
# remove enclosing double quotes that git adds:
|
|
if (substr($2, 1, 1) == "\"" && substr($2, length($2), 1) == "\"") {
|
|
$2 = substr($2, 2, length($2) - 2)
|
|
# unescape remaining double quotes
|
|
gsub(/\\"/, "\"", $2)
|
|
# unescape escaped backslashes
|
|
gsub(/\\\\/, "\\", $2)
|
|
}
|
|
# escape apostrophes: ' => '\''
|
|
gsub(/'/, "'\\''", $2)
|
|
printf("t %s '%s'\n", ct, $2)
|
|
}
|
|
EOF
|
|
|
|
# prefix is stripped:
|
|
git --no-pager status "${status_opts[@]}" . \
|
|
| cut -c 4- >"${tmpfile}"
|
|
|
|
# prefix is not stripped:
|
|
git --no-pager whatchanged "${whatchanged_opts[@]}" . \
|
|
| awk "${awk_flags[@]}" "${awk_script}" "${tmpfile}" - \
|
|
| BASH_ENV='' bash "${bash_opts[@]}" -
|