)]}'
{
  "log": [
    {
      "commit": "c62bc7932cf0e29a82bc0ab68b1f502c86ac611d",
      "tree": "f9f99a753dedd8823bde3b984da8da13dd7f6bba",
      "parents": [
        "2bd4f893bb333b1c2ec9fce130e791b11d0476ba"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Oct 15 22:04:27 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Oct 15 22:04:27 2026 +0000"
      },
      "message": "groupsio-webhook: build the app at import, so uwsgi can find it\n\nuwsgi looks the callable up in the module\u0027s namespace and never calls a\nmodule __getattr__, so the lazily built app was never found and every\nwebhook call got a 500. Build it when the module loads whenever CONFIG\nis set; without CONFIG, as in the tests, the module still only offers\ncreate_app().\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "2bd4f893bb333b1c2ec9fce130e791b11d0476ba",
      "tree": "79e71c41bf5a93d18565795896b65066129b4125",
      "parents": [
        "e5db6532530394ef84bf25ac8901d8a33fe5b9d0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Oct 14 18:25:09 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Oct 14 18:25:09 2026 +0000"
      },
      "message": "groupsio-webhook: touch notify_file after each archived message\n\nThe new lkml host has to index the inboxes the webhook writes, so NNTP\nsees new messages. Doing that in the request would hold up the response,\nand a failed index would make groups.io resend a message that is already\narchived. Touch a configured file instead, so a systemd path unit (or\nanything else) can run the follow-up work on its own.\n\nKey changes:\n\n- Add the optional notify_file setting, touched only after a successful\n  write\n- Log a failed touch without failing the request\n- Test both, and document the setting in the sample config\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "e5db6532530394ef84bf25ac8901d8a33fe5b9d0",
      "tree": "3b4c36fa433cb2f1300bc9c617cc5e264c5164e0",
      "parents": [
        "4c1b6cb66159e2e9a8e8ca8abe267de44fbc8ab8"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Oct 14 17:40:24 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Oct 14 17:40:24 2026 +0000"
      },
      "message": "groupsio-webhook: archive into local public-inbox v2 repos via ezpi\n\nRelaying messages over SMTP to the archiver only for them to end up in\npublic-inbox anyway adds a hop that can fail and a mail queue to babysit.\nWrite each message directly into a local v2 inbox with ezpi instead,\ncreating it on first use, and keep anything we can\u0027t archive as a\nreplayable JSON payload rather than losing it.\n\nKey changes:\n\n- Write to {inboxdir}/{list}.{domain} via ezpi.add_rfc822_v2, with an\n  optional per-list \"inbox\" override; drop SMTP and v1 pirepo support\n- Require groupsio_psk, since unsigned requests could now create inboxes\n- Validate the group address before using it as a path, and stop using\n  it in emergency file names, to prevent path traversal\n- Save raw payloads to emerg_dir: 200 for unknown lists and messages\n  ezpi rejects, 500 for write failures so groups.io retries\n- Return 400 rather than 500 for empty, malformed, or non-JSON payloads\n- Build the app in create_app() and expose \"app\" lazily so the module\n  can be imported without CONFIG, and add tests\n- Pass ruff and mypy --strict\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "4c1b6cb66159e2e9a8e8ca8abe267de44fbc8ab8",
      "tree": "7d90f64ea97711de83792eab2a337d77a6c221a8",
      "parents": [
        "ef00d0bfe0873c0866308549ae95fdeb222fe746"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Oct 14 17:37:27 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Oct 14 17:37:27 2026 +0000"
      },
      "message": "groupsio-webhook: fix emerg_dir check and Falcon 4 responses\n\nThe emergency-save guard was inverted: with no emerg_dir set it called\nos.path.isdir(None), which raises TypeError from inside the very error\npaths meant to preserve undeliverable messages, and with a missing\ndirectory it went on to fail in open(). Falcon 4 also removed\nResponse.body, so every request handler raised AttributeRemovedError.\n\nKey changes:\n\n- Skip emergency saves unless emerg_dir is set and is a directory\n- Set resp.text instead of the removed resp.body\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "ef00d0bfe0873c0866308549ae95fdeb222fe746",
      "tree": "d2ebb5b5a9b7e985cc3e8d4ad047ca860d7d92b9",
      "parents": [
        "19b2ad24fc056e1bf4fee1898d36381dd13c663d"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Oct 09 14:28:00 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Oct 09 14:28:00 2026 +0000"
      },
      "message": "git-archive-signer: strip \"$TARNAME-\" from tags too\n\nFor tags like kup-0.4.0, the archive prefix came out as kup-kup-0.4.0/,\nbecause only a leading \"v\" was stripped. sig-note-to-kup builds\nkup-0.4.0/ for repos with archive-signer.relregex \u003d name-version, so the\nsignature never matched, and without that setting releases were published\nas kup-kup-0.4.0.tar.gz. Strip \"$TARNAME-\" as well; tags like v1.2.3 give\nthe same prefix as before.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "19b2ad24fc056e1bf4fee1898d36381dd13c663d",
      "tree": "6d4e10cba61d86348e67f8351b3b049fb607f72c",
      "parents": [
        "b5ffded0e60c396312aef1a2627d8b5e70830adb"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 23 06:54:13 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 23 06:54:13 2026 +0000"
      },
      "message": "get-verified-shallow-tar: resolve TARGETDIR to an absolute path\n\nWe chdir into the work directory to check the tarball against the\nchecksums file, but the path to that file was built from TARGETDIR,\nwhich defaults to \".\". Running the script anywhere other than from an\nabsolute TARGETDIR therefore failed every time, right after spending\nseveral minutes downloading the tarball:\n\n  sha256sum: ./shallow-tar-verify.XXXXXXXXX.untrusted/sha256sums.check:\n             No such file or directory\n  FAILED to verify the downloaded tarball checksum\n\nResolve TARGETDIR once, immediately after checking that it exists, so\neverything derived from it is absolute.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "b5ffded0e60c396312aef1a2627d8b5e70830adb",
      "tree": "4f9eac981f5d7924445f9bbaabceca8b55692246",
      "parents": [
        "241d2dc621db428049b8ab73296c14c44273c9cf"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 23 06:47:00 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 23 06:48:08 2026 +0000"
      },
      "message": "Add get-verified-shallow-tar for CI build trees\n\nSet up a Linux build tree from the nightly shallow tarballs published\nunder kernel.org/pub/linux/kernel/shallow-tar/, in the spirit of\nget-verified-tarball. A CI system names the tree, the branch and the\ncommit it wants, and gets back a detached checkout at exactly that\ncommit, ready to build in.\n\nThe tarball is only ever a pre-warmed object cache. The autosigner\nsignature attests that these are the bytes kernel.org published and\nnothing more, so the requested commit is always fetched from\ngit.kernel.org and checked out by object ID, and the unpacked tree is\nreset to a trusted configuration before it is used.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "241d2dc621db428049b8ab73296c14c44273c9cf",
      "tree": "93bfec61f6099e6902fe595fb6ec71e6810d3888",
      "parents": [
        "6c37a3ecc30654da4c11cc9358c0232b20d098b2"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:36:20 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:36:20 2026 +0000"
      },
      "message": "dirsigner: log a summary when a run finishes\n\nThe log said \"Recursively scanning ...\" at the start and then went quiet,\nso there was no way to tell a finished run from one that died partway\nthrough or is still grinding away on a large tree.\n\nCount the directories walked, files hashed, checksum files written and\nlistings written, and log all of it on the way out along with the elapsed\ntime. The hashed count is left out when nothing was hashed, and the\nlisting count when -i was not given, so a quiet run stays a short line.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "6c37a3ecc30654da4c11cc9358c0232b20d098b2",
      "tree": "593df104b087df9e22fdd285ebcb151b6e392990",
      "parents": [
        "43f47a6e2047b5e1ac6e035366be2c586bc3150c"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:28:26 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:28:40 2026 +0000"
      },
      "message": "dirsigner: list files that -x kept out of the checksums\n\n-x has always meant \"do not checksum this\", which was the only thing it\ncould mean before we generated listings. Once index.html arrived it\nquietly acquired a second meaning, and files the server had always shown\ndisappeared from the page. On the real tree that hid every .sign file,\nwhich is exactly what somebody fetches alongside a tarball.\n\nKeep the two ideas apart. Our own bookkeeping files stay hidden and\nunchecksummed, since a listing that described itself would never settle.\nEverything -x names is still left out of the sums file, but it is a file\nthat is really there and the server listed it, so we list it too. With\n--version-sort the excluded names are merged in and sorted with the rest,\nso a .sign lands next to the tarball it signs; without it they are\nappended, because found_files is in filesystem order and there is nowhere\nsafe to slot them in.\n\nThis means -x no longer hides anything from a listing. Nothing needs it\nto: dotfiles were already hidden, and an excluded directory is still\ndropped, because we never walk into one and so have nothing to say about\nwhat is in it.\n\nKey changes:\n- Split the internal never-list patterns out of the caller\u0027s excludes\n- Collect excluded files and pass them to the index, not the sums file\n- Factor the repeated glob matching into matches_any()\n\nAssisted-by: LLM [analysis, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "43f47a6e2047b5e1ac6e035366be2c586bc3150c",
      "tree": "ff05d0cbb50a4590d87c41536a4cb200c0d0583e",
      "parents": [
        "87346df5ef0157e80908c2d754c9a684ac7db376"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:02:07 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:02:07 2026 +0000"
      },
      "message": "dirsigner: keep dotted subdirectories out of index.html\n\nHidden files were already left out of the listing, the way nginx\nautoindex left them out, but hidden directories were not. On the real\ntree that published build leftovers nobody should be browsing: .pc,\n.tmp_versions and a handful of .testing directories all showed up in\ntheir parents\u0027 listings.\n\nApply the same rule to subdirectories that already applied to files.\nThe walk still descends into them and still signs what is there, which\nmatches how autoindex behaved: hidden from the listing, but served if\nasked for by name.\n\nAssisted-by: LLM [analysis, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "87346df5ef0157e80908c2d754c9a684ac7db376",
      "tree": "b1324f6f4e11eab232d426c8ef347b5a3663d031",
      "parents": [
        "d98019f46cff493c36be0a32dbc18f2dcf624440"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:01:54 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 19:01:54 2026 +0000"
      },
      "message": "dirsigner: leave timestamps off directory entries in index.html\n\nA directory\u0027s mtime moves whenever anything is added to or removed from\nit, and that includes the index.html we write there ourselves. Listing\nthat timestamp meant writing a child\u0027s index.html made its parent\u0027s page\nstale, so one change rippled upwards and the tree needed as many runs to\nsettle as it was deep. Worse, an rsync pull that resets directory mtimes\nto match its source invalidated every listing in the tree at once.\n\nLeave the column blank for directories, padded to the same width so the\nsize column still lines up. This is a deliberate departure from nginx\nautoindex, which showed the stamp, and the fidelity test now says so\nexplicitly rather than being weakened. The stamp was of little use\nanyway: it said when the directory itself last changed, not when\nanything in it did.\n\nAssisted-by: LLM [analysis, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "d98019f46cff493c36be0a32dbc18f2dcf624440",
      "tree": "25f1083b91a1c73c0d88c22626d7ad4eb09621e9",
      "parents": [
        "4c5ede3c01efde29cd49af91bca57d31265fd865"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:33:52 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:33:52 2026 +0000"
      },
      "message": "dirsigner: mark our own listings and never touch anyone else\u0027s\n\nEnabling -i used to overwrite whatever index.html it found, so a page\nsomebody wrote by hand was destroyed on the first run. There was no way\nto tell the two apart after the fact either.\n\nPut a marker comment on the first line of every listing we generate and\nrefuse to write to an index.html that does not carry it. The marker holds\nno version or timestamp on purpose: it has to stay byte-stable, or every\nrun would find every listing different and rewrite the whole tree.\n\nA symlink is never ours, because we never create one, so it is now left\nalone rather than replaced. That drops the separate guard against writing\nthrough a link: the file is opened O_NOFOLLOW, which both refuses the link\nand closes the window where a regular file could be swapped for one\nbetween the check and the write.\n\nAlso give an empty directory a listing holding just the parent link,\nwhich is what autoindex served for one. Leaving none behind meant an\nempty directory turned into a 404 once autoindex is switched off.\n\nThe skip notice is logged at debug rather than info. It describes a\nstanding state of the tree instead of something we just did, so at info\nit would repeat on every run, forever.\n\nKey changes:\n- INDEX_MARKER on the first line of every generated listing\n- read_our_index() returns NOT_OURS for anything we did not write\n- empty directories get a bare listing instead of none\n- 17 new tests, including that the marker only counts on the first line\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "4c5ede3c01efde29cd49af91bca57d31265fd865",
      "tree": "455201d7b4682867aa45c98e244d7653420cdded",
      "parents": [
        "35d51cfe70ced7f1d6f953848f7a7fd126de85d5"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:23:42 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:24:45 2026 +0000"
      },
      "message": "dirsigner: use X | None instead of Optional\n\nThe script was written to the same conservative style as the rest of this\ncollection, which predates the union syntax. dirsigner runs on python-3.12,\nso it can assume the newer syntax and read a little better for it.\n\nNote the floor in a comment, so nobody lifts an idiom out of here into a\nscript that has to run somewhere older.\n\nVerified the suite passes under 3.12 and not just the newest interpreter.\n\nAssisted-by: LLM [codegen]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "35d51cfe70ced7f1d6f953848f7a7fd126de85d5",
      "tree": "301dc57558c809b0a1b77bf0b1de1549dee1dfbc",
      "parents": [
        "14ef26d785e7b188fab560770c541e5c95d3e9a1"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:22:16 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:22:16 2026 +0000"
      },
      "message": "dirsigner: stop stat\u0027ing paths we are about to open anyway\n\nEvery directory in the tree paid for a handful of stat calls that told us\nnothing the following open or unlink would not have. strace over one\nunchanged directory showed twelve syscalls against its own metadata:\n\n  newfstatat(\"pub/a/.dirsigner.status.js\")   \u003c- exists()\n  openat(\"pub/a/.dirsigner.status.js\")       \u003c- ...then opened regardless\n  newfstatat(\"pub/a/sha256sums.asc\")         \u003c- sign_tree\n  newfstatat(\"pub/a/sha256sums.asc\")         \u003c- write_index_file exists()\n  newfstatat(\"pub/a/sha256sums.asc\")         \u003c- stat_index_entry, next line\n  newfstatat(\"pub/a/index.html\")             \u003c- islink()\n  newfstatat(\"pub/a/index.html\")             \u003c- exists()\n  openat(\"pub/a/index.html\")                 \u003c- ...then opened regardless\n\nAsk the filesystem once. Opening and unlinking already report a missing\nfile through FileNotFoundError, and a single lstat answers both \"is it\nthere\" and \"is it a symlink\" for index.html. stat_index_entry returns\nNone for a file that is not there, so the exists() ahead of it was a\nthird look at the same path. That is twelve syscalls down to eight per\ndirectory, and the tree has a lot of directories.\n\nAlso raise the default chunk size from 8K, which needed 24k read() calls\nto hash a 200MB tarball, to 256K, which needs 762. The cron job already\npasses -b 131072, so this only fixes the default.\n\nVerified byte-identical output: every index.html, sums file and status\nfile matches what the previous commit produced for the same tree.\n\nKey changes:\n- try/except instead of exists() before open() and unlink()\n- one lstat instead of islink() plus exists() for index.html\n- drop the redundant exists() before stat_index_entry\n- default chunk size 8K -\u003e 256K\n- tests asserting an unchanged run writes nothing, rehashes nothing and\n  stays near one stat per file\n\nAssisted-by: LLM [analysis, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "14ef26d785e7b188fab560770c541e5c95d3e9a1",
      "tree": "933a7a52a9913b2fb8d272c43341fbdb61b52a7a",
      "parents": [
        "e969295b1f1527a17c7d83beffdef38c2f35bd48"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:17:43 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:17:43 2026 +0000"
      },
      "message": "dirsigner: cut the per-file work in the scanning hot path\n\nScanning an unchanged tree was doing about twice the work it needed to.\nstrace over a 400-file tree showed 822 stat calls where 420 suffice, and\nthe redundant one was literally the same syscall twice in a row:\n\n  newfstatat(AT_FDCWD, \"pub/a/f-1.txt\", {...}, AT_SYMLINK_NOFOLLOW) \u003d 0\n  newfstatat(AT_FDCWD, \"pub/a/f-1.txt\", {...}, AT_SYMLINK_NOFOLLOW) \u003d 0\n\nos.path.islink() is a stat, and we called os.lstat() on the same path\nimmediately afterwards. Take the one lstat and read S_ISLNK out of the\nst_mode we already have.\n\nWeeding stale entries out of the status was quadratic: it tested each\nentry against found_files, which is a list. Directories in the tree hold\nthousands of files, so build a set first. That is 97ms down to 0.6ms for\n4000 entries.\n\nversion_sort compared (name, version, tail) field by field, which is\nexactly what tuple comparison does, so get_version_info can be the sort\nkey directly. Sorting no longer reparses both names on every comparison,\nand the comparator goes away. Verified equivalent over all 1089 pairs of\na corpus of awkward names and 200 shuffled full sorts.\n\nFinally, the subdirectory loop skipped symlinks before it checked\nexcludes, so -x could never keep a symlinked directory out of the index.\nDrop the check: os.walk does not descend into a symlinked directory\nregardless, so it bought nothing but a stat per subdirectory.\n\nKey changes:\n- one lstat per file instead of two\n- set membership when weeding the status file\n- get_version_info as a sort key instead of a comparison function\n- excludes now apply to symlinked directories\n\nAssisted-by: LLM [analysis, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "e969295b1f1527a17c7d83beffdef38c2f35bd48",
      "tree": "c8c8bce6408538514724a59f2c7ea683a0047db5",
      "parents": [
        "2be866e67dcf35412f027b8169e5b771505a4ffd"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:10:35 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:10:35 2026 +0000"
      },
      "message": "dirsigner: bump the version to 0.2\n\nMark the python3 port, the generated index.html listings and the symlink\nhandling as a new release.\n\nAssisted-by: LLM [codegen]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "2be866e67dcf35412f027b8169e5b771505a4ffd",
      "tree": "cc8ff4043e29b9e42e4a0a75f8ff49432abbed73",
      "parents": [
        "bcf3aba15386eaa0364d98bfdaf7ae1ec8a4dafb"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:10:11 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:10:11 2026 +0000"
      },
      "message": "dirsigner: update the copyright range to 2014-2026\n\nThe notice still named only 2014, the year of the original script, even\nthough it has been edited since. Widen it to cover the port and the work\nthat followed.\n\nAssisted-by: LLM [codegen]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "bcf3aba15386eaa0364d98bfdaf7ae1ec8a4dafb",
      "tree": "9c2a72fab778dfe0f7a2f53b6565d7b925d366e3",
      "parents": [
        "561145a7351ba92bb53903a49431566c8ec17c06"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:08:37 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 17:08:37 2026 +0000"
      },
      "message": "dirsigner: list symlinks and never write through one\n\nSymlinks were left out of the generated listings entirely, because\nsign_tree() skipped them before it could collect them. The web server\u0027s\nautoindex did list them, so dropping them changed what visitors see.\n\nCollect symlinks into their own list and append them to the index after\nthe checksummed files, following the link the way autoindex did: a link\nshows its target\u0027s size and mtime, and a link to a directory is listed\nwith a trailing slash and no size. A dangling link falls back to lstat on\nthe link itself. Symlinks are still never checksummed.\n\nWhile here, refuse to write index.html through a symlink. Writing to a\nstray index.html link would have followed it and clobbered whatever it\npointed at, possibly outside the tree. Unlink it and write a real file\ninstead.\n\nKey changes:\n- move the excludes check ahead of the symlink check, so excludes now\n  apply to symlinks too\n- add stat_index_entry(), the single place that looks at disk for\n  subdirs, symlinks and the sums file\n- replace a symlinked index.html instead of writing through it\n- 15 new tests covering symlink listing, ordering and the clobber case\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "561145a7351ba92bb53903a49431566c8ec17c06",
      "tree": "b0af4248700b3b7befb212670c6b08a7cc8f7900",
      "parents": [
        "4aec776f7b2ee588fd15c7ba7edef80372f7ebba"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 16:38:40 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 16:38:40 2026 +0000"
      },
      "message": "dirsigner: optionally write index.html directory listings\n\nAdd -i/--index, off by default, to write an index.html in every directory\nso the web server no longer has to build listings itself. Generating them\nhere is much cheaper: sizes and mtimes come out of the status we already\nkeep, so a listing costs no stat calls beyond the subdirectories and the\nsums file.\n\nThe output is byte-for-byte what nginx\u0027s autoindex module served, checked\nagainst live kernel.org listings: CRLF endings, a 50-column name field,\nthe size right-aligned in 8 with nginx\u0027s own rounding to K, M and G, and\nGMT timestamps. Long names are shown truncated but always link in full,\nand dotfiles stay hidden.\n\nindex.html is written after the sums file, so it can never be part of it;\nenabling the feature adds it to the exclude list, and any index.html a\nprevious run happened to sign drops out on the next pass. With the\nfeature off nothing changes at all.\n\nKey changes:\n- ordering moves from write_sums_file() into sign_tree(), so the listing\n  and the sums file always agree on the order of files. Without -s that\n  is still whatever order the filesystem gave us: sorting by default\n  would rewrite and re-sign every sums file in the tree\n- subdirectories sort by name, which is what autoindex did. version_sort\n  is no help there, since it reads everything without a name-version\n  filename as version 0\n- the listing is written outside the \"did anything change\" gate, so\n  directories holding nothing but subdirectories get one too. An\n  unchanged listing is not rewritten, which keeps mtimes stable\n- the sums file is listed even though it is excluded from itself, since\n  it was visible in the old server-generated listings\n- -u/--url-prefix sets the path shown in the title, for trees that are\n  not served from the root\n\nAssisted-by: LLM [plan, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "4aec776f7b2ee588fd15c7ba7edef80372f7ebba",
      "tree": "39295d7b4b93afdcb021a4f957031e4a6521d65f",
      "parents": [
        "0e56948d572c35f6c46bc483a4a5507dcaad0da4"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 15:20:13 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 16:25:26 2026 +0000"
      },
      "message": "tests: add a test suite for dirsigner\n\nCover the filename version parsing and comparator, the status file\nround-trip, the chunked hasher, sums file writing and the tree walk\nitself, including that an unchanged tree is neither rehashed nor\nre-signed and that removed files drop out of both the sums file and the\nstatus file.\n\ndirsigner imports the gpgme bindings at module level, which are distro\npackages rather than something installable from PyPI, so the tests stub\nthe module when it is missing and always replace it with a recording\nfake. No test touches a real keyring.\n\nAlso annotate conftest.load_script() and raise a clear ImportError when a\nscript has no import spec, so the tests directory passes mypy too.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "0e56948d572c35f6c46bc483a4a5507dcaad0da4",
      "tree": "a032aa9adf8978f655da568690af8d89f0655879",
      "parents": [
        "2c48d95ae508374105e6d6f1d789da58b3ef325e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 15:20:03 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 16:25:26 2026 +0000"
      },
      "message": "dirsigner: add type annotations and satisfy ruff and mypy\n\nAnnotate every function so the script passes `mypy --strict`, which the\nrepository requires of new code, and sort the imports for ruff.\n\nA FileStatus TypedDict now describes what we remember about each file\nbetween runs. Reading the stat fields by name instead of by tuple index\nis both clearer and typed correctly; the values stored are unchanged, so\nexisting .dirsigner.status.js files stay valid and nothing gets rehashed.\n\nsign_tree() now loads the status file once when it enters a directory\nrather than lazily on first use, which drops two \"is it loaded yet\"\nbranches and lets the type checker see that it is never None.\n\nsave_status() no longer writes an empty stub file when it is called with\nan empty status and no status file exists. That path is unreachable from\nsign_tree(), but leaving a stub behind was never the intent.\n\nThe gpgme bindings ship no type information and have no stubs on PyPI, so\npyproject.toml gains an ignore_missing_imports override for gpg.*.\n\nAssisted-by: LLM [codegen, review]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "2c48d95ae508374105e6d6f1d789da58b3ef325e",
      "tree": "0d0dbbd7b9e6cef648237f85af7971f588050f59",
      "parents": [
        "655c234d0ed002710e3681411a768b0fe292267b"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 15:11:19 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 24 16:25:25 2026 +0000"
      },
      "message": "Import dirsigner from its standalone repository\n\nBring dirsigner.py into the helpers collection so it stops living in its\nown single-file project. The version imported here is the vendored copy\nfrom the syncmaster ansible role, which is newer than the standalone\nrepository and carries the whole python3 port.\n\nKey changes:\n- argparse instead of optparse, with -t/--tree marked required\n- json instead of the abandoned anyjson\n- the gpg (gpgme 1.x) bindings instead of the old gpgme module\n- packaging.version instead of the removed distutils LooseVersion, via a\n  _safe_version() helper that falls back to version 0 on unparseable\n  strings\n- functools.cmp_to_key() for the version sort, os.walk() without the\n  python2 unicode() call, and a list() copy when pruning stale status\n  entries\n- import block sorted to satisfy the repository ruff configuration\n\nAssisted-by: LLM [analysis, review, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "655c234d0ed002710e3681411a768b0fe292267b",
      "tree": "aae63220d205f3b6086384c9b265ad72634701f4",
      "parents": [
        "a1a0f276c06210eebf504d26b7a99aa039c7337c"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Aug 19 14:29:18 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Aug 19 14:29:18 2026 +0000"
      },
      "message": "git-patchwork-bot: drop the series total from the housekeeping summary\n\nUnlike Patch, patchwork\u0027s Series model has no archived (or similar\nactive-state) field to filter on -- confirmed live that archived\u003dfalse\nand completed\u003dfalse are both silently ignored on the series list\nendpoint. So the \"of N series\" total was just the all-time count with\nno honest way to make it mean \"active\", the same problem the patch\ntotal had. Drop it rather than report a number that reads as current\nbut isn\u0027t.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "a1a0f276c06210eebf504d26b7a99aa039c7337c",
      "tree": "6c31fc0ee941303f327225380518129e32207bbe",
      "parents": [
        "273ea41bc460128d6f174e492647ab2b2245db07"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Aug 19 14:24:24 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Aug 19 14:24:24 2026 +0000"
      },
      "message": "git-patchwork-bot: report active patch count, not the all-time total\n\nThe patch count query had no archived filter, so it reported every\npatch ever submitted to the project instead of what\u0027s actually still\nactive. Filter on archived\u003dfalse and say \"active patches\" so the number\nmeans something to a maintainer skimming the report.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "273ea41bc460128d6f174e492647ab2b2245db07",
      "tree": "b9c4ed88dc93aab2433c021c1df3e177923fa7e6",
      "parents": [
        "f82e8928ed5c88ef673c1f7f19e4ecdc28e84c9b"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 15:55:31 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 15:55:31 2026 +0000"
      },
      "message": "git-patchwork-bot: fold series total into the housekeeping timing line\n\nCombine the checked-count and project series total into one line\ninstead of repeating the series count twice across two lines.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "f82e8928ed5c88ef673c1f7f19e4ecdc28e84c9b",
      "tree": "cd4275ca5e7cdab56ce93448606a7b97594c58bb",
      "parents": [
        "45e8a3e7317c4ddf5c2c437ca73e70a804c5dfa4"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 15:53:22 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 15:53:22 2026 +0000"
      },
      "message": "git-patchwork-bot: move housekeeping summary to bottom, report timing\n\nMove the series-checked/project-totals summary from the top of the\nmaintainer email to the bottom, above the signature, so the actual\nhousekeeping detail leads the message. Also swap \"for supersession\" for\nhow long the autosupersede pass actually took, which is more useful to a\nmaintainer skimming the report than a mostly-fixed phrase.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "45e8a3e7317c4ddf5c2c437ca73e70a804c5dfa4",
      "tree": "d3072d33cc0e91f42f56a1a81b8d24d4f7902ca4",
      "parents": [
        "7759d6e9ece29b65a14d6b7cb885aee039673070"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 15:48:16 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 15:48:16 2026 +0000"
      },
      "message": "git-patchwork-bot: add series/patch/series totals to housekeeping summary\n\nGive maintainers more context in the housekeeping email: how many series\nwere checked for supersession during this run, and how many patches and\nseries the project currently holds overall.\n\nThe per-run count was already computed locally (the size of the series\ndict built from get_project_series_since()), so surfacing it is free.\nThe project-wide totals are new: patchwork\u0027s list endpoints return a\nGitHub-style Link header, and a per_page\u003d1 request makes the rel\u003d\"last\"\npage number equal to the exact total item count. This is one lightweight\nrequest per count instead of paging through the whole patch/series\nhistory, which matters on a server that can already be struggling.\n\nKey changes:\n- Add Restmaker.get_list_count(), which reads the total off the Link\n  header (or falls back to the body length when there is no header).\n- Track hk_stats[\u0027series_checked\u0027] and fold it into the aggregate\n  housekeeping run log line alongside superseded/archived counts.\n- Prepend a short summary (series checked, project totals) to the\n  maintainer report when one is actually sent.\n- Bump BOT_VERSION.\n\nAssisted-by: LLM [codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "7759d6e9ece29b65a14d6b7cb885aee039673070",
      "tree": "b9e2acdc2d99225ac5f8a7f8df7da5ecec4638b4",
      "parents": [
        "804157c0c0de88cbeb096717d0e96e43130765e6"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 14:00:39 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 18 14:00:39 2026 +0000"
      },
      "message": "git-patchwork-bot: log run start/finish with bot version and stats\n\nThe circuit breaker and pending-ops ledger are silent by design when\nnothing goes wrong, which made it hard to tell \"no failures happened\"\napart from \"the deployed script never actually ran\" or \"it died before\nreaching the interesting code.\" Add an explicit bot version and\nbracket every run with start/finish log lines carrying it, plus a\none-line summary of what happened.\n\nBOT_VERSION is a YYYYMMDDNN string, bumped by hand for each release, so\na support conversation can pin down exactly which behavior a given log\nline came from without needing to correlate against git history.\n\nKey changes:\n- BOT_VERSION constant (\u00272026081801\u0027)\n- pwrun() now returns a stats dict (patches updated, refs held back,\n  pending ops recorded/cleared) instead of None; check_repos()\n  aggregates it across repos into a \"finished regular run: ...\" line\n- housekeeping() now returns a stats dict (series superseded, patches\n  archived); the housekeeping dispatch in __main__ aggregates it into\n  a \"finished housekeeping run: ...\" line\n- Both entry points log a \"starting ... run (bot version ...)\" line\n\nAssisted-by: LLM [plan, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "804157c0c0de88cbeb096717d0e96e43130765e6",
      "tree": "f42294e251282537dc500c21840cc8e12c56ceee",
      "parents": [
        "2e42344887b60dc0f09f986051bfee4dfeee14ad"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 17 21:56:30 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 17 21:56:30 2026 +0000"
      },
      "message": "git-patchwork-bot: add a per-rev pending-ops ledger for isolated failures\n\nThe circuit breaker (previous commit) handles systemic outages by holding\nback whole refs, but that is too coarse for the common case of a single\nrev hitting a transient, skippable error while the server is otherwise\nhealthy. Add a fine-grained complement: a pending_ops table that records\nthe rev, its cached match data, and the stage/note of the failure, so the\nnext run retries just that rev instead of relying on the whole ref being\nre-diffed.\n\nPending ops are merged back into rpwhashes for their project on every\nrun (even one with no fresh git activity, which previously returned\nearly before any retry could happen), and the match/resolve/apply\ncall sites now record-or-clear a rev\u0027s ledger row once the ref\u0027s\nprocessing loop completes without being interrupted by an open circuit.\nA breaker trip mid-ref skips the reconciliation entirely, since Tier 2\nalready owns holding that ref back.\n\nKey changes:\n- Bump DB_VERSION to \u00272\u0027 and add db_ensure_pending_ops()/\n  db_record_pending_op()/db_clear_pending_op()/db_get_pending_ops()\n- Merge ledgered pending ops into rpwhashes per project, and no longer\n  bail out of pwrun() early when there are no fresh revs but pending\n  ops exist\n- Track per-rev failure stage/note through the match, resolve, and\n  apply call sites, reconciling the ledger once a ref\u0027s try block\n  completes uninterrupted\n\nAssisted-by: LLM [plan, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "2e42344887b60dc0f09f986051bfee4dfeee14ad",
      "tree": "3bdddeaf745c0b82952228422a540521a57786c8",
      "parents": [
        "50d9e431fc2bbc0226686608a3837873d4a99753"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 17 21:41:56 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 17 21:41:56 2026 +0000"
      },
      "message": "git-patchwork-bot: add a per-server circuit breaker for pwrun()\n\nTreating every 5xx as skippable fixed the wedge from series 1109538, but\nopened a worse hole: a patchwork outage returning 5xx for everything would\nlook identical to a clean run that found nothing to do, since heads still\nadvance unconditionally at the end of pwrun(). A plain consecutive-failure\ncounter isn\u0027t enough either -- an intermittent pattern like \"a few failures,\none success, repeat\" never crosses a fixed threshold, so it leaks through.\n\nGive each Restmaker a small circuit breaker: three consecutive failures that\nsay something about the server (5xx, or no response at all) rather than\nabout the specific object (404 stays neutral) opens the breaker, after which\nevery further call to that server is refused locally, without a network\nround trip, for the rest of the process. Because _server_cache hands out one\nRestmaker per server URL, this stops load against a struggling server across\nevery repo and project that talks to it in the same run.\n\nTripping the breaker must not turn into ledgering every revision left in the\nbatch one by one -- commits often land in batches of hundreds when a\nmaintainer merges and pushes in bulk, and retrying each such row\nindividually next run would just recreate the same overload. So pwrun()\ninstead tracks which refs finished cleanly: any ref interrupted by the\nbreaker, or never reached because it was already open, keeps its old head\nin the database. The next run\u0027s ordinary git diff picks the whole gap back\nup on its own, with no per-commit bookkeeping needed.\n\nKey changes:\n- CircuitOpenError, plus consecutive_failures/circuit_open state and a\n  CIRCUIT_BREAKER_THRESHOLD on Restmaker, wired into get_cover(),\n  get_patch(), get_series(), and update_patch()\n- update_patch() now raises PatchworkFetchError instead of a bare\n  untyped RuntimeError, so its failures follow the same skip/reraise\n  contract as the other getters\n- pwrun() catches CircuitOpenError per ref, and saves the pre-run head\n  for any ref that didn\u0027t finish cleanly instead of the new one\n\nAssisted-by: LLM [analysis, plan, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "50d9e431fc2bbc0226686608a3837873d4a99753",
      "tree": "5b036431f06310f5c2d00032c47a4ce8ebcb86d1",
      "parents": [
        "febeffbc5983d7734f552d12bc027bb923eaa721"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Aug 14 03:13:38 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Aug 14 03:13:38 2026 +0000"
      },
      "message": "git-patchwork-bot: do not let a single unavailable object wedge the run\n\nPatchwork returns HTTP 500 for an individual series it cannot serialize,\ne.g. series/1109538/ on patchwork.kernel.org. Restmaker.get_series()\nturned that into a KeyError, and the only caller, in pwrun(), did not\ncatch it. The exception unwound out of pwrun() and killed the process.\n\nThat is worse than losing one series. pwrun() saves the new repo heads\nonly after it finishes the project loop, so an escaping exception leaves\nthe heads where they were: the next cron run re-walks the same\nrevisions, matches the same patch, fetches the same series, and dies in\nexactly the same place. The bot stays wedged until someone intervenes.\ncheck_repos() has no handler either, so every repo after this one in the\nconfig is skipped as well.\n\nThe same hole existed in every other single-object fetch. get_patch()\nalso raised, while its callers tested \"if not pdata\" and \"if pdata is\nNone\", clearly written expecting None rather than an exception.\n\nIntroduce PatchworkFetchError, which subclasses KeyError so callers\nwritten against the old getters keep working, and which carries the HTTP\nstatus. Its \"skippable\" property draws the same line commit 6d2034e drew\nfor the series-list endpoint: 404 (the object is gone) and 5xx\n(patchwork cannot serialize it) are properties of that one object and do\nnot resolve on a retry, so the run should step over it. Anything else --\nauth failures, throttling, redirects, and connection errors, which carry\nno status at all -- says something about the API or the network instead,\nand must keep propagating. Blanket-swallowing would be its own failure\nmode: if the API starts refusing every request, skipping each object in\nturn would look like a clean run that simply found nothing to do.\n\nKey changes:\n- Add PatchworkFetchError and reraise_unless_skippable(); raise it from\n  get_cover(), get_patch() and get_series() with the HTTP status\n- Guard all eight call sites; pwhash_differ() reports and exits cleanly\n  rather than dumping a traceback, since fetching that one patch is the\n  whole job and there is nothing to skip past\n- get_patch() no longer caches a miss for non-skippable errors, so a\n  transient connection failure does not poison the cache for the run\n- Fold the status extraction in get_list_page() into a shared helper\n- Add TestPatchworkFetchError, TestReraiseUnlessSkippable,\n  TestRestmakerFetchErrors and TestPwrunSurvivesUnavailableObjects; the\n  latter drives the real pwrun() against a real sqlite pw.db and asserts\n  on the heads table, since whether the heads advanced is the wedge\n\nAssisted-by: LLM [analysis, codegen, tests]\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "febeffbc5983d7734f552d12bc027bb923eaa721",
      "tree": "aaa4749b15ff4e1f8c67c93762a2bfecdb103ecb",
      "parents": [
        "7b11b54722684adca52809ba02e1cfccaff6e3f0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jun 30 20:22:02 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jun 30 20:22:02 2026 +0000"
      },
      "message": "git-patchwork-bot: fall back when upstream commit is absent from the tree\n\nThe upstream exclusion resolves the mainline HEAD against the upstream\nrepo (torvalds/linux.git), but the resulting \"git log ... ^\u003csha\u003e\" runs in\nthe subsystem repo. The two only share the object reliably when they\nshare an object store, e.g. via grokmirror alternates that point both\nrepos at one objstore. In that common topology the object is always\npresent (grokmirror lands objects in the objstore before advancing any\nrefs), so exclusion works.\n\nWhen the repos do NOT share an object store, the upstream SHA is absent\nfrom the subsystem repo, and \"git log ^\u003csha\u003e\" fails with \"bad object\".\ngit_get_command_lines() turns that failure into an empty result, which\ngit_get_new_revs() reads as \"no new revs\" and silently skips every ref\nfor the project -- a silent, total failure that is worse than the\novercounting it was meant to fix.\n\nGuard against this: after resolving the upstream commit, verify the\nobject is actually present in the repo we are about to query (git\nrev-parse --verify --quiet \u003csha\u003e^{commit}). If it is not, log a notice\nand proceed without exclusion, falling back to the previous behavior\nrather than dropping everything.\n\nKey changes:\n- git_get_new_revs(): verify upstream_commit is present in the target\n  repo before appending ^sha; clear it and warn if absent\n- Add test for the absent-object fallback path\n- Adjust the caching test to count only upstream-repo resolution calls,\n  since the per-repo presence check also goes through git_run_command\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "7b11b54722684adca52809ba02e1cfccaff6e3f0",
      "tree": "52ff3812628c6a7a607973ef74bf4bc1badab5f2",
      "parents": [
        "6d2034e5a2d909849dcf0b39ac8fcd14a3a4b53d"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jun 30 20:15:15 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jun 30 20:15:15 2026 +0000"
      },
      "message": "git-patchwork-bot: skip commits already present in upstream (mainline)\n\nAfter a merge window closes, subsystem maintainers rebase their branches\non the new mainline. The bot\u0027s saved HEAD for the branch is not an\nancestor of the new tip, so git log old..new hands back all of mainline\u0027s\nnew commits as well as the genuine new patches -- easily 17000+ revisions\nthat are not in patchwork and never will be.\n\nIntroduce an optional \"upstream\" setting per repo entry in the config.\nWhen set, the value is a path (relative to reposdir) and an optional ref\nname (default: master) pointing to the authoritative upstream tree.\ngit_get_new_revs() resolves the upstream HEAD with git rev-parse and\nappends ^\u003csha\u003e to the git log invocation. Because the rebased branch\ncontains mainline\u0027s commits as direct ancestors, git log excludes them\nautomatically, leaving only the patches that are genuinely new to the\nbranch.\n\nThe upstream HEAD is cached in _upstream_commit_cache on the first\nsuccessful rev-parse, so all repos that share the same upstream (e.g.,\nevery Linux kernel subsystem tree pointing at torvalds/linux.git) pay\nthe subprocess cost only once per cron run.\n\nMissing upstream repos and unresolvable refs are handled gracefully: the\nbot logs a warning and falls back to the original (unconstrained) range\nrather than aborting.\n\nKey changes:\n- git_get_new_revs(): add upstream_gitdir/upstream_ref params; resolve\n  and cache upstream HEAD; append ^sha to git log args when available\n- pwrun(): parse \"upstream\" from repo settings and pass to\n  git_get_new_revs()\n- Add _upstream_commit_cache module-level dict\n- Add four new tests covering exclusion, graceful degradation, and\n  single-resolve caching\n\nAssisted-by: claude-sonnet-4-6\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "6d2034e5a2d909849dcf0b39ac8fcd14a3a4b53d",
      "tree": "6c06836a7464c1010f57490b36a96ab60bd7702c",
      "parents": [
        "5f8449d333db57649a827bb6daa933a9a02f8bcb"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jun 30 16:58:20 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jun 30 16:58:20 2026 +0000"
      },
      "message": "git-patchwork-bot: do not treat HTTP 404 as an unserializable series\n\nThe resilient autosupersede pagination in get_project_series_since() only\nrecognized two stop conditions: a short 200 page in the main loop, and a\n200 response with an empty list in the per-series isolation loop. But\npatchwork signals \"no such page / past the end of results\" with HTTP 404,\nnever with an empty 200 (get_unpaginated() already relies on this). So\nonce pagination ran off the end via a 404 -- which happens when the result\ncount is an exact multiple of REST_PER_PAGE, or when the isolation loop\nwalks past the last series -- the 404 was misread as \"this page has an\nunserializable series.\" That dropped into the per_page\u003d1 isolation loop,\nwhere every request was also past the end and returned 404, so reached_end\n(set only on the impossible 200+empty) never fired and the loop ran away,\nincrementing page forever while logging \"Skipping unserializable series at\nindex ###\" with ever-growing indices.\n\nHandle 404 as the end of results in both the main loop and the isolation\nloop, and only enter isolation for an actual 5xx (the genuine\nserialization failure); any other unexpected status now stops cleanly\ninstead of hammering the API one series at a time.\n\nKey changes:\n- get_project_series_since(): 404 in the main loop breaks (end of results)\n- isolation loop: 404 sets reached_end; only 5xx logs/skips a bad series\n- non-5xx, non-404 statuses stop rather than bisect\n- tests: model end-of-results as 404 (not empty 200) and add regression\n  tests asserting no runaway bisect on 404 / unexpected status\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "5f8449d333db57649a827bb6daa933a9a02f8bcb",
      "tree": "36e4d725ba7b18d7a92e4376a9a51cb721663184",
      "parents": [
        "bcffe78ee2b047b39c9431feec0fa7237c245a02"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 20:47:18 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 20:47:18 2026 +0000"
      },
      "message": "pr-tracker-bot: add type annotations and clean up lint\n\nAnnotate the entire module so it passes \u0027mypy --strict\u0027, and tidy a\nfew things the linters flagged. No behavior change is intended; the\nexisting test suite continues to pass.\n\nKey changes:\n- Add type annotations to every function and the module-level helpers\n- Sort imports and drop trailing whitespace to satisfy ruff\n- Guard email.utils.parsedate() results before passing them to\n  time.strftime() (parsedate can return None on unparseable dates)\n- Only decode text/plain payloads that are actually bytes\n- Rename a couple of shadowed locals (git_run_command output buffer,\n  the file log handler) so their types stay consistent\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "bcffe78ee2b047b39c9431feec0fa7237c245a02",
      "tree": "7aea3650a245aa6e38b71b4346d20090b1bd91b2",
      "parents": [
        "d19f44de2deff84474b52ae608faa0160c437d26"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 20:40:50 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 20:40:50 2026 +0000"
      },
      "message": "pr-tracker-bot: ignore pull requests with identical base and tip\n\nA malformed pull request can list the same commit-id on both the\n\"changes since commit\" (base) and \"for you to fetch changes up to\"\n(tip) lines. Such a request is empty -- there is nothing to actually\npull -- and the tip is simply whatever commit the sender happened to\nbe based on, which is frequently already in the maintainer\u0027s tree.\n\nWhen that happens, pr-tracker-bot would parse the tip commit-id,\nlater find it already merged via \u0027git branch --contains\u0027, and send a\nspurious \"has been merged\" notification (pointing at an unrelated\nmerge commit). This was reported by Linus and Andrew Morton:\nhttps://lore.kernel.org/all/CAHk-\u003dwhn39dQtoMeJrz++nAnZpgnrooU0dgzOZXdn1LyxQ+f2w@mail.gmail.com/\n\nDetect this case in record_pr_data() by also parsing the base\ncommit-id and refusing to track the request when it matches the tip.\nThe comparison is abbreviation-tolerant in case the two lines use\ndifferent hash lengths.\n\nKey changes:\n- Add PULL_BODY_SINCE_COMMIT_ID_RE to capture the base commit-id\n- Add commit_ids_match() helper for abbreviation-tolerant comparison\n- Skip recording when base and tip resolve to the same commit\n- Add tests/test_pr_tracker_bot.py covering the new behavior\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "d19f44de2deff84474b52ae608faa0160c437d26",
      "tree": "7ae01618cfa7ec716a34c483a641fe047e41290e",
      "parents": [
        "049d4ea92ce895823f8b001e794e46cb6b50466f"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:58:02 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:58:02 2026 +0000"
      },
      "message": "git-patchwork-bot: make autosupersede resilient to unserializable series\n\nPatchwork returns HTTP 500 for an entire series-list page if it cannot\nserialize even one series in it (e.g. a patch whose data breaks URL\nbuilding during embedded serialization). The autosupersede query fetched\nseries newest-first with per_page\u003d100, so such a series landed on the\nfirst page: get_series_list() swallowed the 500, returned an empty list,\nand the loop treated that as \"no series\", so autosupersede silently did\nnothing for the whole project. This was happening to the git project,\nwhere one series in the recent window cannot be serialized at any page\nsize.\n\nAdd Restmaker.get_list_page(), which exposes the HTTP status instead of\nhiding it, and get_project_series_since(), which bounds the query with a\nserver-side \"since\" filter and pages through results by id. When a page\nfails, it re-fetches that page one series at a time so a single\nunserializable series is skipped instead of blocking the rest. Rewrite\nthe autosupersede collection loop to use it.\n\nThis also drops the manual descending pagination and a\nstrptime(\"%Y-%m-%dT%H:%M:%S\") call that would raise on series dates\ncarrying microseconds, and fixes a latent bug where the stored series\n\u0027id\u0027 was the builtin function rather than the series id.\n\nVerified against patchwork.kernel.org on Python 3.9: the git project\u0027s\nautosupersede now skips the one bad series and processes the other 254 in\nthe window. Add TestGetListPage and TestGetProjectSeriesSince.\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "049d4ea92ce895823f8b001e794e46cb6b50466f",
      "tree": "5f1eb5a2021682fa121fac1a7d9abc3d3e9f69b1",
      "parents": [
        "5878685303d7c5733b52c2d0c3896471d0ae14db"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:36:16 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:36:16 2026 +0000"
      },
      "message": "git-patchwork-bot: match project link_name exactly, not by prefix\n\nproject_by_name() resolved a configured project to the first API project\nwhose link_name *started with* the configured name. Whenever one project\nname is a prefix of another, this matched the wrong project: \"linux-mm\"\nresolved to \"linux-mmc\" (which the API returns first), so every\nhousekeeping run for linux-mm actually operated on linux-mmc. Because\nlinux-mmc\u0027s old patches are already archived under its own config, the\nquery returned nothing and linux-mm was never touched -- silently, with\nno error.\n\nPrefer an exact (case-insensitive) link_name match, and fall back to the\nold startswith behaviour only when nothing matches exactly, so any\nconfiguration relying on the looser match keeps working.\n\nVerified against patchwork.kernel.org: linux-mm now resolves to its own\nproject id and the autoarchive query returns its backlog as expected.\n\nAdd TestProjectByName covering exact-match-over-prefix, the longer name\nresolving to itself, case-insensitivity, and the prefix fallback.\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "5878685303d7c5733b52c2d0c3896471d0ae14db",
      "tree": "a750a296fdd88458a1cdba4f738c8d01594accc2",
      "parents": [
        "5f730d3f1a49bf70fd25b44945c064f98b2cef48"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:15:54 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:15:54 2026 +0000"
      },
      "message": "Add uv.lock to .gitignore\n\nRunning the test suite with \"uv run\" generates a uv.lock at the repo\nroot. The project doesn\u0027t track dependencies through uv, so this file\nis a transient artifact rather than something we want under version\ncontrol.\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "5f730d3f1a49bf70fd25b44945c064f98b2cef48",
      "tree": "f1f1041ca2c7d202055ef7e2d3278843b823f47a",
      "parents": [
        "3cd422ea7cf306572abea2b9e5f56548a474c37a"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:13:35 2026 +0000"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 18 19:13:35 2026 +0000"
      },
      "message": "git-patchwork-bot: fix autoarchive date filtering via patchwork \"before\"\n\nThe autoarchive housekeeping loop parsed each patch\u0027s date locally with\nstrptime(\"%Y-%m-%dT%H:%M:%S\") to decide whether it was old enough to\narchive. Any patch whose date carried microseconds (e.g. a timestamp\nending in \".123456\") raised an unhandled ValueError, aborting the\nhousekeeping run for that project and every project after it in the\nloop. This left large backlogs (e.g. linux-mm) entirely unarchived.\n\nOffload the date filtering to patchwork instead, using the server-side\n\"before\" filter (lookup_expr\u003dlt on the patch date), which has been part\nof the REST API since well before our pinned v1.2. Every patch the\nserver returns is now already old enough to archive, so we no longer\nparse dates on our side and the \"too recent\" early-exit (lastpage) is\ngone. The state\u003dnew restriction is intentionally preserved: we only\narchive patches that have seen no action.\n\nAdd a TestAutoArchive suite covering the new query parameters, the\nmicrosecond-timestamp regression, real-run refetch pagination, the\nseen-set infinite-loop guard, and dryrun pagination.\n\nAssisted-by: claude-opus-4-8\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "3cd422ea7cf306572abea2b9e5f56548a474c37a",
      "tree": "f0f489957364e09a08e5816753e181e642da9299",
      "parents": [
        "b284d87cc92c668c31c7e2817ac1e1fb54ce0de0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 11:19:26 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 11:19:26 2026 -0400"
      },
      "message": "git-patchwork-bot: add ruff/mypy config and fix lint issues\n\nAdd ruff and mypy strict configuration to pyproject.toml and fix\nall reported issues:\n\n- Sort imports per isort conventions (ruff I001)\n- Wrap long regex line to stay within 120 char limit (ruff E501)\n- Remove six stale type: ignore[assignment] comments that mypy\n  strict mode no longer needs (unused-ignore)\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "b284d87cc92c668c31c7e2817ac1e1fb54ce0de0",
      "tree": "d8fd15b5cf6add57c247fe2696d9dc9306200cf6",
      "parents": [
        "0ea22312bbdbd51dae7ce558f7f53577bc1c59b3"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 11:10:44 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 11:10:44 2026 -0400"
      },
      "message": "git-patchwork-bot: add tests for utilities, git wrappers, and database\n\nExpand the test suite with coverage for:\n\n- listify() and get_tweaks() pure utility functions\n- git_get_new_revs() log parsing with mocked git output, including\n  committer filtering, deleted refs, and colons in subjects\n- send_summary() email construction with mocked globals and SMTP\n- git_get_repo_heads(), git_get_command_lines(), git_get_patch_id(),\n  and git_get_rev_info() with mocked git commands\n- SQLite database functions using in-memory databases: table init,\n  DB_VERSION, and heads save/get roundtrip\n\nTotal test count: 90\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "0ea22312bbdbd51dae7ce558f7f53577bc1c59b3",
      "tree": "cb00b0eb56db66e35dbb6429a61ebd4719254153",
      "parents": [
        "9f1bb6fbcadf99b980750ed16d397cc064f5dde3"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 11:05:18 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 11:05:18 2026 -0400"
      },
      "message": "git-patchwork-bot: add mocked API and hash tests\n\nAdd tests for the patchwork API interaction functions using\nunittest.mock to avoid needing a live patchwork instance:\n\n- get_patchwork_pull_requests_by_project: pull URL parsing, missing\n  refnames, null pull_url entries, query parameter validation\n- get_patchwork_patches_by_project_hash: hash lookups\n- get_patchwork_patches_by_project_msgid: message-id lookups\n- get_patchwork_hash: diff hashing including line number\n  normalization, -p1 prefix handling, preamble stripping, and\n  CRLF normalization\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "9f1bb6fbcadf99b980750ed16d397cc064f5dde3",
      "tree": "42c5a13241c7c4ffec850e8faddae5819f721bd5",
      "parents": [
        "eeec6d56c78687e0dcb1de7d38975ee151eeb3aa"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:59:23 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:59:23 2026 -0400"
      },
      "message": "git-patchwork-bot: extract PR matching logic into testable functions\n\nPull the merge commit parsing and pull request matching out of pwrun()\ninto standalone functions that can be unit tested without mocking the\npatchwork API or git:\n\n- parse_merge_logline(): extracts ref and URL from a merge subject\n- find_matching_pr(): matches a merge against a set of patchwork PRs\n\nAdd 18 new tests covering both functions, including the bluetooth PR\nscenario, protocol/subdomain/suffix mismatches, and edge cases.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "eeec6d56c78687e0dcb1de7d38975ee151eeb3aa",
      "tree": "0a2c0dbd4685aa41618e207954b3e25ff0bf0d2d",
      "parents": [
        "849a0d915c29a3e6c6a20a87b455994ba2209df5"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:57:09 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:57:09 2026 -0400"
      },
      "message": "Add pytest scaffolding and initial patchwork bot tests\n\nSet up a tests framework for the repo:\n\n- pyproject.toml with pytest configuration\n- tests/conftest.py with load_script() helper for importing\n  hyphenated script filenames via importlib\n- tests/test_patchwork_bot.py with URL normalization tests covering\n  protocol mismatches, subdomain variations, .git suffixes, and\n  trailing slashes\n\nAlso move test fixture TOML files from the top level into tests/.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "849a0d915c29a3e6c6a20a87b455994ba2209df5",
      "tree": "b92f11325c02a3bbded3cf967c455b0c06328afc",
      "parents": [
        "e2c9b3f3f791da67228d1522c547079d591e11ae"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:56:52 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:56:52 2026 -0400"
      },
      "message": "Add rtt-probe.py for email round-trip time monitoring\n\nMeasures mail delivery latency by sending HMAC-signed probe emails\nand checking for their arrival via IMAP or public-inbox. Reports\nround-trip times to syslog or stdout for infrastructure monitoring.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "e2c9b3f3f791da67228d1522c547079d591e11ae",
      "tree": "58fbd83ac6a983e94c354eead59f56c52ccac9bf",
      "parents": [
        "1320a25a9a556d5b434d6fad601efac54dedb57e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:53:51 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:53:51 2026 -0400"
      },
      "message": "Add find-abusers.py for identifying heavy git server consumers\n\nParses uwsgi logs (git-http-backend, cgit) and journalctl git-daemon\nentries to find IPs consuming the most server time. Optionally groups\nresults by ASN prefix for easier identification of abusive networks.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "1320a25a9a556d5b434d6fad601efac54dedb57e",
      "tree": "6e3ca5169657b6a2144ed0cce07e767d7acb3f02",
      "parents": [
        "77e2e6124fa34eb10694398271649c82f1645e48"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:51:12 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:51:12 2026 -0400"
      },
      "message": "Add .gitignore entries for Python tooling and vale config\n\nAdd common Python artifact directories and files to .gitignore:\n__pycache__, *.pyc, .venv, .mypy_cache, .ruff_cache, .pytest_cache.\n\nAlso add .vale.ini for prose linting of markdown files.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "77e2e6124fa34eb10694398271649c82f1645e48",
      "tree": "4a6e66964f016c355c32d7a1a51d5a02e5910e90",
      "parents": [
        "44169ff49ab3707fb1c84f2ed2ab340c47ff698e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:42:16 2026 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Sun Mar 15 10:42:16 2026 -0400"
      },
      "message": "git-patchwork-bot: normalize URL paths for pull request matching\n\nThe previous commit (44169ff) switched pull request URL matching from\na substring check to exact path comparison in order to handle protocol\nand subdomain differences. However, exact equality breaks when the\nmerge commit and the patchwork pull URL differ by a trailing slash or\na .git suffix -- both of which are common in practice.\n\nFor example, patchwork stores the URL from the pull request email with\na .git suffix (bluetooth.git), while the merge commit subject line\noften omits it (bluetooth). The old substring match tolerated this\nbecause \"bluetooth\" is a prefix of \"bluetooth.git\", but strict path\nequality fails.\n\nFix this by normalizing paths in get_url_path(): strip trailing\nslashes and .git suffixes before comparison.\n\nReported-by: Jakub Kicinski \u003ckuba@kernel.org\u003e\nLink: https://lore.kernel.org/all/20260312200655.1215688-1-luiz.dentz@gmail.com/T/#u\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-6\n"
    },
    {
      "commit": "44169ff49ab3707fb1c84f2ed2ab340c47ff698e",
      "tree": "62c3f526771cb3600f50ce3b21877feb848fff6d",
      "parents": [
        "4681d5cf14a4753d96c3efcbc92e08c1df3b7786"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jan 22 13:20:31 2026 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jan 22 13:21:27 2026 -0500"
      },
      "message": "git-patchwork-bot: fix pull request matching with URL rewrites\n\nWhen git\u0027s insteadOf configuration rewrites URLs (e.g., git:// to\nhttps://), the pull request URL in Patchwork differs from the URL in the\nmerge commit message. Additionally, subdomain variations like\ngit.kernel.org vs gitolite.kernel.org can cause mismatches.\n\nFix this by comparing only the path portion of URLs instead of the full\nURL. Even though we\u0027re dropping the domain, the path + tag combination\nshould be unique enough to avoid any clashes.\n\nReported-by: Krzysztof Kozlowski \u003ckrzk@kernel.org\u003e\nLink: https://lore.kernel.org/tools/f8463626-68c6-4b3e-ac0c-5a01db8e7755@kernel.org/\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nAssisted-by: claude-opus-4-5-20251101\n"
    },
    {
      "commit": "4681d5cf14a4753d96c3efcbc92e08c1df3b7786",
      "tree": "15f974b21a3e6dfd4e3b6f86740dd492fd139a3e",
      "parents": [
        "774dd7242e9344fdcc3a99da6de4d6a8d0a6873d"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 12 11:30:16 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 12 11:30:16 2025 -0400"
      },
      "message": "bugzilla-junker: actually open shelf with writeback\n\nWithout writeback, we don\u0027t actually get any updates.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "774dd7242e9344fdcc3a99da6de4d6a8d0a6873d",
      "tree": "474594707c88cb9662d2ebd45ca6b0729d0763b7",
      "parents": [
        "a2887ad61baf36a4d6a9bcc5d0b3f9df6865e94c"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 12 11:10:37 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Aug 12 11:10:37 2025 -0400"
      },
      "message": "bugzilla-junker: support path vars in configuration\n\nWe should support the usual vars in the configuration file.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "a2887ad61baf36a4d6a9bcc5d0b3f9df6865e94c",
      "tree": "00af5872dfbc9ec0353a622eb12073ba7933ee61",
      "parents": [
        "96690e5967d4d09ccf8fe8158eab5910c2af9042"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 11 17:52:42 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 11 17:52:42 2025 -0400"
      },
      "message": "bugzilla-junker: add config validation\n\nI had to update my own working config, so add schema validation when\nparsing the config file.\n\nAssisted-by: claude-4-sonnet\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "96690e5967d4d09ccf8fe8158eab5910c2af9042",
      "tree": "5ae66c668300ef2aa77a7204ac89c43655645fe6",
      "parents": [
        "da9905d53c681b54909a5b7d6e106db11da6e00c"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 11 17:36:08 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 11 17:36:08 2025 -0400"
      },
      "message": "bugzilla-junker: rework so I can give it to others\n\nThis was always a quick-and-dirty hack, but it\u0027s more useful than\nanything else I\u0027ve seen around. Clean it up so I can pass this along to\nsomeone else to do alongside with me.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "da9905d53c681b54909a5b7d6e106db11da6e00c",
      "tree": "ebba2b757929ec550acf6c774b9d42b55d6b5773",
      "parents": [
        "26818cb6b6d8a653fe6ca72ec15b9151a020b6f4"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jun 18 14:38:23 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jun 18 14:38:23 2025 -0400"
      },
      "message": "git-patchwork-bot: allow fnmatch-style matching of heads\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "26818cb6b6d8a653fe6ca72ec15b9151a020b6f4",
      "tree": "028294634eb9e909872c53c9019604135affc4ab",
      "parents": [
        "16df0a04a90f74ec23f160c3693241fd1a2e1922"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jun 18 13:13:30 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jun 18 13:13:30 2025 -0400"
      },
      "message": "git-patchwork-bot: further typing improvements\n\nImprove typing and add a small feature that also matches heads by\nfnmatch.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "16df0a04a90f74ec23f160c3693241fd1a2e1922",
      "tree": "1fc92af5dd3e563ccabfa696659826f667500320",
      "parents": [
        "2f6defce39d4e5283855d50e69aca58b15db1132"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri May 30 08:56:00 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri May 30 08:56:00 2025 -0400"
      },
      "message": "Further typing improvements for git-patchwork-bot\n\nAdditionally, limit msgid matches to only patch.msgid.link URLs, to\nreduce the number of false-positives.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "2f6defce39d4e5283855d50e69aca58b15db1132",
      "tree": "14ed40eb52f1d0d8e37db126850424330593e9fb",
      "parents": [
        "058221fcb31a6fd91883161cf7b826d5d9fd2527"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri May 02 17:21:14 2025 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri May 02 17:21:14 2025 -0400"
      },
      "message": "git-patchwork-bot: appease mypy linter\n\nPreparing to work on git-patchwork-bot again, so appease the latest\nmypy/ruff warnings and errors.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "058221fcb31a6fd91883161cf7b826d5d9fd2527",
      "tree": "2ddb9def98543db31487142b29420f1f6eb00ec8",
      "parents": [
        "8c95860c1cad92d00d4ee61e914d0d79dba2aa46"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Sep 20 07:07:41 2024 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Sep 20 07:07:41 2024 -0400"
      },
      "message": "mlmmj-subscriber-sync: add support for exclusion lists\n\nWe want to be able to specify addresses to never automatically\nsubscribe, so add support for .remote-subscriber-excl.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "8c95860c1cad92d00d4ee61e914d0d79dba2aa46",
      "tree": "a000fa10fa3354edfc32eaddd1769abc94d181d0",
      "parents": [
        "0679daed4769351c39926e263eabb39e6ec05fcb"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 18 10:06:16 2024 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 18 10:06:16 2024 -0400"
      },
      "message": "subscriber-sync: fix for multiple M: entries\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "0679daed4769351c39926e263eabb39e6ec05fcb",
      "tree": "4906e2e953b53100b64fe8da305ac4a62222f194",
      "parents": [
        "5d490055cefcc49e755f3567028f14bd10878de0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 18 07:26:48 2024 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Sep 18 07:26:48 2024 -0400"
      },
      "message": "mlmmj-subscriber-sync: support parsing MAINTAINERS file\n\nProvide a mechanism to parse all M: entries from the MAINTAINERS file as\na source of subscribers.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "5d490055cefcc49e755f3567028f14bd10878de0",
      "tree": "b3e782a8daf8786e0d8b97cf5b605d543b1a0c52",
      "parents": [
        "c3fa8bd14fe452fcceafd7f2baea171ac5533f30"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Aug 28 15:13:30 2024 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Aug 28 15:13:30 2024 -0400"
      },
      "message": "Update some scripts to no longer default to gpg2\n\nIt\u0027s now safe to assume that most distros will have a non-legacy version\nof gpg as the default binary.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "c3fa8bd14fe452fcceafd7f2baea171ac5533f30",
      "tree": "b7e376dc98b7421cc365ef1a0e800b569b391376",
      "parents": [
        "f25e5126091f1a25dd610f8ab620be8f8195e73b"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jan 22 14:47:49 2024 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jan 22 14:47:49 2024 -0500"
      },
      "message": "pwbot: fix pagination logic bug in REST results\n\nWe weren\u0027t doing the right thing when processing paginated data.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "f25e5126091f1a25dd610f8ab620be8f8195e73b",
      "tree": "d9698f392844fa77af3321f683eea518a69d0a18",
      "parents": [
        "67f2c63f05705876eeaa88ae234a55d44a9b3b0d"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jul 19 14:51:42 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jul 19 14:51:42 2023 -0400"
      },
      "message": "mlmmj-subscriber-sync: also catch OSError\n\nDon\u0027t crash on OSError when retrieving external subscriber info (e.g. if\nthere is a DNS lookup error).\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "67f2c63f05705876eeaa88ae234a55d44a9b3b0d",
      "tree": "376e053bc13dadc0350ac4b44620b18a0a8b2651",
      "parents": [
        "a951b8fe4a5f9bfd915e4a6cf998539d5f063834"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jul 19 14:47:18 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jul 19 14:47:18 2023 -0400"
      },
      "message": "pr-tracker-bot: add process-id to logging\n\nTrying to catch an elusive race condition, so add process-id to logging\ninformation.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "a951b8fe4a5f9bfd915e4a6cf998539d5f063834",
      "tree": "f9923134a1734793746ec043c22326ec8ef69ce5",
      "parents": [
        "902480fcb975912222ebf4a4b5972ec23948a412"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jul 19 14:45:49 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed Jul 19 14:45:49 2023 -0400"
      },
      "message": "git-patchwork-bot: support histogram patches\n\nWhen we cannot match a commit to any patches, try to regenerate it again\nwith the histogram algorithm in case the original patch was sent using\nthat option.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "902480fcb975912222ebf4a4b5972ec23948a412",
      "tree": "5dc3bb9388660552da96892542365d6816bbe430",
      "parents": [
        "82063fb68ab759d489e1efbc718efe4f10db62f4"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jul 18 12:04:05 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jul 18 12:04:05 2023 -0400"
      },
      "message": "Keep DKIM and X-Developer headers\n\nThese headers carry important end-to-end signatures, so do not strip\nthem when processing archives.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "82063fb68ab759d489e1efbc718efe4f10db62f4",
      "tree": "9aa8ff2eb50157378bea35f8ac24bc982b6e8479",
      "parents": [
        "4f84e5d7942e7573bcf0bb97256d5a74d9b96e5b"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 15 16:51:40 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 15 16:51:40 2023 -0400"
      },
      "message": "git-patchwork-bot: retry patch generation with --histogram\n\nWhen people send patches, their output will be dependent on their local\ndiff.algorithm setting. If they set it to something other than the\ndefault (\"myers\"), we may fail to generate a matching patch from the\ncommit.\n\nWhen we can\u0027t find a match, try to regenerate the patch using the\nhistogram algorithm and attempt the match again.\n\nReported-by: Jakub Kicinski \u003ckuba@kernel.org\u003e\nLink: https://msgid.link/20230615100021.43d2d041@kernel.org\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "4f84e5d7942e7573bcf0bb97256d5a74d9b96e5b",
      "tree": "2ab6018715257745b71dc9e4fb1f84e8b69ef8af",
      "parents": [
        "e474b98965d1f66373eda69b26f30db6f2818228"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Jun 02 16:31:40 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Jun 02 16:31:40 2023 -0400"
      },
      "message": "mlmmj-subscriber-sync: add ability to use a git repo for subs\n\nThe sync script can now use a git repository for external subscriber\ndata.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "e474b98965d1f66373eda69b26f30db6f2818228",
      "tree": "e6e4b963613c08706bce297e9c708c535a1bd899",
      "parents": [
        "3002ffb2e95c179d6f0aa4d6598f6405ad47c941"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed May 31 16:40:28 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Wed May 31 16:40:28 2023 -0400"
      },
      "message": "list-archive-collector: improve marc retrieval\n\nAdded some features to retrieving messages from marc.info:\n\n- earlier messages don\u0027t seem to have a message-id, so we fake it now\ninstead of completely ignoring these (they still serve a purpose)\n- the \"backoff\" time now considers how much time has elapsed since the\nlast request to marc.info, so if we already spent a couple of seconds\nchecking a message for spam, we don\u0027t additionally sleep an extra second\n- if the target mailbox already has messages in it, we will not\nre-download those\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "3002ffb2e95c179d6f0aa4d6598f6405ad47c941",
      "tree": "1621dd9d5da303b24753361cfc7d8d472fa965fd",
      "parents": [
        "b9c3a4ac3d0acf8e5722a2c0883b2400e315c90b"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 25 13:16:44 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 25 13:16:44 2023 -0400"
      },
      "message": "Update mlmmj-subscriber-sync to support checksums\n\nWe\u0027re now grabbing a checksums file.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "b9c3a4ac3d0acf8e5722a2c0883b2400e315c90b",
      "tree": "58789bc1ce99207a95f4f6750cdc10bf856e2752",
      "parents": [
        "29093fb09a4d55589d03a83fc07d8300e6e8e4fe"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 04 18:05:23 2023 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 04 18:05:23 2023 -0400"
      },
      "message": "Add mlmmj-subscriber-sync\n\nSmall helper script to synchronize mlmmj lists with external subscriber\ninformation.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "29093fb09a4d55589d03a83fc07d8300e6e8e4fe",
      "tree": "da9127143ff1ae7585d279477088b83e3ed794ce",
      "parents": [
        "ebb4f5797aec4bf2c8cdc116aecdbd698468d62e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jan 03 14:17:15 2023 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jan 03 14:17:15 2023 -0500"
      },
      "message": "groupsio-webhook: don\u0027t check for action\n\nValid messages may come in with other actions than just\n\"sent_message_accepted\" (e.g. \"sent_message_moderated\" or\n\"sent_message_not_subscriber\"), so accept everything that contains the\nmessage payload.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "ebb4f5797aec4bf2c8cdc116aecdbd698468d62e",
      "tree": "f4d027e6ea8b8ae10e29fbefbbc31743ef142381",
      "parents": [
        "9426618979ca1c6c5e32b552bdbd3af10688b4a0"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Oct 21 11:33:21 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Fri Oct 21 11:33:21 2022 -0400"
      },
      "message": "list-archive-maker: fix msgmap.sqlite3 support\n\nThe message-id\u0027s in msgmap.sqlite3 are without \u003c\u003e, which we actually use\nfor mapping.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "9426618979ca1c6c5e32b552bdbd3af10688b4a0",
      "tree": "b9dfcd12a01eab33de9abb96f75cba4b09321130",
      "parents": [
        "354fc16e397312c8972c6b48e7645d2c98d71db3"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Oct 20 15:47:25 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Oct 20 15:47:25 2022 -0400"
      },
      "message": "archive-collector: allow clean-subject to take multiple strings\n\nThe things lists do to their messages is annoying.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "354fc16e397312c8972c6b48e7645d2c98d71db3",
      "tree": "63cf3ce0686bd08aa79e42c2aa68afbe226cb308",
      "parents": [
        "a72a55b06660b249c82cdba32a4dc54e8a7ea2f3"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Oct 20 14:11:29 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Oct 20 14:11:29 2022 -0400"
      },
      "message": "list-collectors: small improvements\n\nAdd a few small improvements to the list-archive-collector and\nlist-archive-maker lists, allowing to more easier deal with partial\narchives.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "a72a55b06660b249c82cdba32a4dc54e8a7ea2f3",
      "tree": "e0b7ebb348f4fcbf673d63d4aec1bcd550151218",
      "parents": [
        "f0dbc2734d7e51ccfbba99f2e4b89fbe1dce0bdc"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Oct 11 17:40:33 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Oct 11 17:40:33 2022 -0400"
      },
      "message": "Optimize emergency saving of messages\n\nInstead of duplicating code, just use a single instance of _save_emerg.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "f0dbc2734d7e51ccfbba99f2e4b89fbe1dce0bdc",
      "tree": "f594feee1a5225156fdd4bb2eabd2407f825674b",
      "parents": [
        "7b708a358db586545cd36d8842efd94dece124af"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Oct 11 16:46:35 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Oct 11 16:46:35 2022 -0400"
      },
      "message": "More debugging options for groupsio webhook\n\nShould be largely backwards compatible with what we had before, but\nallowing us to also troubleshoot occasional 500 errors.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "7b708a358db586545cd36d8842efd94dece124af",
      "tree": "9b02a4f3829f10f059a7a3ea5bf76a31cf13cbae",
      "parents": [
        "8869f6cce1bf25b78632baf89bca792200090036"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Oct 11 13:56:24 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Oct 11 13:56:24 2022 -0400"
      },
      "message": "Rewrite groupsio-hook to add logging\n\nWe\u0027re seeing some 500 errors, and to debug them we need proper logging\nand emergency message dumping.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "8869f6cce1bf25b78632baf89bca792200090036",
      "tree": "950d7e31a1ae89977dac309f05f96731ed4f0ce5",
      "parents": [
        "f2680af521673c402282324d478ae682ef969c14"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 08 16:41:51 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Aug 08 16:41:51 2022 -0400"
      },
      "message": "mainline-when: treat 19 as the last minver\n\nThis may change in the future, but for now treat .19 as the last minor\nversion before majver is incremented.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "f2680af521673c402282324d478ae682ef969c14",
      "tree": "df17014c9c753dafff87349a6164cbe4dfff81e9",
      "parents": [
        "ead2b92216c135152f28f9847b52479fe279edf8"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jun 27 14:41:08 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jun 27 14:41:08 2022 -0400"
      },
      "message": "Allow list-id to be a globbing match\n\nAdd ability to specify list-ids as a globbling match (even if it\u0027s just\n\u0027*\u0027 for \"everything\").\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "ead2b92216c135152f28f9847b52479fe279edf8",
      "tree": "f600124fa8cf0c21090f8d4363c2b907d20d9a56",
      "parents": [
        "f057cb93e1094eceeedf86ce906aa90616541674"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jun 27 11:51:08 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jun 27 11:51:08 2022 -0400"
      },
      "message": "Add support to write out as maildir\n\nIt is periodically useful to save the archives as maildir instead of\nmailbox. Add support for -asmaildir.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "f057cb93e1094eceeedf86ce906aa90616541674",
      "tree": "c1c40a67b58af465e398d8366713e545b2b0e75a",
      "parents": [
        "ef2bec9db7f633b50558fb82b3c04e7a3addfe6e"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 02 14:27:07 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Jun 02 14:27:07 2022 -0400"
      },
      "message": "Tweak wording on mainline-when\n\nAdd a note in the vcal output that the final release may actually end up\nbeing another -rc instead.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "ef2bec9db7f633b50558fb82b3c04e7a3addfe6e",
      "tree": "211e26e44955dba6925819401567923012d0bd7e",
      "parents": [
        "7d92b25a26a7c7e964f296b4a364c55fcb8637d1"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 26 12:31:34 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 26 12:31:34 2022 -0400"
      },
      "message": "Treat failures to uncompress as non-fatal\n\nWe shouldn\u0027t crash out if we got a corrupted download. This script is\nlooking specifically at bad signatures -- a download that won\u0027t even\nproperly uncompress is probably not trying to sneak in badness.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "7d92b25a26a7c7e964f296b4a364c55fcb8637d1",
      "tree": "bc82fa9842dbb06fc47f9841875a8649ebe26a43",
      "parents": [
        "5ef489023ee2e8728cf63131eee33b24aa837c61"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 26 12:26:44 2022 -0400"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Apr 26 12:26:44 2022 -0400"
      },
      "message": "Adjust default sig-prover node list\n\nWe\u0027re migrating the nodes to their new homes on Equinix gen3 systems, so\nadjust the list of edge sites accordingly.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "5ef489023ee2e8728cf63131eee33b24aa837c61",
      "tree": "2d36eb25a2a8668cb14f5a3dfc91e35b93b491c3",
      "parents": [
        "56e1574f18bce218ccc65ec507d36849f2bee0fb"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Feb 01 10:14:08 2022 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Feb 01 10:14:08 2022 -0500"
      },
      "message": "mainline-when: fix var scoping issues\n\nIt worked, but it was a bug waiting to happen.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "56e1574f18bce218ccc65ec507d36849f2bee0fb",
      "tree": "da7eeee8eb7c968e909c58d50d8c919a05e74fa2",
      "parents": [
        "8f0b9e92e2dbd5dfd14ad9268a5c721eb611d573"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jan 31 16:40:13 2022 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jan 31 16:40:13 2022 -0500"
      },
      "message": "mainline-when: rework to remove depedencies and generate .ics\n\n- removes dependency on python-packaging\n- switches to using logger calls instead of print()\n- adds ability to generate .ics files\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "8f0b9e92e2dbd5dfd14ad9268a5c721eb611d573",
      "tree": "09bfa4847940d8e6736ce2620967e64aad318d35",
      "parents": [
        "10b7024b67ae4985a88d29abba46953497601ac3"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jan 31 11:30:43 2022 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Jan 31 11:34:58 2022 -0500"
      },
      "message": "mainline-when: add a tiny script to estimate dates\n\nHelp people estimate kernel release dates.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "10b7024b67ae4985a88d29abba46953497601ac3",
      "tree": "6e3d359e13cf8382195672a5e4b80636cdf41fba",
      "parents": [
        "3da44d217d546e8b7fe5b00f905ca38913255db2"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jan 25 11:52:31 2022 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jan 25 11:52:31 2022 -0500"
      },
      "message": "Fix hash generation after switching to \"git show\"\n\nWhen I switched to \"git show\" I didn\u0027t fully test whether it would\ngenerate the same patchwork hashes as \"git diff\" proper. The few sample\ntries I ran happened to do the right thing, but many other commits would\ngenerate different hashes. Make sure we ignore anything that comes\nbefore we find \"diff \" on the line.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "3da44d217d546e8b7fe5b00f905ca38913255db2",
      "tree": "1c7a0615a8adc224c55b041ae3b0c6f871c089e9",
      "parents": [
        "41cfb037967b23f873979dac3de7d018397ccc7c"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jan 25 09:09:42 2022 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Jan 25 09:09:42 2022 -0500"
      },
      "message": "patchwork-bot: add ability to match by msgid\n\nPatchwork API 1.2 allows us to match by msgid, so update patchwork-bot\nto use Link: trailers for additional matching when we are unable to\nmatch by patchwork hash.\n\nAdditionally, reworks caching that wasn\u0027t actually saving us any time\nand drops the use of git patch-id that we used in the absence of msgid\nmatching.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "41cfb037967b23f873979dac3de7d018397ccc7c",
      "tree": "d2a7509c9910c5970b0d54fc35fb5967def72aa8",
      "parents": [
        "0c107968964fdff89ed6a7afb651b0fa5b28efe9"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Dec 16 15:10:47 2021 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Thu Dec 16 15:10:47 2021 -0500"
      },
      "message": "Revert \"patchwork-bot: Ignore empty context lines\"\n\nThis reverts commit 8998144862b16cfa7a2ec07757d43ca269d59373.\nUnfotunately, this change broke matching for other patches, so roll it\nback to the way it was.\n\nReported-by: Jakub Kicinski \u003ckuba@kernel.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "0c107968964fdff89ed6a7afb651b0fa5b28efe9",
      "tree": "c2faac229c44127bc56be5a1120bb0d20ac6d898",
      "parents": [
        "db381d559031ce8ff3899ac79d62dbace4db902f"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Dec 14 16:54:48 2021 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Dec 14 16:54:48 2021 -0500"
      },
      "message": "patchwork-bot: add support for --tokens-file\n\nWe want to be able to share our configuration with others, which\nrequires that we keep secrets in a different file than the rest of the\nlogic.\n\nSuggested-by: Kees Cook \u003ckees@outflux.net\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "db381d559031ce8ff3899ac79d62dbace4db902f",
      "tree": "07e9a04f646bd296cc4fb9cc0e4d4c36ba3d925e",
      "parents": [
        "a48d14b2cd13540d0ad1a65e504b7d5ac19c15be"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Dec 14 16:01:42 2021 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Dec 14 16:01:42 2021 -0500"
      },
      "message": "patchwork-bot: add proper typing hints\n\nIt helps to catch easy bugs when writing in PyCharm and other IDEs.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "a48d14b2cd13540d0ad1a65e504b7d5ac19c15be",
      "tree": "93a0bc2061c04924193019f38653ed03ee4e1fa9",
      "parents": [
        "ffe2576ac7731b7596e4bdb1c10ab9768f93ee09"
      ],
      "author": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Dec 14 14:49:51 2021 -0500"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Tue Dec 14 14:49:51 2021 -0500"
      },
      "message": "patchwork-bot: remove xmlrpc vestiges\n\nWe used to need xmlrpc because API \u003c 1.2 didn\u0027t expose ability to search\npatches by hash. Now that it\u0027s available in API 1.2, we can get rid of\nall xmlrpc calls and just leave the REST api support.\n\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\n"
    },
    {
      "commit": "ffe2576ac7731b7596e4bdb1c10ab9768f93ee09",
      "tree": "50fcf221eb4ee92696d904c0caa9aa734a2aa26f",
      "parents": [
        "8998144862b16cfa7a2ec07757d43ca269d59373"
      ],
      "author": {
        "name": "Kees Cook",
        "email": "keescook@chromium.org",
        "time": "Thu Dec 02 15:38:36 2021 -0800"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Dec 13 17:28:51 2021 -0500"
      },
      "message": "patchwork-bot: Add --pwhash for debugging\n\nFinding a 2-character whitespace difference between patches with different\npatchwork hashes strained my sanity, so I want to keep the tooling I\nbuilt to track it down. Using \"--pwhash PATCH-ID\", patchwork-bot will\ncompare the given patch in patchwork (and its hash) against the diff\nfrom stdin (and its hash).\n\nSigned-off-by: Kees Cook \u003ckeescook@chromium.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nLink: https://lore.kernel.org/r/20211202233836.2024510-3-keescook@chromium.org\n"
    },
    {
      "commit": "8998144862b16cfa7a2ec07757d43ca269d59373",
      "tree": "f7a0b0e28ff598c86e09ca02fbc4791c31f0511b",
      "parents": [
        "6fc2e91a876b0a2771eedb0fe6106d7f47eb8aab"
      ],
      "author": {
        "name": "Kees Cook",
        "email": "keescook@chromium.org",
        "time": "Thu Dec 02 15:38:35 2021 -0800"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Dec 13 17:28:51 2021 -0500"
      },
      "message": "patchwork-bot: Ignore empty context lines\n\nIt seems that there is some mismatch in diff output (or storage?) between\ngit and patchwork under conditions I haven\u0027t figured out (MUA or MTA\nwhitespace stripping?): patchwork appears to ignore empty context lines\n(i.e. a line that is only a single space). As a result, the patchwork\nhash will not match, and commits will not be identified in patchwork.\n\nAs an example of a commit in upstream that patchwork-bot cannot locate\nin patchwork due to a different hash, due to the differing empty context\nlines:\n\n$ MSGID\u003d20210911102818.3804-1-len.baker@gmx.com\n$ wget -qO- https://patchwork.kernel.org/project/linux-hardening/patch/$MSGID/raw/ | \\\n\tgrep \u0027^ $\u0027 | wc -l\n0\n\n$ SHA\u003df11ee2ad25b22c2ee587045dd6999434375532f7\n$ git diff ${SHA}~..${SHA} | \\\n\tgrep \u0027^ $\u0027 | wc -l\n2\n\nTeaching patchwork-bot to ignore empty context lines allows pwhashes to\nmatch in these cases, which lets those patches get located again.\n\nSigned-off-by: Kees Cook \u003ckeescook@chromium.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nLink: https://lore.kernel.org/r/20211202233836.2024510-2-keescook@chromium.org\n"
    },
    {
      "commit": "6fc2e91a876b0a2771eedb0fe6106d7f47eb8aab",
      "tree": "2d3228e91222281f7839e6f8e3310029a05902ac",
      "parents": [
        "460990bdca13805f7e9b5d6c8037437f655bcfac"
      ],
      "author": {
        "name": "Kees Cook",
        "email": "keescook@chromium.org",
        "time": "Tue Nov 30 16:11:26 2021 -0800"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Dec 13 17:28:18 2021 -0500"
      },
      "message": "patchwork-bot: Allow initial database to skip recent commits\n\nTo test patchwork-bot, it is helpful to create the initial database\nwithout a certain number of the latest commits, so that on the next\nexecution, the \"missing\" commits will appear new, and will be used for\nprocessing state changes. Add the --ancestors argument (defaulting to\n0, the existing behavior), to provide this ability.\n\nSigned-off-by: Kees Cook \u003ckeescook@chromium.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nLink: https://lore.kernel.org/r/20211201001126.4106635-6-keescook@chromium.org\n"
    },
    {
      "commit": "460990bdca13805f7e9b5d6c8037437f655bcfac",
      "tree": "000524be6f07952c868a04a44994c1381e83785a",
      "parents": [
        "36971ee3ad62a56f40c3663a82b7475151df2a77"
      ],
      "author": {
        "name": "Kees Cook",
        "email": "keescook@chromium.org",
        "time": "Tue Nov 30 16:11:25 2021 -0800"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Dec 13 17:28:18 2021 -0500"
      },
      "message": "patchwork-bot: Handle repo being a worktree\n\nLocate the real git location when operating on a worktree, allowing\nmultiple pw.db files to be stored in a single top-level git repo.\n\nSigned-off-by: Kees Cook \u003ckeescook@chromium.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nLink: https://lore.kernel.org/r/20211201001126.4106635-5-keescook@chromium.org\n"
    },
    {
      "commit": "36971ee3ad62a56f40c3663a82b7475151df2a77",
      "tree": "bc77dd172eaca0f21bf97ba9d3c9a7d608700b5c",
      "parents": [
        "e2dc648b93b77e45fde736ecbff8d0080fca41ff"
      ],
      "author": {
        "name": "Kees Cook",
        "email": "keescook@chromium.org",
        "time": "Tue Nov 30 16:11:24 2021 -0800"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Dec 13 17:28:18 2021 -0500"
      },
      "message": "patchwork-bot: Allow limiting heads to single branch\n\nTo sanely use a single git repository with multiple worktrees, it must\nbe possible to limit the commits by branch (otherwise all commits are\nvisible for all worktrees). Add an optional \"branch\" argument for this\nlimit.\n\nSigned-off-by: Kees Cook \u003ckeescook@chromium.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nLink: https://lore.kernel.org/r/20211201001126.4106635-4-keescook@chromium.org\n"
    },
    {
      "commit": "e2dc648b93b77e45fde736ecbff8d0080fca41ff",
      "tree": "57e3b87f15631c108e3f23f05d28e9f5243b93f5",
      "parents": [
        "bba5718890cbc3704528c028e104dde197d4c49c"
      ],
      "author": {
        "name": "Kees Cook",
        "email": "keescook@chromium.org",
        "time": "Tue Nov 30 16:11:23 2021 -0800"
      },
      "committer": {
        "name": "Konstantin Ryabitsev",
        "email": "konstantin@linuxfoundation.org",
        "time": "Mon Dec 13 17:28:18 2021 -0500"
      },
      "message": "patchwork-bot: Create CACHEDIR if it is missing\n\nCreate the CACHEDIR if it is missing (e.g. the first time\ngit-patchwork-bot is executed).\n\nSigned-off-by: Kees Cook \u003ckeescook@chromium.org\u003e\nSigned-off-by: Konstantin Ryabitsev \u003ckonstantin@linuxfoundation.org\u003e\nLink: https://lore.kernel.org/r/20211201001126.4106635-3-keescook@chromium.org\n"
    }
  ],
  "next": "bba5718890cbc3704528c028e104dde197d4c49c"
}
