From 044ed0720b9af19ad0e586adf907f77f908f0bf3 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 16 Oct 2026 11:17:24 +0200 Subject: feat(git): gitcfg, global git config through git itself list, set, add and unset over git config --global. --fixed-value lets a write touch one value of a multi-valued key. set and unset check the old value is still there first: --replace-all with no match appends and exits 0, and the file may have changed in an editor since the page read it. Co-Authored-By: Claude Sonnet 5.5 --- desktop/modules/git/gitcfg | 56 +++++++++++++++++++++++++++ desktop/modules/git/test-gitcfg.sh | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100755 desktop/modules/git/gitcfg create mode 100755 desktop/modules/git/test-gitcfg.sh (limited to 'desktop/modules/git') diff --git a/desktop/modules/git/gitcfg b/desktop/modules/git/gitcfg new file mode 100755 index 0000000..df955bf --- /dev/null +++ b/desktop/modules/git/gitcfg @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Copyright (C) 2026 Danilo M. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# The global git config, for the drawer's git page. +# +# gitcfg list git config --global -z --list +# gitcfg set replace the one value equal to +# gitcfg add add a value +# gitcfg unset remove the one value equal to +# +# Every write goes through git config, so the file keeps its comments, layout +# and quoting. --fixed-value makes a value a literal rather than a regex, so a +# write touches one value of a multi-valued key, and the `--` before the key +# keeps a leading dash an argument rather than an option. +# +# A write prints nothing on success. On failure it prints the reason on +# stdout, for the page, and exits 1. + +set -u + +usage() { + printf 'usage: %s list | set | add | unset \n' \ + "${0##*/}" >&2 + exit 2 +} + +g() { + local err + err="$(git config --global "$@" 2>&1)" || { printf '%s\n' "${err:-git config failed}"; exit 1; } +} + +# --replace-all with no value equal to appends and succeeds, and +# --unset of a missing value fails silently, so both check first: the file +# may have changed in the editor since the page read it. +has() { + git config --global --fixed-value --get-all -- "$1" "$2" >/dev/null \ + || { printf '%s no longer holds that value\n' "$1"; exit 1; } +} + +case "${1:-}" in + list) [[ $# -eq 1 ]] || usage; exec git config --global -z --list ;; + set) [[ $# -eq 4 ]] || usage; has "$2" "$3"; g --fixed-value --replace-all -- "$2" "$4" "$3" ;; + add) [[ $# -eq 3 ]] || usage; g --add -- "$2" "$3" ;; + unset) [[ $# -eq 3 ]] || usage; has "$2" "$3"; g --fixed-value --unset -- "$2" "$3" ;; + *) usage ;; +esac diff --git a/desktop/modules/git/test-gitcfg.sh b/desktop/modules/git/test-gitcfg.sh new file mode 100755 index 0000000..328a575 --- /dev/null +++ b/desktop/modules/git/test-gitcfg.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# +# Copyright (C) 2026 Danilo M. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# The one runnable check for gitcfg. GIT_CONFIG_GLOBAL points git at a file +# in a temp directory, so the real ~/.gitconfig is never read or written. +# +# Usage: ./test-gitcfg.sh (exit 0 = all passed) + +set -u + +here="$(cd "$(dirname "$0")" && pwd)" +ctl="$here/gitcfg" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +export GIT_CONFIG_GLOBAL="$tmp/gitconfig" + +fail=0 +check() { + if [[ "$2" == "$3" ]]; then + echo "ok $1" + else + echo "FAIL $1" + printf ' want: %q\n got: %q\n' "$2" "$3" + fail=1 + fi +} +vals() { git config --global --get-all "$1" | paste -sd,; } + +# Values starting with a dash, as versionsort.prereleasesuffix holds, and a +# comment that every write must leave alone. +printf '# keep me\n[v]\n\ts = -rc\n\ts = -beta\n\ts = -alpha\n' > "$GIT_CONFIG_GLOBAL" + +check "list" $'v.s\n-rc|v.s\n-beta|v.s\n-alpha|' "$("$ctl" list | tr '\0' '|')" + +out="$("$ctl" set v.s -beta -BETA)"; rc=$? +check "set one of three: rc" 0 "$rc" +check "set one of three: silent" "" "$out" +check "set one of three: values" "-rc,-BETA,-alpha" "$(vals v.s)" + +# git would append here and exit 0; gitcfg must refuse instead. +out="$("$ctl" set v.s -gone x)"; rc=$? +check "set missing old: rc" 1 "$rc" +check "set missing old: says so" 1 "$([[ -n "$out" ]] && echo 1)" +check "set missing old: nothing appended" "-rc,-BETA,-alpha" "$(vals v.s)" + +out="$("$ctl" unset v.s -rc)"; rc=$? +check "unset one of three: rc" 0 "$rc" +check "unset one of three: values" "-BETA,-alpha" "$(vals v.s)" + +out="$("$ctl" unset v.s -gone)"; rc=$? +check "unset missing: rc" 1 "$rc" +check "unset missing: says so" 1 "$([[ -n "$out" ]] && echo 1)" + +out="$("$ctl" add a.b "x=y")"; rc=$? +check "add with =: rc" 0 "$rc" +check "add with =: value" "x=y" "$(vals a.b)" + +out="$("$ctl" add "bad key" v)"; rc=$? +check "add bad key: rc" 1 "$rc" +check "add bad key: git's error" 1 "$([[ "$out" == *section* ]] && echo 1)" + +check "comment kept" 1 "$(grep -c '^# keep me$' "$GIT_CONFIG_GLOBAL")" + +"$ctl" bogus >/dev/null 2>&1; rc=$? +check "usage: rc" 2 "$rc" + +exit "$fail" -- cgit v1.2.3