23 May 2026, 20:00

AI and I upgraded MediaWiki

Up There 2, July 2016

I created wiki.robnugen.com using MediaWiki probably over 15 years ago.

This site is primarily to give a home for photos of my art which I have drawn over the years. Each piece has a permalink written on the back.

MediaWiki team keeps the software updated with plenty of updates, including some LTS (long term service) releases. Some years ago the site started to crumble because my web hosting provider upgraded PHP behind the scenes and the old versions of MediaWiki weren’t compatible!

Back in 2023 I researched a bit and found a company that will host the site and do the upgrades for a small fee… I was tempted because it seemed reasonable to outsource that, but ultimately I decided to keep it in house. If they could do it, I can do it!

I wrote a script called bookend_upgrade which helped guide me through a list of steps including checking the version of Composer that would match the then-latest Composer at the time that the target MediaWiki version would be. It protected my copy of LocalSettings.php in its own git repo and blah blah to help me upgrade MediaWiki. It worked!

Since 26 May 2023, the site has been running on MediaWiki 1.39.3. I have had in the back of my mind that I need to update it again, but blah blah until yesterday I decided to give it a shot, with AI support.

Yesterday AI and I:

  • Got the install from 1.39.3 → 1.39.17 (about 3000 commits).
  • Discovered (and fixed) four problems with the script.
  • Updated scripts to use MediaWiki’s submodules for skins and vendor

Today AI and I upgrade the site to 1.43. AI did most of the work. Once it was done, I had him clean up his scripts and write about it.

https://wiki.robnugen.com/wiki/Times:2026_may_24_upgraded_mediawiki

Want to explore?

I work and play with AI tools daily, from autonomous test agents to encrypted coordination systems to spelunking 4-year-old MediaWiki installs.

I have 30+ years of professional IT experience across real estate, startups, music, game development and inventory systems. Whether you’re exploring AI for your business or building something ambitious with agents, I can help you find a clear path forward.

https://cal.eu/robnugen/tech-support-with-rob-nugen

18 May 2026, 09:00

git close-bubble: Reliably Close Merge Bubbles

git close-bubble demo

I like Merge Bubbles

I like git. I use it for all my programming projects of course, and everything from ~/.ssh/ to ~/.claude/ to my Godot game (coming soon, I promise) to my book formatted in LaTeX, and nearly any directory that’s got custom-edited text files in it.

I generally work alone on my projects, so I don’t need PRs like [github-flow](https://docs.github.com/en/get-started/using-github/github-flow) and especially don’t need the original [git-flow](https://nvie.com/posts/a-successful-git-branching-model/). But I do like to keep groups of commits together. For that, I like merge bubbles.

For example:

*   e3f9cca DONE admin set existing user password
|\
| * 0a6dd90 Admin user_edit: hide set-password panel on self + server-side guard
| * d5dc655 AdminSetPasswordCest: end-to-end round trip on abc
| * ba74df9 placeholder for AdminSetPasswordCest
| * bbb4013 Admin user_edit: set-password form + handler
| * 384f4ba locale: admin user_edit set-password strings (EN + JA)
|/
* 7cd2527 BEGIN admin set existing user password
*   658cc93  DONE admin-driven brand manager registration
|\
| * 6e1f640 register.php: admin-driven success message names the new manager + login URL
| * bcda5fd register.php: gate to admin only post-bootstrap
| * 94b8c63 Admin users list: '+ Add brand manager' link to /login/register.php
|/
* 8eb82ba BEGIN brand manager registration UX

Every set of related commits lives inside a bubble. I start with a BEGIN commit, which generally has either nothing (via --allow-empty) or a minimum change, like updoot the the version of the code.

From there, I stack up a bunch of related commits. When it’s time to close the commit, I just:

  1. Look up the hash of the BEGIN commit
  2. Copy the hash to my paste buffer
  3. git checkout [paste]
  4. git merge --no-ff active-branch -m "DONE with my awesome change"
  5. gitl, which for me means git log --oneline --graph --decorate --all
  6. Look up the newly created hash for the DONE commit
  7. Copy the hash to my paste buffer
  8. git branch -f active-branch [paste]
  9. git checkout active-branch

It’s a lot! It’s a mess; it’s annoying; it’s fragile, but it’s repeatable and I love it.

I asked my AI about it and it was like “yeah no worries mate” (or something like that), and presented me with this script which I have saved in ~/.local/bin/git-close-bubble:

git close-bubble

#!/usr/bin/env bash
# Close a merge bubble started with a "BEGIN ..." commit.
# Usage: git close-bubble <message> [BEGIN-commit]
#        git close-bubble --dry-run [BEGIN-commit]

set -euo pipefail

DRY_RUN=0
if [ "${1:-}" = "--dry-run" ]; then
    DRY_RUN=1
    shift
fi

if [ "$DRY_RUN" -eq 0 ] && [ $# -lt 1 ]; then
    echo "usage: git close-bubble <message> [BEGIN-commit]" >&2
    echo "       git close-bubble --dry-run [BEGIN-commit]" >&2
    exit 1
fi

if [ "$DRY_RUN" -eq 1 ]; then
    MSG=""
    BEGIN_ARG="${1:-}"
else
    MSG="$1"
    BEGIN_ARG="${2:-}"
fi

B=$(git branch --show-current)

if [ -n "$BEGIN_ARG" ]; then
    S=$(git rev-parse --verify "$BEGIN_ARG")
else
    CLOSED=$(git log --merges --format='%P' | awk '{print $1}' | sort -u)
    S=""
    while IFS= read -r c; do
        if ! printf '%s\n' "$CLOSED" | grep -qx "$c"; then
            S="$c"; break
        fi
    done < <(git log --grep='^BEGIN ' --format='%H')
    if [ -z "$S" ]; then
        echo "error: no unclosed BEGIN commit found" >&2
        exit 1
    fi
fi

echo "Would close bubble starting at: $(git log -1 --format='%h %s' "$S")"

if [ "$DRY_RUN" -eq 1 ]; then
    echo "(dry-run; no changes made)"
    exit 0
fi

git checkout "$S"
git merge --no-ff "$B" -m "$MSG"
M=$(git rev-parse HEAD)
git checkout "$B"
git merge "$M"

echo "Done. Branch '$B' now points at $M. Inspect, then 'git push' when satisfied."

It near-magically handles the fragile command line stuff with a single line:

git close-bubble "DONE my cool code"

It does all the 9 steps above without me having to copy hashes and remembering all the incantations.

Dry Run

There is a dry-run option you can use to see what hash it would target as the BEGIN commit.

git close-bubble --dry-run