Setting the Stage: Context for the Curious Book Reader
This technical journal entry captures a developer regaining control over a project named Pipulate after a period of heavy reliance on AI coding assistants. The narrative is a compelling blend of high-level strategic decision-making and hands-on, command-line execution. At the heart of the entry is the developer’s choice to prioritize momentum in codebase cleanup over tackling a new “rabbit hole” project like home-hosting. This decision leads to a series of practical refactoring and Git repository management tasks, all performed with a renewed focus on “internalizing” skills rather than delegating them.
The entry serves as a live demonstration of a solo developer’s workflow, showcasing how to manage a feature branch takeover, clean up a cluttered repository, and perform project-wide code modifications. The core technical skills on display are the use of command-line tools like ripgrep
(rg
), sed
, and xargs
for refactoring, and a specific, robust git
workflow for promoting a feature branch to main
and then pruning obsolete branches. This is all framed within the author’s larger goal of taking back the “reins” of the codebase from AI and re-establishing a strong personal mental model of the project’s architecture.
Choosing Momentum Over Rabbit Holes
We are always standing at the precipice of rabbit hole projects that could uselessly consume the discretionary time of your weekend — or the rest of your life. Some of those journeys down into the rabbit hole are valuable learnings to move forward better in life while others are complete wastes of time. The real trick is knowing one from the other.
I have to choose between home-hosting and the general iterative improvements on the Pipulate codebase to make it leaner, meaner and more ready for public consumption. But home-hosting gets me the increasingly critical ability to watch the bots come visit my site in real-time by watching the logfiles like a fishtank as part of AI-readiness. These are both equally urgent.
Once I start the home-hosting work, momentum shifts and I risk losing it. I’m on a roll with the code cleanup so I’m going to run with that for awhile.
I’m gonna drop the whole .cursor/rules
directory into cruft
given that I’m
quitting Cursor cold turkey. There are good rules in there, but that’s part of
the whole markdown bloat thing. I’m going to try to let the codebase speak for
itself to the AI code assistants.
Applying the rg | xargs sed
Pattern for Code Refactoring
Okay, let’s keep cleaning up the Pipulate repo root. I shouldn’t have to look at
that many files and folders. I feel piper_models
is an asset that belongs in
the assets
folder.
[mike@nixos:~/repos/pipulate]$ rg piper_models
modules/voice_synthesis.py
61: local_model_dir = "./piper_models"
helpers/docs_sync/archive/stale_progress_reports/ai_chip_otheseus_voice_integration_plan.md
34:mkdir -p piper_models/en/en_US/amy/low/
104: local_model_dir = "./piper_models"
[mike@nixos:~/repos/pipulate]$
Okay, this one is a bit trickier because I have to backslash the forwardslash in
order to neutralize it because the forwardslash is part of the
search-and-replace API. This is the same issue as in vim so I know how to handle
it. If I were to search and replace all the back to the “from here” symbol it
would be a lot more backslashing to get the ./
but that’s not necessary in
this case:
[mike@nixos:~/repos/pipulate]$ \rg -l --null piper_models | xargs -0 sed -i 's/piper_models/assets\/piper_models/g'
[mike@nixos:~/repos/pipulate]$ rg piper_models
modules/voice_synthesis.py
61: local_model_dir = "./assets/piper_models"
helpers/docs_sync/archive/stale_progress_reports/ai_chip_otheseus_voice_integration_plan.md
34:mkdir -p assets/piper_models/en/en_US/amy/low/
104: local_model_dir = "./assets/piper_models"
[mike@nixos:~/repos/pipulate]$
And then finally I actually move the folder:
[mike@nixos:~/repos/pipulate]$ mv piper_models/ assets
[mike@nixos:~/repos/pipulate]$
I do this without a git mv
because that folder is not actually in the git
repo. As a fetched-from-the-net resource it doesn’t have to take up room in the
repo so it’s filtered out with .gitignore
, which reminds me that’s an instance
where I have to edit it manually.
Once this is done I test it with nix develop .#dev
which constructs the
piper_modules
folder inside my assets
folder and when I go to the Pipulate
app and do the magic demo keyboard shortcut: Ctrl
+Alt
+D
the demo runs and
as I do the follow-up Ctrl
+Alt
+y
responses, it gets to the voice synthesis
part and talks! And so I’m pretty sure this worked but I’ll keep an eye on it
because you never know with caching. I won’t be satisfied until after a full
reboot or testing on a different system.
Taking Back the Reins: The Need to Internalize Git
Okay, so now I have a few urgent steps I want to get back into the main branch,
especially this one about the new python312full
no longer being in the nix
repo. That broke it for everyone and it wasn’t me. Important lesson there. Use
the version names that won’t break, even in the unstable repo!
Now I have to do things I’ve done with AI assistance a bunch of times but never internalized! That’s got to stop. I figure it out and make the strong mental model now, and I commit these moves to muscle memory.
When you are on a git branch and it should become the new main trunk, there are several paths.
The path we take keeps the entire history intact. There have been no new changes in the main trunk that we need to merge in. And even if there were changes there, they would have been mistakes and we do not need to merge them in. I am working as a singleton developer for the most part, using git mostly for infinite undo’s and for a distribution method to the public. If I start receiving pull requests (PR’s) that might change, but for now it’s just me and I can do this sort of stuff safely.
That being the case, my preferred method is… well, it’s also worth pointing
out that the git branch that I’m on does have its own remote endpoint that I
made on GitHub as a matter of safety so that I could git push
from this branch
and have the code in 2 places so that I can always do a git reset --hard
to
any hash and if I make a mistake I can always just git pull
and get back to
the HEAD. There may be other ways that are more effective, but after all these
years I still consider myself a git novice and this is that way that’s worked
for me.
Okay, so the preferred method is to actually rename the current branch that I’m
on which happens to be modularize into main. And then I have to make the
changes so that when I do my next git push
it actually replaces the remote
branch named main with this branch. modularize does in fact have the complete
git history of main thus far and so this is really just a git branch naming
swaparoo. Help me out Gemini. How do I do this?
The Three-Step Branch Takeover: Promoting modularize
to main
Gemini: Of course. You’ve described the perfect scenario for a branch “takeover.” Since you’re the sole developer and your modularize
branch is the definitive source of truth, you can safely and cleanly make it the new main
.
Here is the step-by-step process to rename your current branch to main
and replace the old main
on GitHub.
✅ Safety First: A Quick Backup
Before you start, do one last regular push of your current branch to its remote backup on GitHub. This ensures you have a complete copy of your work stored safely under the modularize
name before you begin the renaming process.
git push origin modularize
The Three-Step Takeover
This process will make your local and remote main
branches identical to your current modularize
branch.
Step 1: Force Rename Your Local Branch
First, rename your local modularize
branch to main
. We use the -M
flag (a capital M) which is a force rename. This is necessary because you likely already have an old branch named main
locally, and this command tells Git, “I know main
exists, overwrite it.”
git branch -M modularize main
Your terminal prompt should now show that you are on the main
branch, which contains all of your modularize
history.
Step 2: Force Push to Remote main
⚠️
This is the most important step. You need to force push your new local main
branch to the remote repository (origin
). A regular push will be rejected because the history of your new main
has diverged from the old main
on GitHub. A force push tells the remote, “Discard your version of main
and accept mine as the new, correct history.”
git push --force origin main
(You can also use the shorthand -f
instead of --force
)
After this command completes, the main
branch on GitHub will be a perfect mirror of your work.
Step 3: Clean Up the Old Remote Branch
Your work is now safely on main
, so the modularize
branch on GitHub is redundant. It’s good practice to delete it to avoid confusion.
git push origin --delete modularize
For Muscle Memory
To internalize this, just remember the three key actions:
- Rename:
git branch -M <current> main
- Force Push:
git push --force origin main
- Clean Up:
git push origin --delete <current>
Me:
[mike@nixos:~/repos/pipulate]$ git push origin modularize
Everything up-to-date
[mike@nixos:~/repos/pipulate]$ git branch -M modularize main
[mike@nixos:~/repos/pipulate]$ git push --force origin main
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To github.com:miklevin/pipulate
920903b..e7268d8 main -> main
[mike@nixos:~/repos/pipulate]$ git push origin --delete modularize
To github.com:miklevin/pipulate
- [deleted] modularize
[mike@nixos:~/repos/pipulate]$
The Great Pruning: Deleting All Other Local and Remote Branches
Excellent, I’m right with you there, Gemini! It all worked perfectly. How do I delete all my local and remote branches now except for main?
[mike@nixos:~/repos/pipulate]$ git branch
backup-before-reset
botify-extraction-experiment
bughunt_20250709_090905
clean-oz-transition
database-fix
database-persistence-fix
ebb
extractions
fix-cherry-pick-mcp-improvements
golden-path-cascading-v2
golden-path-mcp-complete
* main
main_pre_merge
mainbeforecatchmeup
poppyfield
poppyfield2
safety_golden_path_restoration
teststuff
working-monolithic-version
yellowbricklion
yellowbricklion2
yellowbricklion3
yellowbricklion4
yellowbricklion5
yellowbricklion6
yellowbricklion7
yellowbrickroad
yellowbrickscarecrow
yellowbricktinman
yellowbrickwogglebug
yellowbrickwogglebug2
[mike@nixos:~/repos/pipulate]$
Gemini: Of course. You can clean up all your old branches with two one-liner commands: one for your local machine and one for the remote repository on GitHub.
Delete Local Branches (Except main
) 🧹
This command lists all your branches, filters out the main
branch, and then passes the resulting list to the delete command.
git branch | grep -v "* main" | xargs git branch -d
git branch
: Lists your local branches.grep -v "* main"
: Inverts the match (-v
), finding all lines that do not contain* main
.xargs git branch -d
: Passes each branch name to thegit branch -d
(delete) command.
⚠️ If you have unmerged work on some branches, the command above will fail for those specific branches. To force delete them regardless of their status, use a capital -D
instead:
git branch | grep -v "* main" | xargs git branch -D
Delete Remote Branches (Except main
) ☁️
You can use the same logic to clean up the branches on GitHub. This command gets your local branch list (excluding main
) and tells GitHub to delete each one.
git branch | grep -v "* main" | xargs git push origin --delete
It’s perfectly fine if you see errors for branches that never existed on the remote; the command will still successfully delete all the ones that do.
Okay, I followed the instructions and deleted all my local branches that aren’t main and tried doing the command to do the same remote but got an error.
Gemini: That error is happening because you’ve already successfully deleted all your local branches. The previous command relied on that local list, which is now empty, so there are no branch names to pass along to the delete
command.
To clean up the remote branches, you need a command that gets the list of branches directly from the remote server.
The Correct Command for Remote Cleanup
This command will list all branches on the remote (origin
), filter out main
, clean up the names, and then delete them.
git branch -r | grep -v "main" | sed 's/origin\///' | xargs git push origin --delete
How It Works
git branch -r
: Lists all remote-tracking branches (e.g.,origin/some-branch
).grep -v "main"
: Filters out any lines containing “main”, which takes care of theorigin/main
branch itself.sed 's/origin\///'
: This is the key change. It uses the stream editor to find the textorigin/
in each line and replaces it with nothing, leaving just the clean branch name.xargs git push origin --delete
: Passes the clean branch names to the remote delete command.
Me:
[mike@nixos:~/repos/pipulate]$ git branch -r | grep -v "main" | sed 's/origin\///' | xargs git push origin --delete
To github.com:miklevin/pipulate
- [deleted] backup-before-reset
- [deleted] botify-extraction-experiment
- [deleted] catchmeup
- [deleted] ebb
- [deleted] extractions
- [deleted] golden-path-cascading-v2
- [deleted] golden-path-mcp-complete
- [deleted] poppyfield
- [deleted] poppyfield2
- [deleted] poppyfield3
- [deleted] poppyfield4
- [deleted] poppyfield5
- [deleted] pumpkinhead
- [deleted] rubyslippers
- [deleted] yellowbricklion
- [deleted] yellowbricklion3
- [deleted] yellowbricklion4
- [deleted] yellowbricklion5
- [deleted] yellowbricklion6
- [deleted] yellowbrickscarecrow
- [deleted] yellowbricktinman
- [deleted] yellowbrickwogglebug
- [deleted] yellowbrickwogglebug2
[mike@nixos:~/repos/pipulate]$
And now that’s a good cleanup and decluttering!
While it’s not quite down to my rule of 7 yet, the repo root is looking a lot more clean that it did before:
mike@nixos:~/repos/pipulate]$ ls -la
total 1148
drwxr-xr-x 22 mike users 4096 Sep 12 14:03 .
drwxr-xr-x 10 mike users 4096 Sep 9 19:46 ..
-rw-r--r-- 1 mike users 11 May 13 12:07 app_name.txt
drwxr-xr-x 8 mike users 4096 Sep 12 13:55 assets
-rw-r--r-- 1 mike users 66 Jul 18 11:15 botify_token.txt
drwxr-xr-x 16 mike users 4096 Sep 9 19:30 browser_automation
drwxr-xr-x 11 mike users 4096 Sep 4 20:20 client
-rw-r--r-- 1 mike users 21047 Sep 9 19:22 cli.py
-rw-r--r-- 1 mike users 14929 Sep 8 05:31 config.py
drwxr-xr-x 5 mike users 4096 Sep 12 14:01 cruft
drwxr-xr-x 4 mike users 4096 Sep 12 14:01 data
-rw-r--r-- 1 mike users 9516 Sep 8 05:31 demo.json
-rw-r--r-- 1 mike users 7196 Jul 9 11:47 discover_mcp_tools.py
drwxr-xr-x 2 mike users 4096 Jul 8 11:50 dist
drwxr-xr-x 9 mike users 4096 Jul 18 11:28 downloads
-rw-r--r-- 1 mike users 215294 May 11 12:04 favicon.ico
-rw-r--r-- 1 mike users 1497 Sep 12 14:17 flake.lock
-rw-r--r-- 1 mike users 29141 Sep 12 13:59 flake.nix
drwxr-xr-x 7 mike users 4096 Sep 12 14:28 .git
-rw-r--r-- 1 mike users 70 May 11 12:04 .gitattributes
-rw-r--r-- 1 mike users 1992 Sep 12 13:59 .gitignore
-rw-r--r-- 1 mike users 926 Sep 12 13:16 hello_world.ipynb
drwxr-xr-x 17 mike users 4096 Sep 9 19:29 helpers
-rw-r--r-- 1 mike users 1556 Jul 12 13:58 __init__.py
-rw-r--r-- 1 mike users 9517 Sep 8 05:31 install.sh
drwxr-xr-x 2 mike users 4096 Sep 12 13:17 .ipynb_checkpoints
drwxr-xr-x 4 mike users 4096 Jun 14 07:49 .jupyter
-rw-r--r-- 1 mike users 1073 Jul 9 11:33 LICENSE
drwxr-xr-x 2 mike users 4096 Sep 12 14:18 logs
-rw-r--r-- 1 mike users 218622 Sep 9 19:30 mcp_tools.py
drwxr-xr-x 5 mike users 4096 Sep 12 14:02 modules
drwxr-xr-x 2 mike users 4096 Jul 8 11:50 pipulate.egg-info
drwxr-xr-x 3 mike users 4096 Sep 9 19:31 plugins
drwxr-xr-x 2 mike users 4096 Sep 9 19:48 __pycache__
-rw-r--r-- 1 mike users 2277 Sep 9 17:59 pyproject.toml
-rw-r--r-- 1 mike users 104465 Sep 12 13:05 README.md
-rw-r--r-- 1 mike users 5957 Sep 9 15:41 requirements.txt
-rw-r--r-- 1 mike users 377450 Sep 9 19:25 server.py
-rw-r--r-- 1 mike users 36 May 11 12:06 .sesskey
drwxr-xr-x 3 mike users 4096 Sep 9 19:30 tools
drwxr-xr-x 3 mike users 4096 Sep 4 20:20 training
drwxr-xr-x 7 mike users 4096 Jul 13 17:19 .venv
[mike@nixos:~/repos/pipulate]$
It’s also important to note that to be able to git push
up to main again
after this I had to do the 1-time:
git push -u origin main
Alright, this is where I have to give my brain a little rest. Get some food.
Take a break and come back fresh. I can really feel that the urgency of getting
a stronger and stronger mental model around all this, what files are what and
what files are where is taking a priority over home hosting again, especially as
I dive down into the helpers
and tools
directory.
The other day I moved almost everything that gets imported as a Python module by
server.py
into the modules
folder with the exception of a few things left
out on the repo root, like mcp_tools.py
. But that files has some conceptual
overlap with the tools
directory where I tied deconstructing my MCP tools into
a plugin system but failed, and that is the remains now which AI is telling me
is a dependency.
I feel I want to go all-in one way or the other getting rid of tools
and
making it one big file in mcp_tools.py
or going the other way and eliminating
mcp_tools
in favor of the plugin system, if it’s still actually working
properly. That’s the level of loss of control that happened deferring too much
to AI coding assistance over the months.
The magic rolling pin procedure instead of being yet another AI-deferred task (which I tried and failed) it will instead be a process of me regaining a tight grip on the codebase reins.
Book Analysis
Crafting Compelling Narratives: Title & Filename Brainstorm:
- Title Option 1: Git Branch Takeover: A Solo Developer’s Guide to Promoting a Feature Branch
- Filename:
git-branch-takeover-promote-feature-branch.md
- Rationale: This title is highly practical and keyword-rich, directly targeting a common and often confusing task for developers. It’s an excellent, discoverable title for a technical “how-to” guide.
- Filename:
- Title Option 2: From Clutter to Clean: A Command-Line Workflow for Repo Hygiene
- Filename:
git-command-line-repo-hygiene-cleanup.md
- Rationale: This title focuses on the outcome—a clean repository—and the method. It appeals to developers who feel overwhelmed by branch clutter and are looking for a systematic, command-line-based solution.
- Filename:
- Title Option 3: Taking Back the Reins: Internalizing Git for Codebase Control
- Filename:
internalizing-git-codebase-control.md
- Rationale: This is a more philosophical title that captures the author’s core motivation. It resonates with developers who feel they’ve become too reliant on GUI tools or AI assistants and want to build a deeper, more fundamental understanding of their tools.
- Filename:
- Preferred Option:
- Title (plain text for YAML): Git Branch Takeover: A Solo Developer’s Guide to Promoting a Feature Branch
- Filename:
git-branch-takeover-promote-feature-branch.md
- Rationale: It is the most direct and useful title for a book chapter. It clearly states the problem and the solution, making it an invaluable, actionable resource for any developer, especially those working alone who need a safe and repeatable process for managing their main branch.
Book Potential Analysis:
- Strengths as Book Fodder:
- High-Value, Common Problem: The entry provides a clear, step-by-step solution to a frequent and intimidating problem for many developers: how to safely replace the main branch with a feature branch.
- Authentic Learning Process: It captures the author’s explicit goal of moving from “AI-assisted” to “internalized” knowledge, which is a highly relatable narrative for developers in the current AI-heavy landscape.
- Complete, End-to-End Workflow: It doesn’t just show one command; it presents a full workflow from a small code refactor, to a major branch promotion, to the final cleanup of dozens of stale branches, including debugging the process.
- Safe and Repeatable Commands: The provided one-liner commands for bulk branch deletion are extremely practical, powerful, and immediately useful for any developer looking to clean up their repositories.
- Opportunities for Enrichment (for Book Adaptation):
- Diagram the Branch Takeover: Create a simple “before and after” Git graph diagram. The “before” would show
origin/main
andorigin/modularize
as divergent branches. The “after” would show the oldmain
gone and the newmain
pointing to themodularize
commit history. - Add a “Why Not Rebase or Merge?” Sidebar: Briefly explain the common alternatives (
git merge
andgit rebase
) and articulate precisely why the author’s “rename and force push” method was the correct choice for this specific solo-developer context, clarifying the trade-offs. - Create a “Git Hygiene” Checklist: Distill the cleanup actions into a reusable checklist for developers. This would include steps like “Regularly prune merged local branches,” “Delete remote branches after merging PRs,” and “Periodically review and delete stale experimental branches.”
- Diagram the Branch Takeover: Create a simple “before and after” Git graph diagram. The “before” would show
AI Editorial Perspective: From Journal to Chapter:
This entry is a perfect case study for a chapter on “Practical Version Control for the Solo Developer.” It demystifies a set of Git operations that are often presented with confusing complexity or dire warnings, and reframes them as a safe, powerful workflow when applied in the right context. The narrative strength lies in the author’s explicit desire to build “muscle memory.” This transforms a dry technical guide into a story about skill acquisition and empowerment.
The human-AI dialogue is particularly effective here. The AI acts as a Socratic mentor, providing the precise commands needed, while the human provides the crucial context, intent, and then works through the entire process, including the predictable follow-up problems (like how to delete remote branches after local ones are gone). This interaction is a realistic model of how developers can use AI as a tool to accelerate learning and build confidence. The entry’s raw, step-by-step log of commands and their outputs is its greatest asset, serving as an authentic, “over-the-shoulder” lesson that is far more impactful than a sanitized tutorial.
Suggested Next AI Processing Steps:
- Task Suggestion 1: Generate a Visual Metaphor for the Git Process.
- Potential Prompt Snippet for Next AI: “Read the description of the ‘branch takeover’ in the text. Create a simple, non-technical analogy to explain the process. For example, ‘It’s like replacing the master blueprint for a house (the main branch) with a completely revised blueprint from a side project (the feature branch), then shredding all other old drafts.’”
- Task Suggestion 2: Create a “Safety and Context” Advisory Box.
- Potential Prompt Snippet for Next AI: “Based on the git commands for force-pushing and deleting branches, write a concise ‘Warning’ or ‘Use With Caution’ advisory box. Explain in what specific situations (e.g., collaborative projects with shared branches) this workflow would be dangerous and should be avoided, contrasting it with the solo-developer context where it is safe.”