From: Eric Wong Date: Fri, 28 Mar 2025 03:32:09 +0000 (+0000) Subject: use git(1) for configs if libgit2 <1.8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=929410860264a82fbf8990aae2761048151da365;p=thirdparty%2Fpublic-inbox.git use git(1) for configs if libgit2 <1.8 libgit2 multiline support appeared broken before 1.8. I noticed the following commits in libgit2 seemed relevant to us, at least: * dff05bc30 (Multiline config values not preserved on saving, 2021-11-25) * de9a76b92 (config: properly handle multiline quotes, 2023-12-14) --- diff --git a/MANIFEST b/MANIFEST index 5e599990d..c61423982 100644 --- a/MANIFEST +++ b/MANIFEST @@ -392,6 +392,7 @@ lib/PublicInbox/XhcMsetIterator.pm lib/PublicInbox/approxidate.h lib/PublicInbox/khashl.h lib/PublicInbox/lg2.h +lib/PublicInbox/lg2_cfg.h lib/PublicInbox/xap_helper.h lib/PublicInbox/xh_cidx.h lib/PublicInbox/xh_date.h diff --git a/lib/PublicInbox/Lg2.pm b/lib/PublicInbox/Lg2.pm index a4ea4b763..d952f14c1 100644 --- a/lib/PublicInbox/Lg2.pm +++ b/lib/PublicInbox/Lg2.pm @@ -34,8 +34,17 @@ BEGIN { die "E: libgit2 not installed: $err\n" if $?; $vals->{$k} = $val; } + my $modversion = $vals->{modversion}; + *modversion = sub { $modversion }; + my $lg2_ver = eval("v$modversion"); my $f = "$dir/lg2.h"; $c_src = PublicInbox::IO::try_cat $f or die "cat $f: $!"; + # old versions were broken w/ multi-line, and also lacked the + # LIBGIT2_VERSION_CHECK macro (and Inline::C won't let us hide + # functions via CPP #if blocks) + if ($lg2_ver ge v1.8) { + $c_src .= PublicInbox::IO::try_cat "$dir/lg2_cfg.h"; + } # append pkg-config results to the source to ensure Inline::C # can rebuild if there's changes (it doesn't seem to detect # $CFG{CCFLAGSEX} nor $CFG{CPPFLAGS} changes) @@ -49,8 +58,7 @@ BEGIN { $CFG{CCFLAGSEX} = $vals->{cflags}; $CFG{LIBS} = $vals->{libs}; my $boot = 'git_libgit2_init();'; - eval("v$vals->{modversion}") ge v0.26 and - $boot .= <[%d]->[0] is NULL", i); - cmd0 = SvPV(*sv, dlen); - if (CFG_OP_EQ("--add", cmd0, dlen)) { - FETCH_CSTR(name, cmd, 1); - FETCH_CSTR(val, cmd, 2); - rc = git_config_set_multivar(cfg, name, "$^", val); - } else if (CFG_OP_EQ("--unset-all", cmd0, dlen)) { - FETCH_CSTR(name, cmd, 1); - rc = git_config_delete_multivar(cfg, name, ".*"); - if (rc == GIT_ENOTFOUND) - rc = GIT_OK; - } else if (CFG_OP_EQ("--replace-all", cmd0, dlen)) { - FETCH_CSTR(name, cmd, 1); - FETCH_CSTR(val, cmd, 2); - FETCH_CSTR(re, cmd, 3); - rc = git_config_set_multivar(cfg, name, re, val); - } else { - name = cmd0; - FETCH_CSTR(val, cmd, 1); - rc = git_config_set_string(cfg, name, val); - } - } - e = rc == GIT_OK ? NULL : giterr_last(); - git_config_free(cfg); - if (rc != GIT_OK) - croak("config -f %s (%d %s)", - f, rc, e ? e->message : "unknown"); -} diff --git a/lib/PublicInbox/lg2_cfg.h b/lib/PublicInbox/lg2_cfg.h new file mode 100644 index 000000000..f8fa48294 --- /dev/null +++ b/lib/PublicInbox/lg2_cfg.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) all contributors + * License: AGPL-3.0+ + * + * multiline configs broken before 1.8, I only noticed the former myself: + * dff05bc30 (Multiline config values not preserved on saving, 2021-11-25) + * de9a76b92 (config: properly handle multiline quotes, 2023-12-14) + */ +#define CFG_OP_EQ(op, cmd0, dlen) \ + (dlen == (sizeof(op) - 1) && !memcmp(op, cmd0, sizeof(op) - 1)) + +/* leaks on internal bugs: */ +#define FETCH_CSTR(var, ary, i) do { \ + SV **s = av_fetch(ary, i, 0); \ + if (!s) croak("BUG: " #var " = ary[%d] is NULL", i); \ + var = SvPV_nolen(*s); \ +} while (0) + +void cfgwr_commit(const char *f, AV *todo) +{ + I32 i, todo_max = av_len(todo); + SV **sv; + git_config *cfg; + const git_error *e; + const char *cmd0, *name, *val, *re; + int rc = git_config_open_ondisk(&cfg, f); + STRLEN dlen; + AV *cmd; + + for (i = 0; rc == GIT_OK && i <= todo_max; i++) { + sv = av_fetch(todo, i, 0); + /* leaks on internal bugs: */ + if (!SvROK(*sv)) croak("BUG: not a ref"); + cmd = (AV *)SvRV(*sv); + sv = av_fetch(cmd, 0, 0); + if (!sv) croak("BUG: cmd0 = $todo->[%d]->[0] is NULL", i); + cmd0 = SvPV(*sv, dlen); + if (CFG_OP_EQ("--add", cmd0, dlen)) { + FETCH_CSTR(name, cmd, 1); + FETCH_CSTR(val, cmd, 2); + rc = git_config_set_multivar(cfg, name, "$^", val); + } else if (CFG_OP_EQ("--unset-all", cmd0, dlen)) { + FETCH_CSTR(name, cmd, 1); + rc = git_config_delete_multivar(cfg, name, ".*"); + if (rc == GIT_ENOTFOUND) + rc = GIT_OK; + } else if (CFG_OP_EQ("--replace-all", cmd0, dlen)) { + FETCH_CSTR(name, cmd, 1); + FETCH_CSTR(val, cmd, 2); + FETCH_CSTR(re, cmd, 3); + rc = git_config_set_multivar(cfg, name, re, val); + } else { + name = cmd0; + FETCH_CSTR(val, cmd, 1); + rc = git_config_set_string(cfg, name, val); + } + } + e = rc == GIT_OK ? NULL : giterr_last(); + git_config_free(cfg); + if (rc != GIT_OK) + croak("config -f %s (%d %s)", + f, rc, e ? e->message : "unknown"); +} diff --git a/t/lg2_cfg.t b/t/lg2_cfg.t index e8cdff4ae..067bc866f 100644 --- a/t/lg2_cfg.t +++ b/t/lg2_cfg.t @@ -24,6 +24,9 @@ my $cfgwr_commit = $ENV{TEST_VALIDATE_GIT_BEHAVIOR} ? sub { } } : PublicInbox::Lg2->can('cfgwr_commit'); +SKIP: { +$cfgwr_commit or skip 'libgit2 '.PublicInbox::Lg2->modversion. + ' < 1.8 too old for multiline configs', 1; my $cfg; # for explain() if needed my $get = sub { my (@key) = @_; @@ -57,4 +60,16 @@ $cfgwr_commit->($f, [ [ qw(x.b d) ], [ qw(--add x.b e) ], [ qw(--add x.b f) ] ]); is_xdeeply $get->('x.b'), [ qw(d e f) ], 'multiple changes chained'; +use PublicInbox::IO qw(write_file try_cat); +my $src = <', $f, $src; +$cfgwr_commit->($f, [ [ qw(x.b d) ], [ qw(--add x.b e) ] ]); +like try_cat($f), qr/^\Q$src\E$/sm, 'newlines preserved'; +} # SKIP + done_testing;