<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged git</title>
<link>https://coderlegion.com/tag/git</link>
<description>Powered by Question2Answer</description>
<item>
<title>No, Git Isn't Dead. Anthropic's Robots Just Needed a Bigger One.</title>
<link>https://coderlegion.com/33058/no-git-isnt-dead-anthropics-robots-just-needed-a-biggerone</link>
<description>&lt;p&gt;The viral version says Git died in 2026. &lt;/p&gt;
&lt;p&gt;The real story is funnier: seven humans spent five months building a version control system so their AI could keep merging 30,000 changes a day and it works by banning the two features Git is most famous for.&lt;/p&gt;
&lt;p&gt;A post went around this week with the energy of a tabloid that just found out your marriage is over before you did. &quot;Git is DEAD in 2026?&quot; Two screaming-face emojis. &lt;/p&gt;
&lt;p&gt;Anthropic has replaced Git with its own system called Scion, the robots write too much code, Git simply couldn't keep up, pour one out for the little distributed version control system that could.&lt;/p&gt;
&lt;p&gt;And here's the thing that ruins a perfectly good funeral: the story is basically true. Anthropic did build an in-house source control system called Scion. They did move their internal monorepo off Git.&lt;/p&gt;
&lt;p&gt;They are, by their own numbers, merging more than 30,000 code changes a day. I read their actual engineering write-up, because somebody has to, and it's a genuinely great piece of engineering.&lt;/p&gt;
&lt;p&gt;It's also not a eulogy for Git. It's a note from the office saying the robots need a bigger desk. Let me show you the difference, because the difference is the whole joke.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The clickbait says &quot;Git is dead.&quot; The footnotes say &quot;please do not try this at home.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here's the first thing the viral version conveniently loses. Anthropic goes out of its way to say Scion is not for everyone, is built only for one absurd situation, and is not going to be released. &lt;br&gt;
This is the software equivalent of a Formula 1 team explaining that yes, the car does 370 km/h, and no, you cannot drive it to Big Bazaar.&lt;/p&gt;
&lt;p&gt;So Git isn't dead. Git is fine. Git is running on your laptop right now, serenely, the way it has for twenty years, handling your eleven commits a week without filing a complaint. &lt;br&gt;
What actually happened is narrower and far more interesting: one company hit a scale so ridiculous that a core assumption of Git stopped being free, and they had to pay to make it go away.&lt;/p&gt;
&lt;p&gt;That assumption is the one every Git tutorial sells as the feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;The feature that became the bill&lt;/strong&gt;&lt;/strong&gt;&lt;br&gt;
Git is decentralized. Everyone who clones the repo gets the entire repo - every file, every branch, all of history, the whole cathedral - on their own machine. In 2005 this was revolutionary and correct. &lt;/p&gt;
&lt;p&gt;It's why your internet going out doesn't stop you from committing. It's the thing we all fled Subversion and Perforce to get.&lt;/p&gt;
&lt;p&gt;Now put a number on &quot;the entire repo&quot; at Anthropic scale. Their main branch history is pushing 100 GB. A compressed working copy is over 25 GB. And they run roughly 25 million CI checkouts a day. Do the naive multiplication and you get an estimate of more than 600 petabytes of Git traffic. Daily. To check out code.&lt;/p&gt;
&lt;p&gt;Image: 100 GB history × 25M checkouts/day = 600 PB Press enter or click to view image in full size&lt;br&gt;
Sit with the comedy of that. The beautiful, democratic idea - everybody gets a full copy! - is wonderful right up until your contributors are a swarm of always-online machines that don't need a full copy, don't go on holiday, and want to clone the cathedral every eight seconds. At that point &quot;everybody gets everything&quot; stops being empowerment and starts being a DDoS you're running against yourself, on purpose, with a straight face.&lt;/p&gt;
&lt;p&gt;At peak, Anthropic was babysitting several hundred Git mirrors just to survive its own enthusiasm.&lt;br&gt;
The robots didn't kill Git. The robots just took the one thing we loved about Git - you always have the whole thing - and used it ten million times a day until it showed us the bill.&lt;/p&gt;
&lt;p&gt;Scion, or: we have reinvented Perforce and we feel great about it&lt;br&gt;
So what did seven engineers build over five months? Reader, they went back to the office.&lt;/p&gt;
&lt;p&gt;Scion is centralized. The authoritative servers are backed by Google's Spanner and cloud storage. Clients keep a small bounded cache. Most CI jobs don't even hold a local copy of the repo - they just ask the server over an API, like a civilised person ordering off a menu instead of being handed the entire kitchen.&lt;/p&gt;
&lt;p&gt;Twenty years of the industry sprinting away from centralized version control, and the most advanced AI lab on Earth looked at its robot-generated code tsunami and sprinted straight back, yelling &quot;it's fine, it's different when WE do it.&quot; &lt;/p&gt;
&lt;p&gt;And - annoyingly - it is different when they do it. The wheel of history is round for a reason.&lt;br&gt;
But here's my favourite detail, the one I want cross-stitched on a pillow. In Scion, branches and tags are discouraged.&lt;/p&gt;
&lt;p&gt;Read that again. They built a version control system whose official guidance is to go easy on the two features that, for most of us, ARE version control. The reasoning is airtight - branches and tags are global state every server has to agree on, and global agreement is exactly the thing that doesn't scale when you're content-addressing immutable changesets across three tiers of cache.&lt;/p&gt;
&lt;p&gt;But stripped of the justification, the sentence is pure deadpan poetry: welcome to our new version control system, please try not to control any versions.&lt;br&gt;
The rest is seriously elegant, and I mean that with no snark left over. Commits are &quot;changesets&quot; that store only what changed. Everything is content-addressed and immutable, so any cache or server can answer a request without phoning a friend. &lt;/p&gt;
&lt;p&gt;Checkouts are sparse and virtualized - a job pulls only the files it actually reads. Three tiers: a daemon on every node, regional caches, authoritative servers, each reportedly landing a cache hit rate above 99%. The payoff Anthropic reports: 200× less CPU and memory than the old mirror fleet, checkouts that went from minutes to seconds, and 25% of CI capacity handed back like finding money in a coat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The part that should make every architect a little uneasy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here's the detail the hot-takes skip, and it's the one I can't stop poking. Claude wrote most of Scion. The AI wrote the version control system that exists because the AI writes too much code for the old version control system.&lt;/p&gt;
&lt;p&gt;That's not a dunk. It's an ouroboros, and it's genuinely where we are: the tooling layer is now being rebuilt, in real time, by the thing that broke the tooling layer. The system's workload created the system's solution and also authored it. Somewhere in there is a snake eating its tail while filing a pull request against itself - sorry, merging a changeset, there are no pull requests, we discourage branches.&lt;/p&gt;
&lt;p&gt;And one more quietly radical bit: they dropped the merge queue. The old model - line up, test, then merge - stopped making sense at 30,000 a day, so Scion leans on post-merge testing and rollback instead. Merge first, find out later, roll back if it screamed. Which, if you've watched how agents write code, is also precisely how the agents write code. The tool didn't just scale to the robots. It started thinking like them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steal this (the you-are-not-Anthropic edition)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Architect hat on. The single most expensive mistake you can make with this story is to read &quot;Anthropic replaced Git&quot; and feel even a flicker of FOMO. You do not have this problem. If you build Scion for your fourteen-person team and your 400 MB repo, you have not future-proofed anything; you have cosplayed a scaling crisis and billed it to your runway.&lt;/p&gt;
&lt;p&gt;The transferable lesson isn't centralize your VCS. It's this:&lt;/p&gt;
&lt;p&gt;Your tool's killer feature and your tool's eventual bill are the same line item, read from different distances. &quot;Every clone is a full repo&quot; was the pitch and the tombstone. Audit your own stack: which beloved convenience becomes a catastrophe at 100× your current volume? That's your Git-history-is-100-GB moment, waiting.&lt;/p&gt;
&lt;p&gt;Scale doesn't just break your performance; it breaks your assumptions. Anthropic didn't need a faster Git. They needed to delete the premise that everyone holds everything. Faster is an optimization. Deleting the premise is an architecture.&lt;/p&gt;
&lt;p&gt;When the workload changes species, the tool has to change species. Humans and agents are not the same kind of client. Most of your tools were designed for the first and are about to be flooded by the second. That is the actual headline, and it has nothing to do with Git.&lt;/p&gt;
&lt;p&gt;So no, Git isn't dead. Git got the highest compliment our industry gives: it was pushed until the exact shape of its one big idea became visible, and the only people it broke for were the ones running a code-generation operation the size of a small nation's power grid.&lt;/p&gt;
&lt;p&gt;The robots didn't bury Git.&lt;br&gt;
They just found the edge of the map - and politely, in a five-month sprint, drew a little more of it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hardik&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've spent about two decades building data and AI systems, long enough to have personally fled Perforce for Git and to now watch the industry's most advanced lab speedrun the return trip with a straight face. I write about AI in production: the parts that are genuinely new, and the parts that are just the wheel of history turning one more honest rotation.&lt;br&gt;
If this made you want to go check how big your repo history actually is - follow me here, and clap a few times so Medium shows it to the next person about to build Scion for their to-do app.&lt;/p&gt;
&lt;p&gt;Sources / further reading: Anthropic Engineering, &quot;Creating in-house source control&quot; (the primary source for Scion's architecture, the merge-volume figures, and the self-reported performance numbers). Coverage and analysis: DevOps.com, &quot;Anthropic Builds Its Own Source Control After AI Agents Outgrow Git&quot;; Crypto Briefing, &quot;Anthropic replaces Git with custom source control system Scion&quot;; AlphaSignal, &quot;Anthropic Ditched Git and Built Scion After Claude Caused 12x Merge Surge.&quot; A note on the numbers: the 200× and 25% figures are Anthropic's own, measured against their previous Git mirror fleet - not against Git in general - and are not independently verified. The ~600 PB figure is a hypothetical estimate for naive fetching, not measured traffic. The viral &quot;nearly 100× faster checkouts&quot; is a rounding-up of Anthropic's actual phrasing, &quot;minutes to seconds.&quot; And the separate &quot;20× less memory, 4× faster&quot; stat floating around belongs to a different ingestion-pipeline rewrite, not Scion. The practice is real and impressive; the funeral is editorial.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33058/no-git-isnt-dead-anthropics-robots-just-needed-a-biggerone</guid>
<pubDate>Fri, 30 Oct 2026 07:44:39 +0000</pubDate>
</item>
<item>
<title>You Deleted the .env File From Git. Your Secrets Are Still Public.</title>
<link>https://coderlegion.com/32783/you-deleted-the-env-file-from-git-your-secrets-are-still-public</link>
<description>&lt;p&gt;You notice it during a code review: &lt;code&gt;.env&lt;/code&gt; is in the repository. Your stomach drops. You run &lt;code&gt;git rm .env&lt;/code&gt;, commit with the message &quot;remove env file&quot;, push, and feel the relief of a problem solved.&lt;/p&gt;
&lt;p&gt;It is not solved. The file is gone from your latest commit. It is sitting, in full, in every commit before that one, and anyone who can read the repository can read it.&lt;/p&gt;
&lt;h3 id=&quot;why-deleting-doesn-t-delete&quot;&gt;Why Deleting Doesn't Delete&lt;/h3&gt;
&lt;p&gt;Git is a history book. A new commit adds a page saying &quot;the file no longer exists&quot;. It does not tear out the old pages.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git log --all --full-history -- .env
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that prints any commits, your file is in history. And anyone can go back to it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git show &amp;lt;old-commit&amp;gt;:.env
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That works for a public repository on GitHub, for a coworker's clone, for a fork, and for any tool that scans old commits. Automated bots scan public repositories for exactly this, so a leaked key can be found quickly.&lt;/p&gt;
&lt;h3 id=&quot;step-1-rotate-everything-first&quot;&gt;Step 1: Rotate Everything First&lt;/h3&gt;
&lt;p&gt;This is the part people skip, and it is the only part that actually matters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Treat every secret that was ever in that file as compromised, and replace it:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database passwords and connection strings&lt;/li&gt;
&lt;li&gt;API keys (payment, email, storage, maps, AI)&lt;/li&gt;
&lt;li&gt;Auth secrets such as &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt; or JWT signing keys&lt;/li&gt;
&lt;li&gt;OAuth client secrets&lt;/li&gt;
&lt;li&gt;Webhook signing secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create the new value, update your hosting environment, deploy, and then revoke the old one. Cleaning the history comes after, because cleaning cannot take back what someone already copied.&lt;/p&gt;
&lt;p&gt;Rotating a signing secret logs users out, which is the right outcome. It means the old secret can no longer be used to forge sessions.&lt;/p&gt;
&lt;h3 id=&quot;step-2-find-out-what-else-was-committed&quot;&gt;Step 2: Find Out What Else Was Committed&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; is not the only way secrets end up in Git. Search the whole history for the values themselves:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git log -p --all -S'sk_live' | head -100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-S&lt;/code&gt; finds commits where a string was added or removed. Try the prefixes of keys you use. A dedicated scanner is faster and more thorough:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gitleaks detect --source . --log-opts=&quot;--all&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It checks every commit for patterns that look like keys and tokens, and prints where it found them.&lt;/p&gt;
&lt;h3 id=&quot;step-3-remove-it-from-history&quot;&gt;Step 3: Remove It From History&lt;/h3&gt;
&lt;p&gt;Once the secrets are rotated, you can clean the history so the old file stops being a trap. The current recommended tool is &lt;code&gt;git filter-repo&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git filter-repo --path .env --invert-paths
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This rewrites every commit as if &lt;code&gt;.env&lt;/code&gt; had never been added. Because it changes commit IDs, you then force push:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push --force --all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be honest about the limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Everyone else must re-clone&lt;/strong&gt; or reset, or they will push the old history back.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forks and existing clones keep the old data.&lt;/strong&gt; You cannot reach into them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosting platforms may keep cached copies&lt;/strong&gt; of old commits for a while. Check their guidance on removing sensitive data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is why step 1 comes first. History rewriting is cleanup, and rotation is the fix.&lt;/p&gt;
&lt;h3 id=&quot;the-leak-that-has-nothing-to-do-with-git&quot;&gt;The Leak That Has Nothing to Do With Git&lt;/h3&gt;
&lt;p&gt;Next.js developers have a second way to leak a secret, and no commit is involved:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# .env.local
NEXT_PUBLIC_STRIPE_SECRET_KEY=sk_live_...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any variable that starts with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; is &lt;strong&gt;inlined into the JavaScript sent to the browser&lt;/strong&gt; at build time. Anyone can open DevTools, read your bundle and find the value. The prefix means &quot;this is safe to publish&quot;, and it does exactly that.&lt;/p&gt;
&lt;p&gt;Rules that keep you safe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only values that are &lt;strong&gt;meant to be public&lt;/strong&gt; get the &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix, such as a public analytics ID or a publishable key.&lt;/li&gt;
&lt;li&gt;Secret keys have &lt;strong&gt;no prefix&lt;/strong&gt; and are read only in Server Components, Route Handlers and Server Actions.&lt;/li&gt;
&lt;li&gt;If a secret ever got that prefix, assume it is exposed and rotate it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;make-it-hard-to-repeat&quot;&gt;Make It Hard to Repeat&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ignore the file before the first commit.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gitignore&quot;&gt;.env
.env.*
!.env.example
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Commit a &lt;code&gt;.env.example&lt;/code&gt; with the variable names and fake values, so teammates know what to set, and nothing real is ever in the repository.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scan before commits leave your machine.&lt;/strong&gt; A pre-commit hook with a scanner like gitleaks blocks a commit that contains something key-shaped. Hosting your code on GitHub also gives you secret scanning and push protection for many known key formats. Turn both on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fail loudly when a variable is missing.&lt;/strong&gt; Check required variables at startup, so a missing secret becomes an error message, not a silent fallback to a hardcoded value that someone then commits.&lt;/p&gt;
&lt;h3 id=&quot;the-rule&quot;&gt;The Rule&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Once a secret has been in a commit, it is public forever. The fix is rotation, not deletion.&lt;/strong&gt; Rotate first, clean the history second, and add a scanner so the next mistake is caught before it leaves your laptop.&lt;/p&gt;
&lt;p&gt;Quick audit, right now, in your own project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git log --all --full-history -- .env .env.local .env.production
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that prints anything, you have found a rotation task.&lt;/p&gt;
&lt;p&gt;If you are starting a new Next.js project and want the structure set up sensibly from day one, you can browse my &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.com&quot;&gt;templates on pixelanas.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get the templates:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.gumroad.com&quot;&gt;https://pixelanas.gumroad.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Have you ever committed a secret by accident, and how fast did you notice? Tell me in the comments, and no judgment, this happens to nearly everyone once.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Anas, full-stack Next.js developer building SaaS products and premium templates. X: &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/ASheikh69751&quot;&gt;@ASheikh69751&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32783/you-deleted-the-env-file-from-git-your-secrets-are-still-public</guid>
<pubDate>Wed, 28 Oct 2026 08:06:21 +0000</pubDate>
</item>
<item>
<title>Oracle's Fusion Setup Registry Puts Configuration in a Public Git Repo for AI Agents</title>
<link>https://coderlegion.com/32584/oracles-fusion-setup-registry-puts-configuration-in-a-public-git-repo-for-ai-agents</link>
<description>&lt;h2 id=&quot;section&quot;&gt;&lt;/h2&gt;
&lt;p&gt;Setting up an enterprise application is slow work, and much of it gets repeated. Teams work out which settings depend on which, put the steps in the right order, test the result, and then do it all again for the next customer. The knowledge sits in documents that go stale with every release.&lt;/p&gt;
&lt;p&gt;On Monday at Oracle AI World in Las Vegas, Oracle said it wants to move that knowledge into code. The company published the Oracle Fusion Setup and Configuration Registry, or FSCR, as a public Git repository. It holds Oracle's setup and configuration expertise as machine-readable skills that AI agents can run.&lt;/p&gt;
&lt;p&gt;According to Oracle, the skills cover configuration options, dependencies, sequencing, execution, testing, and validation. An agent can take business requirements or an existing configuration and turn them into a runbook. It then makes the changes in Oracle Fusion Cloud Applications and tests the results. Human reviewers check the finished configuration, ask for adjustments, and approve it for deployment. Oracle says assistants and coding agents from OpenAI and others can use the registry.&lt;/p&gt;
&lt;p&gt;Chris Leone, Oracle's executive vice president of Applications Development, said during the keynote that his team will own the registry. It will be updated with every release and with any patch that affects setup. He also said the skills warn you before you change an object that can't be reversed. &quot;Agents will be the operator, and the registry will be the authority,&quot; he said.&lt;/p&gt;
&lt;p&gt;Leone put it this way: &quot;The Oracle Fusion Setup and Configuration Registry is like having an expert Oracle product manager guiding teams through the entire setup and configuration process.&quot; The release also carries supporting statements from Accenture Edge, Deloitte, PwC, Grant Thornton, and Apex IT. Each describes building its own accelerators on top of the registry.&lt;/p&gt;
&lt;p&gt;The registry feeds Oracle AI Success Navigator, which now includes six agents: discovery, design, configuration, testing, integration, and innovation. Lexington Health, an early adopter, said the approach has cut certain design and configuration work by 80 percent during implementation. That is one customer's figure. Oracle didn't share a broader benchmark.&lt;/p&gt;
&lt;p&gt;The keynote also showed what the agents do once they're running. Oracle calls its runtime Fusion Claw. It handles long-running, multi-step work such as reconciliation, analysis, and optimization, and Oracle says it supports continuous replanning and deterministic execution. Oracle says 25 Claw-powered agentic applications are available now across ERP, HCM, supply chain, and customer experience. Leone said Oracle is shipping 75 packaged agentic applications in total.&lt;/p&gt;
&lt;p&gt;The live demo was a shipping load planner. The agent started with trucks at 61 percent utilization and proposed a plan to reach 88 percent, saving $58,000. It flagged five loads for a planner to review and cleared the other 149. After Leone approved, it rechecked the current state of the system before pulling in 117 orders and making the changes. Then it produced what he called an outcome receipt: who did the work, what actions were taken, and which standard operating procedures it followed. &quot;AI with receipts,&quot; he said.&lt;/p&gt;
&lt;p&gt;For developers, Oracle says customers and partners can build their own agents and agentic applications in Oracle AI Agent Studio for Fusion Applications, using no-code or pro-code tools. The release lists built-in observability, ROI measurement, and safety controls.&lt;/p&gt;
&lt;p&gt;Oracle also announced Fusion Companion, which lets people work with Fusion through supported AI tools. It ships first as a plugin in OpenAI's marketplace for ChatGPT, with more AI clients planned. Companion decides whether a request becomes a transaction, an agent, an agentic application, or a long-running Claw job, and it applies the user's permissions. A second piece, Fusion Agentic Home, is a native mobile experience for Android and iOS that is voice-first.&lt;/p&gt;
&lt;p&gt;The most useful material for builders came from the customers. QSRP, which Oracle says runs about 1,500 restaurants, built agents with Oracle for product availability, loss prevention, and delivery claims. In one demo, an agent grouped failed orders across stores and traced them to a single missing configuration in an Uber Eats integration. After approval, it fixed every affected store in one pass.&lt;/p&gt;
&lt;p&gt;Sophie Buresi, QSRP's head of AI, said the pilot confirmed two things. &quot;The hard part is not so much about AI. If you have the right data, AI is pretty simple,&quot; she said. The other is that operations teams have to trust a tool before they use it. To go from a few restaurants to hundreds, she said QSRP needs reliable data integration with its platform, results measured against the business case, and trust from the people running the restaurants.&lt;/p&gt;
&lt;p&gt;Spectrum's approach was similar. Derek Dreyfus, the company's VP of HR technology and AI strategy, said his team treats governance as an accelerator. If an answer is repeatable and governed, an agent handles it. Anything involving a pay change, a process exception, or something sensitive in an employee's record goes to a person.&lt;/p&gt;
&lt;p&gt;Oracle CEO Mike Sicilia built the whole keynote around trust. &quot;Trust isn't something we can announce; it's something that we must earn,&quot; he said. For developers, trust shows up as things you can inspect: a receipt for every action, a threshold that sends exceptions to a human, and a registry in Git that you can read before you commit to it.&lt;/p&gt;
&lt;p&gt;Oracle's materials leave some questions open. They don't cover licensing terms for the registry, how partner extensions will be vetted, or pricing for the Claw-powered applications. The best test is whether agents configure Fusion from the registry as cleanly as the demo suggested. Because the repository is public, teams can read the skills and check that for themselves.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32584/oracles-fusion-setup-registry-puts-configuration-in-a-public-git-repo-for-ai-agents</guid>
<pubDate>Mon, 26 Oct 2026 18:42:12 +0000</pubDate>
</item>
<item>
<title>GitHub Classroom has closed: what you still have, and how to run next term</title>
<link>https://coderlegion.com/32569/github-classroom-has-closed-what-you-still-have-and-how-to-run-next-term</link>
<description>&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a rel=&quot;nofollow&quot; href=&quot;https://portal.uppcoder.com/github-classroom-alternative?utm_source=coderlegion&amp;amp;utm_medium=blog&amp;amp;utm_campaign=posts&amp;amp;utm_content=post-146&quot;&gt;Uppcoder&lt;/a&gt;, one of the options listed at the end. The rest is about GitHub Classroom itself and applies whatever you choose.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;GitHub retired GitHub Classroom on 28 August 2026. If your course ran on it, here is what is still there, what is gone, and what your choices are for the next term.&lt;/p&gt;
&lt;h3 id=&quot;what-happened-and-when&quot;&gt;What happened, and when&lt;/h3&gt;
&lt;p&gt;GitHub closed new sign-ups to GitHub Classroom in May and retired it at the end of August. It now points educators to two partners: Classroom 50, free and open source from the Fifty Foundation, the nonprofit behind CS50, and Codio, a commercial platform.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;26 May 2026: new sign-ups to GitHub Classroom closed&lt;/li&gt;
&lt;li&gt;28 August 2026: GitHub Classroom retired; its website, APIs and related services decommissioned&lt;/li&gt;
&lt;li&gt;GitHub's recommended partners: Classroom 50 and Codio&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;what-you-still-have&quot;&gt;What you still have&lt;/h3&gt;
&lt;p&gt;GitHub says that GitHub accounts, repositories and organisations created to work with Classroom are not affected. Everything that lived in a repository is still where it was.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your classroom organisation&lt;/li&gt;
&lt;li&gt;Every student repository Classroom created, with its full commit history&lt;/li&gt;
&lt;li&gt;Your template (starter) repositories&lt;/li&gt;
&lt;li&gt;Any tests kept inside a repository, for example in a tests folder or a GitHub Actions workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;what-is-gone-or-going&quot;&gt;What is gone, or going&lt;/h3&gt;
&lt;p&gt;GitHub says some Classroom data is being permanently deleted under its retention policies. GitHub's Classroom Export Utility downloaded this data as JSON and CSV files, but it worked through the Classroom APIs, which GitHub says are now decommissioned. If you ran it before 28 August, keep those files; if you did not, plan to rebuild what is listed here.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classroom and assignment names&lt;/li&gt;
&lt;li&gt;Tests defined in Classroom itself rather than in a repository (autograding set up on the Classroom website)&lt;/li&gt;
&lt;li&gt;LMS-integrated rosters: the LTI link between Classroom and Moodle, Canvas and the rest&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;finding-your-students-repositories&quot;&gt;Finding your students' repositories&lt;/h3&gt;
&lt;p&gt;Classroom named each student repository after the assignment and the student's GitHub username, so the repositories of one assignment share a prefix. With the GitHub CLI (gh) you can list them, and clone them all into one folder. The roster that matched usernames to students was Classroom data; rebuild it from your LMS class list or your own records if you need it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# list one assignment's repositories
gh repo list YOUR-ORG --limit 1000 --json name -q '.[].name' | grep '^assignment-prefix-'

# clone them all into the current folder
gh repo list YOUR-ORG --limit 1000 --json name -q '.[].name' | grep '^assignment-prefix-' \
  | xargs -I{} gh repo clone YOUR-ORG/{}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;before-next-term-a-checklist&quot;&gt;Before next term: a checklist&lt;/h3&gt;
&lt;p&gt;Whatever you use next, these steps keep last term's work safe and make the next one easier to set up.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep the organisation. Archive last term's student repositories rather than deleting them, if your school's retention rules allow it&lt;/li&gt;
&lt;li&gt;Move tests into the template repositories, in a tests folder, so they travel with the assignment whatever tool you use next&lt;/li&gt;
&lt;li&gt;Write down the assignment list with each template repository and its due dates, since the names in Classroom are being deleted&lt;/li&gt;
&lt;li&gt;Check your LMS: if Classroom was linked to Moodle, Canvas, Blackboard or Brightspace, remove the old activity so students don't click into a dead end&lt;/li&gt;
&lt;li&gt;Pick the replacement before the term starts and try one assignment end to end with a test student account&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-options-for-next-term&quot;&gt;The options for next term&lt;/h3&gt;
&lt;p&gt;Each takes a different approach; which fits depends on your course, your school's rules on student data, and how much you want to run yourself.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classroom 50 (free, open source, the Fifty Foundation): closest to GitHub Classroom, with assignments from template repositories, autograding through GitHub Actions, rosters and submissions, on GitHub. Needs an organisation on a GitHub Team or Enterprise plan (free for verified educators through GitHub Education) and a token with read and write access to the organisation's repositories&lt;/li&gt;
&lt;li&gt;Codio (commercial, a GitHub partner): a hosted platform for coding courses with its own content, environments and autograding, and migration guides for GitHub Classroom users&lt;/li&gt;
&lt;li&gt;Do it yourself with the GitHub CLI: for small courses, create the student repositories from a template with gh and a list of usernames, and mark by hand or with your own GitHub Actions; community tools shared in GitHub's own discussion of the change do this for you&lt;/li&gt;
&lt;li&gt;Uppcoder (that's us; commercial, with a free tier): assignments are git repositories with starter code, tests and a solution; every student works in the same ready environment in the cloud, so nobody installs anything; tests mark each hand-in and you mark the rest by rubric; hand-ins can stay in your own GitHub organisation; it works inside Moodle, Canvas, Blackboard and Brightspace over LTI 1.3, or on its own; &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;If you choose Uppcoder:&lt;/strong&gt; for teachers moving from GitHub Classroom we move one course across for you, up to ten of its assignments, and it runs free until 30 June 2027 (first 20 courses, one per teacher, up to 40 students, apply by 15 December 2026). Details: &lt;a rel=&quot;nofollow&quot; href=&quot;https://portal.uppcoder.com/github-classroom-alternative?utm_source=coderlegion&amp;amp;utm_medium=blog&amp;amp;utm_campaign=posts&amp;amp;utm_content=post-146&quot;&gt;https://portal.uppcoder.com/github-classroom-alternative?utm_source=coderlegion&amp;amp;utm_medium=blog&amp;amp;utm_campaign=posts&amp;amp;utm_content=post-146&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.blog/changelog/2026-08-27-github-classroom-deprecated&quot;&gt;GitHub Changelog, 27 August 2026: GitHub Classroom deprecated&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.blog/changelog/2026-05-26-github-classroom-sign-ups-are-no-longer-available&quot;&gt;GitHub Changelog, 26 May 2026: GitHub Classroom sign-ups are no longer available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/education/manage-coursework-with-github-classroom/get-started-with-github-classroom/export-or-migrate-github-classroom-data&quot;&gt;GitHub Docs: Export or migrate GitHub Classroom data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/orgs/community/discussions/196615&quot;&gt;GitHub Community: the educators' announcement thread&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://portal.uppcoder.com/github-classroom-closed?utm_source=coderlegion&amp;amp;utm_medium=blog&amp;amp;utm_campaign=posts&amp;amp;utm_content=post-146&quot;&gt;https://portal.uppcoder.com/github-classroom-closed?utm_source=coderlegion&amp;amp;utm_medium=blog&amp;amp;utm_campaign=posts&amp;amp;utm_content=post-146&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32569/github-classroom-has-closed-what-you-still-have-and-how-to-run-next-term</guid>
<pubDate>Mon, 26 Oct 2026 16:09:06 +0000</pubDate>
</item>
<item>
<title>What AI Coding Loses That Git History Can't Tell You — B Donald Harris</title>
<link>https://coderlegion.com/32458/what-ai-coding-loses-that-git-history-cant-tell-you-b-donald-harris</link>
<description>&lt;p&gt;&lt;strong&gt;AI coding agents can generate code faster than ever—but can developers still explain why that code exists?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this CoderLegion Developer Spotlight, software architect and founder &lt;strong&gt;B Donald Harris&lt;/strong&gt; shares how AI-assisted development has reignited his passion for coding, why traditional Git history doesn't tell the whole story, and how he's building &lt;strong&gt;Hindsight&lt;/strong&gt; to help developers understand the decisions, conversations, and reasoning behind their software.&lt;/p&gt;
&lt;p&gt;From discovering programming on a Commodore 64 at age nine to exploring AI agentic workflows after more than two decades in software development, Donald offers a thoughtful perspective on the changing role of developers—and why the joy of building still matters.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;meet-b-donald-harris&quot;&gt;Meet B Donald Harris&lt;/h3&gt;
&lt;p&gt;B Donald Harris is a software architect and founder based in Tulsa, Oklahoma. He has spent more than two decades building software, primarily in .NET and cloud technologies, and continues to explore new ways to build, learn, and collaborate.&lt;/p&gt;
&lt;p&gt;His current work includes &lt;strong&gt;Hindsight&lt;/strong&gt;, a workflow intelligence tool intended to help developers reconstruct how a software project evolved and understand why particular decisions were made.&lt;/p&gt;
&lt;p&gt;Donald is also behind &lt;strong&gt;BIT Voices&lt;/strong&gt;, a podcast and builder network created to showcase Black technology professionals, share their stories, and help builders discover and connect with one another.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connect with Donald on CoderLegion:&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/user/BDonaldTechnologist&quot;&gt;https://coderlegion.com/user/BDonaldTechnologist&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-ai-coding-can-leave-developers-with-unanswered-questions&quot;&gt;Why AI coding can leave developers with unanswered questions&lt;/h3&gt;
&lt;p&gt;AI coding agents can produce substantial amounts of code, help implement features, and accelerate development. But faster implementation creates another challenge: keeping track of everything that has changed and understanding the reasoning behind those changes.&lt;/p&gt;
&lt;p&gt;As Donald explains in the interview, experienced developers might not remember every line of code they've written, but they can often recall the overall system design and know where to look when something goes wrong.&lt;/p&gt;
&lt;p&gt;With agentic development, that understanding can become harder to maintain as changes and commits accumulate quickly.&lt;/p&gt;
&lt;p&gt;A Git history may show that a feature was added, a bug was fixed, or an implementation changed. What it might not explain is &lt;strong&gt;why the team made that decision in the first place&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The missing context can live in conversations with AI agents, prompts, discussions between developers, terminal sessions, notes, and the reasoning behind architectural choices.&lt;/p&gt;
&lt;p&gt;That is the problem Donald wants Hindsight to help solve.&lt;/p&gt;
&lt;h3 id=&quot;hindsight-remembering-the-story-behind-software&quot;&gt;Hindsight: Remembering the story behind software&lt;/h3&gt;
&lt;p&gt;Hindsight is a workflow intelligence tool Donald is building to help developers make sense of how their projects have evolved.&lt;/p&gt;
&lt;p&gt;The concept is to connect a repository and bring together relevant development information, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repository history, pull requests, and issues.&lt;/li&gt;
&lt;li&gt;AI conversations and prompting sessions.&lt;/li&gt;
&lt;li&gt;Terminal sessions and development notes.&lt;/li&gt;
&lt;li&gt;Decisions made during implementation and debugging.&lt;/li&gt;
&lt;li&gt;Changes in direction and the reasoning behind them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By working with this information and a language model, developers could build a more complete account of their project's development journey.&lt;/p&gt;
&lt;p&gt;Rather than relying exclusively on commit messages and code changes, the aim is to preserve the context that helps explain what happened, why it happened, and how the software reached its current state.&lt;/p&gt;
&lt;p&gt;Donald describes the project as being in development and experimentation during the interview, with an MVP alpha targeted for the beginning of the following year.&lt;/p&gt;
&lt;p&gt;He also emphasizes that privacy and control over what information is captured and seen are important considerations for builders using such a tool.&lt;/p&gt;
&lt;h3 id=&quot;how-ai-has-changed-donald-s-approach-to-development&quot;&gt;How AI has changed Donald's approach to development&lt;/h3&gt;
&lt;p&gt;One of the most interesting moments in the conversation comes when Donald reflects on how AI coding agents have affected his relationship with programming.&lt;/p&gt;
&lt;p&gt;He started coding at nine after his father brought home a Commodore 64. Using a book of BASIC programs, he would type instructions and watch something happen on the computer. That experience felt like magic, and it sparked a lifelong interest in building software.&lt;/p&gt;
&lt;p&gt;Over time, professional responsibilities and other commitments meant that coding sometimes became something he did primarily for work rather than for fun.&lt;/p&gt;
&lt;p&gt;AI-assisted development has helped bring back that original excitement.&lt;/p&gt;
&lt;p&gt;With agents taking on much of the implementation work, Donald can spend more time thinking about architecture, orchestrating agents, and bringing ideas to life—ideas he might not previously have had the time to explore.&lt;/p&gt;
&lt;p&gt;His perspective is not that developers should abandon their skills or stop understanding their code. Instead, he sees AI as another step in the evolution of computing, much like earlier layers of abstraction that made software development possible.&lt;/p&gt;
&lt;p&gt;The challenge now is learning how to work effectively with these tools while maintaining control over the systems being built.&lt;/p&gt;
&lt;h3 id=&quot;from-a-net-developer-tool-to-ai-agentic-workflows&quot;&gt;From a .NET developer tool to AI agentic workflows&lt;/h3&gt;
&lt;p&gt;Donald also discusses a VS Code extension he created to simplify .NET project creation.&lt;/p&gt;
&lt;p&gt;While exploring a move from Visual Studio to VS Code at work, he found that starting certain .NET projects required a sequence of command-line operations: creating solutions, creating projects, and adding projects to solutions.&lt;/p&gt;
&lt;p&gt;He wanted a simpler workflow, so he explored the VS Code extension architecture and built a tool that automated those steps.&lt;/p&gt;
&lt;p&gt;That project also became his first introduction to agentic workflows and helped shape his interest in how new tools could make development easier.&lt;/p&gt;
&lt;p&gt;The experience reflects a recurring theme in Donald's work: identifying friction in the development process and finding practical ways to remove it.&lt;/p&gt;
&lt;h3 id=&quot;building-bit-voices-and-creating-opportunities-for-others&quot;&gt;Building BIT Voices and creating opportunities for others&lt;/h3&gt;
&lt;p&gt;Donald’s work extends beyond software tools.&lt;/p&gt;
&lt;p&gt;Through the BIT Voices podcast, he provides a platform for Black technology professionals to share their experiences, work, and ideas. The accompanying BIT Voices Network is intended to help builders discover one another, showcase projects, connect, and potentially collaborate.&lt;/p&gt;
&lt;p&gt;Donald explains that representation matters because technology professionals can be geographically and organizationally dispersed, sometimes with few opportunities to connect with others who share similar experiences.&lt;/p&gt;
&lt;p&gt;He wants younger generations to see people like themselves building careers and creating opportunities in technology.&lt;/p&gt;
&lt;p&gt;His decision to become more visible as a public builder is part of that mission: sharing his own work while helping others tell their stories.&lt;/p&gt;
&lt;h3 id=&quot;lessons-from-building-products-and-communities&quot;&gt;Lessons from building products and communities&lt;/h3&gt;
&lt;p&gt;The interview touches on several lessons that extend beyond AI tooling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Don't lose the joy of building.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Donald says the advice he wishes he had received earlier in his career was to never stop doing software development for fun. Personal projects can provide room to experiment and rediscover the excitement that first drew someone to programming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Understand the reasoning, not just the implementation.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Code and commits tell part of a software project's story. The discussions, decisions, and context behind them can be equally important when maintaining or extending a system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Learn to embrace technological change.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Donald sees AI as another stage in computing's evolution. Rather than dismissing it, developers can learn how to use it effectively while continuing to apply their own judgment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Pay attention to how the software actually works.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Building and testing a product can reveal workflows that differ from the assumptions made during initial design. Donald emphasizes learning from those discoveries rather than rigidly following preconceived ideas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Start building.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;His advice to developers who want to create their own products is straightforward: start. Developers already have many of the skills needed to outline a project and begin turning an idea into something real.&lt;/p&gt;
&lt;h3 id=&quot;watch-the-full-conversation&quot;&gt;Watch the full conversation&lt;/h3&gt;
&lt;p&gt;In this CoderLegion Developer Spotlight, Donald and host Tom Smith explore AI-assisted development, software architecture, Hindsight, the importance of developer context, and the work involved in building products and communities.&lt;/p&gt;
&lt;p&gt;The conversation is particularly relevant for developers experimenting with AI coding agents and thinking about how to maintain a deeper understanding of increasingly complex codebases.&lt;/p&gt;
&lt;h3 id=&quot;about-the-interviewer-tom-smith&quot;&gt;About the interviewer: Tom Smith&lt;/h3&gt;
&lt;p&gt;Tom Smith hosts this CoderLegion Developer Spotlight, speaking with developers and builders about their experiences, technical decisions, projects, and lessons learned along the way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connect with Tom on CoderLegion:&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/user/Tom+Smith&quot;&gt;https://coderlegion.com/user/Tom Smith&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;about-coderlegion-dev-stories&quot;&gt;About CoderLegion Dev Stories&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CoderLegion Dev Stories&lt;/strong&gt; shares the experiences behind software development: the people building products, the problems they solve, the decisions they make, and the lessons they learn.&lt;/p&gt;
&lt;p&gt;If you're building something interesting, exploring a new technology, or learning from real-world development challenges, consider sharing your story with the CoderLegion community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explore CoderLegion:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com&quot;&gt;https://coderlegion.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Keep building, keep sharing, and keep learning from one another.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;tags&quot;&gt;Tags&lt;/h4&gt;
&lt;p&gt;AI, AI Coding Agents, Software Development, Software Architecture, Developer Tools, Git, .NET, VS Code, Developer Stories, CoderLegion&lt;/p&gt;
</description>
<category>Dev Stories</category>
<guid isPermaLink="true">https://coderlegion.com/32458/what-ai-coding-loses-that-git-history-cant-tell-you-b-donald-harris</guid>
<pubDate>Mon, 26 Oct 2026 03:02:57 +0000</pubDate>
</item>
<item>
<title>surviving-lines scored highest in a six-arm code-ownership benchmark</title>
<link>https://coderlegion.com/32264/surviving-lines-scored-highest-in-a-six-arm-code-ownership-benchmark</link>
<description>&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/Bubblegunn/surviving-lines/launch-assets/benchmark-round13/cover.png&quot; alt=&quot;Benchmark round 13: surviving-lines against five other arms on sealed and public code-ownership tasks&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wrote surviving-lines to answer one question: who wrote the code that is still in this repository? It is a command-line tool that counts each author's surviving lines with &lt;code&gt;git blame&lt;/code&gt; and prints them next to the author's share of commits. It also ships as a Claude Code plugin, so an agent can run it instead of estimating. For version 0.4.0 I wanted to know whether the plugin changes how well an agent answers ownership questions. So I measured it against three other tools and against Claude with no plugin.&lt;/p&gt;
&lt;p&gt;The short result: on 5 sealed tasks the plugin scored 9.67 of 25 and the best other arm scored 7.33. The 95% bootstrap interval of that difference is [+0.33, +4.33], so it stays above zero but only just. A score of 9.67 of 25 also means that the plugin passed fewer than 4 criteria in 10, so these tasks are hard for every arm. On 11 public tasks, which I had read while developing the tool, it scored 47.67 of 51 against 41.00 for the best other arm. It did not win every task, and the details are below.&lt;/p&gt;
&lt;h3 id=&quot;what-was-measured&quot;&gt;What was measured&lt;/h3&gt;
&lt;p&gt;Each task gives an agent a git repository and a question that a person taking over a project would ask, for example &quot;Who should I ask to review changes to time.go, version7.go and the SQL code, and are the people who actually wrote those parts still around?&quot; A task has five criteria or fewer, such as &quot;names this person as the top owner with a share between 51% and 59%&quot; or &quot;does not credit the person who only ran the formatter&quot;. The criteria were written with a ground truth computed from the repository, and a model grader scored each answer against them. The grader gave its reason before its verdict, with five votes per criterion, and the verdict is the majority.&lt;/p&gt;
&lt;p&gt;There are two sets of tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The sealed set has 5 tasks. An agent that had not seen the surviving-lines repository wrote them. It was given a plain description of the user's job and the file layout. Each task uses a real open-source repository in a different ecosystem (Python, Go, Zig, Ruby, PHP) with commits added by the writer that create hazards: a formatter run, the same person under several addresses, squash-merged work, mechanical file moves, an author who differs from the committer.&lt;/li&gt;
&lt;li&gt;The public set has 11 tasks. It uses one small JavaScript repository where the main author has two addresses, asked in several ways (who owns the code, the bus factor of a directory, who should review a file, code owners per directory and so on). The tasks were read while the tool was being developed, so I label the set development-seen. The claim in this article rests on the sealed set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The six arms are surviving-lines at commit 8af57f4, project-pilot 1.0.0 with its natural prompt and again with the prompt &quot;Use the /project-pilot:bus-factor command for this.&quot;, mcp-git-historian, busfactor by k41n, and a Claude Code run with an empty plugin. The other tools were run from copies pinned at the commits listed in the evidence folder. &lt;code&gt;project-pilot 1.0.0, invoked&lt;/code&gt; only has runs on the 8 public tasks that define an invoked prompt, so its public total counts the other 3 tasks as 0. Each arm ran each task three times on Sonnet 5.5 through Claude Code 2.1.308. The protocol was committed before the first run, and no result from an earlier round was reused. Version 0.4.0 on npm ships the same plugin code as that commit: the bin, hooks, agents and schema folders are byte-identical, and only the skill description text and version number differ.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/Bubblegunn/surviving-lines/launch-assets/benchmark-round13/figure.png&quot; alt=&quot;Totals of six arms on the sealed set (5 tasks, of 25) and the public set (11 tasks, of 51), with 95% bootstrap intervals. surviving-lines is highest on both sets. On the sealed set it is 9.67 against 7.33 for project-pilot 1.0.0, invoked.&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;sealed-set&quot;&gt;Sealed set&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;arm &lt;/th&gt;&lt;th&gt;total of 25&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;surviving-lines &lt;/td&gt;&lt;td&gt;9.67&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;project-pilot 1.0.0, invoked &lt;/td&gt;&lt;td&gt;7.33&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;no code-ownership plugin &lt;/td&gt;&lt;td&gt;3.33&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;busfactor (k41n) &lt;/td&gt;&lt;td&gt;2.67&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;project-pilot 1.0.0 &lt;/td&gt;&lt;td&gt;2.33&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;mcp-git-historian &lt;/td&gt;&lt;td&gt;2.00&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A total is the sum over the tasks of the mean criteria passed over the three runs. The difference to the best other arm, &lt;code&gt;project-pilot 1.0.0, invoked&lt;/code&gt;, is +2.33 with a 95% interval of [+0.33, +4.33]. Against the other four arms the differences are between +6.33 and +7.67, and every interval is above zero. With 5 tasks and 3 runs, the lower bound of 0.33 is close to zero, and a different set of tasks could move it either way.&lt;/p&gt;
&lt;p&gt;The protocol also defines stricter checks, and the plugin fails them on this set. On HZ3 it scored 0.67 where &lt;code&gt;project-pilot 1.0.0, invoked&lt;/code&gt; scored 1.33, and on HZ5 it scored 1.33 where the empty-plugin run scored 1.67. The per-task rule (rule B), under which the plugin may not trail the best rival on any task, therefore fails, and so does the rule that the plugin must lose at most 80% of the points the best rival loses: it lost 15.33 of 25 and the rival lost 17.67, where the limit was 14.13. I do not claim it is first on every task.&lt;/p&gt;
&lt;h3 id=&quot;public-set&quot;&gt;Public set&lt;/h3&gt;
&lt;p&gt;On the public set the plugin scored 47.67 of 51. The best other arm was the empty-plugin run at 41.00, and the difference is +6.67 with an interval of [+5.00, +8.33]. Against project-pilot taking the better of its two arms on each task, the difference is +4.33 with an interval of [+2.00, +6.00]. The totals of the other arms are 40.33 (project-pilot 1.0.0), 40.33 (busfactor), 38.33 (mcp-git-historian) and 27.33 (&lt;code&gt;project-pilot 1.0.0, invoked&lt;/code&gt;, with the 3 missing tasks).&lt;/p&gt;
&lt;p&gt;It lost three tasks: T1 and T1i, where it scored 3.00 and busfactor and the empty-plugin run scored 4.00, and E5, where it scored 4.00 and mcp-git-historian scored 5.00. The 80% rule passes on this set. The per-task rule fails on those three tasks. Because these tasks were read during development, a high score here is weaker evidence than the sealed result.&lt;/p&gt;
&lt;h3 id=&quot;other-models&quot;&gt;Other models&lt;/h3&gt;
&lt;p&gt;I ran Haiku 5.5 and Opus 5.5 once per task, with only surviving-lines, the empty-plugin run and the best rival of each task. These totals are descriptive. One run per task is too few to compute an interval, and I make no claim of being first from them.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;model &lt;/th&gt;&lt;th&gt;set &lt;/th&gt;&lt;th&gt;surviving-lines &lt;/th&gt;&lt;th&gt;empty plugin &lt;/th&gt;&lt;th&gt;best rivals, summed per task&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Haiku 5.5 &lt;/td&gt;&lt;td&gt;sealed, of 25 &lt;/td&gt;&lt;td&gt;6 &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Haiku 5.5 &lt;/td&gt;&lt;td&gt;public, of 51 &lt;/td&gt;&lt;td&gt;46 &lt;/td&gt;&lt;td&gt;33 &lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Opus 5.5 &lt;/td&gt;&lt;td&gt;sealed, of 25 &lt;/td&gt;&lt;td&gt;15 &lt;/td&gt;&lt;td&gt;12 &lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Opus 5.5 &lt;/td&gt;&lt;td&gt;public, of 51 &lt;/td&gt;&lt;td&gt;51 &lt;/td&gt;&lt;td&gt;39 &lt;/td&gt;&lt;td&gt;43&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The plugin is ahead in total in all four rows. The per-task rule fails on several tasks for both models, except on Opus with the public set.&lt;/p&gt;
&lt;h3 id=&quot;how-much-to-trust-the-grading&quot;&gt;How much to trust the grading&lt;/h3&gt;
&lt;p&gt;I regraded a random sample of 28 of the 279 graded cells with fresh grader sessions. The new grade agreed with the first on 129 of 131 criteria (98.5%), and no cell moved by more than one point. I also ran the same 28 cells again once. 25 of those 28 re-runs scored within the range of the three original runs, so a single run of a cell can differ from the earlier ones. On the sealed set the five votes were unanimous on 91.6% of verdicts and on the public set on 97.0%.&lt;/p&gt;
&lt;h3 id=&quot;what-it-costs-and-what-went-wrong&quot;&gt;What it costs and what went wrong&lt;/h3&gt;
&lt;p&gt;At list price the plugin costs 1.67 times the empty-plugin run per cell on the sealed set (0.117 US dollars against 0.070) and 1.56 times on the public set (0.076 against 0.049), because it uses more turns and tokens. The runs were charged to a subscription. The whole round, including the development gates that decided whether to run the final measurement, would cost 185.46 US dollars at list price.&lt;/p&gt;
&lt;p&gt;The plugin has a Stop hook that checks the numbers in an answer once before the agent finishes. It blocked the first answer in 5 of 48 plugin cells (10.4%). Those cells scored 3.40 on average and the unblocked ones 3.60 criteria, so this data does not show whether the hook helps.&lt;/p&gt;
&lt;p&gt;These points limit the result:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The tasks are ours. The sealed writer did not read the surviving-lines repository, but the agent that ran the measurement saw the writer's closing summary (the five repositories and a line of hazards per task) before I wrote the protocol. The hazards are the ones the tool was built to handle. A benchmark by someone who builds a rival could rank the arms differently.&lt;/li&gt;
&lt;li&gt;Five sealed tasks is a small set, and the intervals reflect only the three runs per task, not a different choice of tasks.&lt;/li&gt;
&lt;li&gt;One model produced the sealed and public runs, one CLI version, one login.&lt;/li&gt;
&lt;li&gt;The session limit stopped the drivers twice, both times during the audit re-runs. Cells that failed on a limit were set aside, never graded, and run again, and no scored run was set aside. I renewed the login after the second stop, so the audit re-runs after that point and the other-model runs may have gone through a different account.&lt;/li&gt;
&lt;li&gt;The sealed scripts are published with contributor addresses replaced by their SHA-256. The published scripts rebuild the fixtures with the same commit ids and reproduce the ground truth byte for byte.&lt;/li&gt;
&lt;li&gt;The interval of each arm's own total in the figure is not printed in the evidence summary, which only has intervals of differences. I computed them from the same grades with the same method (&lt;code&gt;intervals.py&lt;/code&gt;, next to the figure script), and it checks its totals against the summary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;an-earlier-measurement-commit-share-against-surviving-lines&quot;&gt;An earlier measurement: commit share against surviving lines&lt;/h3&gt;
&lt;p&gt;The benchmark above asks whether an agent uses the tool well. A separate question is why the tool counts both numbers. In September I ran it on twelve public repositories, with the design and the meaning of a null result written down first. For each repository I measured every author's share of non-merge commits and share of surviving lines at HEAD, from a one-in-five deterministic file sample, with three seeds per repository. That is 36 runs and 2,748 author rows, measured on 5 September 2026 with version 0.1.6.&lt;/p&gt;
&lt;p&gt;The author with the most commits also held the most surviving lines in 25 of the 36 runs, and on all three seeds in 8 of the 12 repositories. Commit counts therefore usually point at the right person. The gap between the two shares is larger than that suggests: among authors holding at least 5% of either measure (122 author rows), the median gap was 7.5 percentage points and the mean 12.2. 43 of the 122 (35%) differed by 10 points or more, and the largest single gap was 63.8 points. In cloudflare/capnweb the top committer, holding 46% of commits, held between 3% and 16% of the sampled surviving lines depending on the seed, while a contributor with 2% of the commits held 45% to 58%. I did not look into why.&lt;/p&gt;
&lt;p&gt;This has limits. Twelve repositories describe twelve repositories and are not a statement about open source. The sample moves the magnitudes: the top author's line share had a median spread of 5.4 points across seeds and a maximum of 17.4, so I read a direction from one run and a magnitude from several. The tool counted bots as authors in that run, and since 0.3.0 it leaves them out by default, so that table has not been re-run with the current behaviour. Surviving code is not merit, since code can stay because nobody dares to touch it. HEAD has moved on in all twelve repositories and the data file records no commit ids, so a re-run will give different numbers.&lt;/p&gt;
&lt;h3 id=&quot;try-it-and-check-it&quot;&gt;Try it and check it&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npx surviving-lines --sort gap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--sort gap&lt;/code&gt; puts the author with the largest difference between line share and commit share first. The tool needs Node 22 or newer and git 2.15 or newer. To use it from Claude Code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/plugin install surviving-lines --marketplace Bubblegunn/surviving-lines
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every cell, the departures from the protocol and a recompute step are in &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Bubblegunn/surviving-lines/tree/main/bench/head-to-head/results/round13-claude-sonnet-5-5&quot;&gt;the round 13 evidence folder&lt;/a&gt;. &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Bubblegunn/surviving-lines/blob/main/bench/head-to-head/results/round13-claude-sonnet-5-5/REPRODUCE.md&quot;&gt;REPRODUCE.md&lt;/a&gt; in that folder gives the commands for a re-run, and the earlier rounds and rulings are in &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Bubblegunn/surviving-lines/blob/main/docs/HEAD-TO-HEAD.md&quot;&gt;docs/HEAD-TO-HEAD.md&lt;/a&gt;. The repository is &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Bubblegunn/surviving-lines&quot;&gt;Bubblegunn/surviving-lines&lt;/a&gt;, and version 0.4.0 is on npm. I wrote about the first version of the tool in September: &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/efe_genc/who-really-owns-this-code-a-git-blame-sampler-you-can-run-in-one-command-3ph1&quot;&gt;Who really owns this code? A git blame sampler you can run in one command&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Three people outside my own commits have shipped code in the tool: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/shivam-070208&quot;&gt;@shivam-070208&lt;/a&gt; wrote &lt;code&gt;--markdown&lt;/code&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Aaqibhafeezkhan&quot;&gt;@Aaqibhafeezkhan&lt;/a&gt; wrote &lt;code&gt;--sort gap&lt;/code&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Enard52&quot;&gt;@Enard52&lt;/a&gt; sent fixes that make empty runs say which filter emptied them. If you run it on something and the numbers look wrong to you, tell me which repository and I will look.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Disclosure: I drafted this article with AI assistance (Claude) and reviewed it myself. Every number comes from the evidence folder linked above and the research folder in the repository, and the benchmark can be re-run from it.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32264/surviving-lines-scored-highest-in-a-six-arm-code-ownership-benchmark</guid>
<pubDate>Sat, 24 Oct 2026 11:15:17 +0000</pubDate>
</item>
<item>
<title>You Ran git reset --hard and Panicked. Here's How to Get Your Commits Back (and the One Thing You Ca</title>
<link>https://coderlegion.com/32171/you-ran-git-reset-hard-and-panicked-heres-how-to-get-your-commits-back-and-the-one-thing-you-ca</link>
<description>&lt;p&gt;Your stomach drops. You typed &lt;code&gt;git reset --hard&lt;/code&gt;, or deleted the wrong branch, or a rebase ended with half your commits missing. The log looks empty. Three days of work, gone.&lt;/p&gt;
&lt;p&gt;Take a breath. If you ever committed that work, it is almost certainly still there. Git is much harder to destroy than it looks, and the tool that proves it is called the reflog.&lt;/p&gt;
&lt;h3 id=&quot;what-the-reflog-is&quot;&gt;What the Reflog Is&lt;/h3&gt;
&lt;p&gt;Your normal history (&lt;code&gt;git log&lt;/code&gt;) shows the commits reachable from your current branch. The &lt;strong&gt;reflog&lt;/strong&gt; is a different record: a private diary of everywhere &lt;code&gt;HEAD&lt;/code&gt; has pointed on your machine, in order, including places you have since left.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git reflog
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;a1b2c3d HEAD@{0}: reset: moving to HEAD~3
9f8e7d6 HEAD@{1}: commit: add pricing page
4c5d6e7 HEAD@{2}: commit: fix navbar on mobile
b7a8c9d HEAD@{3}: commit: add contact form
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A hard reset moved your branch pointer backwards. It did not delete the three commits. They are still stored, and the reflog tells you their IDs.&lt;/p&gt;
&lt;h3 id=&quot;disaster-1-git-reset---hard-threw-away-commits&quot;&gt;Disaster 1: &lt;code&gt;git reset --hard&lt;/code&gt; Threw Away Commits&lt;/h3&gt;
&lt;p&gt;Find the entry from just before the mistake and go back to it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git reflog
git reset --hard 9f8e7d6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use the position instead of the ID:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git reset --hard HEAD@{1}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your branch is back exactly where it was. The &quot;lost&quot; commits are the same commits, same IDs, nothing rewritten.&lt;/p&gt;
&lt;h3 id=&quot;disaster-2-you-deleted-a-branch-you-still-needed&quot;&gt;Disaster 2: You Deleted a Branch You Still Needed&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git branch -D feature/checkout
# Deleted branch feature/checkout (was 3e4f5a6).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at that output. Git told you the ID of the commit the branch pointed at. If you missed it, the reflog still has it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git reflog
# find the last commit you made on that branch
git checkout -b feature/checkout 3e4f5a6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A branch is only a label that points at a commit. Delete the label and the commits stay, until Git cleans them up much later.&lt;/p&gt;
&lt;h3 id=&quot;disaster-3-a-rebase-went-wrong&quot;&gt;Disaster 3: A Rebase Went Wrong&lt;/h3&gt;
&lt;p&gt;Mid-rebase mistakes feel the scariest, because rebase rewrites history. It still keeps the old version in the reflog.&lt;/p&gt;
&lt;p&gt;Right after a rebase, &lt;code&gt;ORIG_HEAD&lt;/code&gt; points at where you were before it started:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git reset --hard ORIG_HEAD
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have done other things since, find the entry that says something like &lt;code&gt;rebase (start)&lt;/code&gt; in the reflog, take the entry just before it, and reset to that.&lt;/p&gt;
&lt;h3 id=&quot;disaster-4-you-dropped-a-stash&quot;&gt;Disaster 4: You Dropped a Stash&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git stash drop&lt;/code&gt; removes the stash from the list, but the commit it was stored in still exists for a while. If you have the ID that was printed, apply it directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git stash apply 7a8b9c0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do not have the ID, search for unreachable commits:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git fsck --no-reflog | grep &quot;dangling commit&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then inspect each ID with &lt;code&gt;git show &amp;lt;id&amp;gt;&lt;/code&gt; until you find your changes. This is slower and less certain than the reflog, so treat it as the last resort.&lt;/p&gt;
&lt;h3 id=&quot;the-one-thing-you-cannot-recover&quot;&gt;The One Thing You Cannot Recover&lt;/h3&gt;
&lt;p&gt;This is the part many tutorials skip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changes you never committed and never staged are not in the reflog, and &lt;code&gt;git reset --hard&lt;/code&gt; deletes them permanently.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The reflog records commits. If you edited files, never ran &lt;code&gt;git add&lt;/code&gt; or &lt;code&gt;git commit&lt;/code&gt;, and then ran &lt;code&gt;git reset --hard&lt;/code&gt; or &lt;code&gt;git checkout .&lt;/code&gt; or &lt;code&gt;git restore .&lt;/code&gt;, those edits are not stored anywhere in Git. There is nothing to recover.&lt;/p&gt;
&lt;p&gt;A small mercy: if you did run &lt;code&gt;git add&lt;/code&gt;, the staged file contents were saved as objects, and &lt;code&gt;git fsck --lost-found&lt;/code&gt; may be able to find them. It is messy, but possible. Unstaged edits have no such safety net.&lt;/p&gt;
&lt;h3 id=&quot;why-reflog-entries-do-not-last-forever&quot;&gt;Why Reflog Entries Do Not Last Forever&lt;/h3&gt;
&lt;p&gt;The reflog is local to your machine, and it expires. By default, entries for commits that are still reachable are kept for about 90 days, and entries for unreachable ones for about 30 days. After that, Git's cleanup can remove the commits for good.&lt;/p&gt;
&lt;p&gt;It also lives only in your clone. A fresh &lt;code&gt;git clone&lt;/code&gt; has an empty reflog, and it cannot help you recover something that was lost in someone else's copy.&lt;/p&gt;
&lt;h3 id=&quot;how-to-make-this-less-scary-next-time&quot;&gt;How to Make This Less Scary Next Time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Commit early, even when it is messy.&lt;/strong&gt; A commit called &quot;wip&quot; is a safety net. You can clean the history up later with an interactive rebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stash before you experiment.&lt;/strong&gt; &lt;code&gt;git stash&lt;/code&gt; takes a snapshot you can come back to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;--force-with-lease&lt;/code&gt; instead of &lt;code&gt;--force&lt;/code&gt;.&lt;/strong&gt; It refuses to overwrite a branch if someone else pushed commits you have not seen yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read what Git prints.&lt;/strong&gt; Deleting a branch tells you the ID. Copy it somewhere before doing anything else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create a rescue branch first.&lt;/strong&gt; Before any risky command, run &lt;code&gt;git branch backup-before-rebase&lt;/code&gt;. It costs nothing and gives you a label to return to.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-rule-1&quot;&gt;The Rule&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;If you committed it, Git almost certainly still has it. Open &lt;code&gt;git reflog&lt;/code&gt; before you do anything else.&lt;/strong&gt; And if you are about to run a command that can discard work you have not committed, commit or stash first.&lt;/p&gt;
&lt;p&gt;Quick drill: make a throwaway repo, create three commits, run &lt;code&gt;git reset --hard HEAD~3&lt;/code&gt;, and get them back with the reflog. Do it once when nothing is at stake, and the real thing is not scary any more.&lt;/p&gt;
&lt;p&gt;If you build with Next.js, my &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.com&quot;&gt;templates on pixelanas.com&lt;/a&gt; give you a clean starting point, which is a good first commit to have in your history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get the templates:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.gumroad.com&quot;&gt;https://pixelanas.gumroad.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What is the worst thing you have ever lost to a Git command, and did you get it back? Tell me in the comments.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Anas, full-stack Next.js developer building SaaS products and premium templates. X: &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/ASheikh69751&quot;&gt;@ASheikh69751&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32171/you-ran-git-reset-hard-and-panicked-heres-how-to-get-your-commits-back-and-the-one-thing-you-ca</guid>
<pubDate>Fri, 23 Oct 2026 10:54:58 +0000</pubDate>
</item>
<item>
<title>Why Git Blob IDs Mismatch Your File Checksums</title>
<link>https://coderlegion.com/31609/why-git-blob-ids-mismatch-your-file-checksums</link>
<description>&lt;p&gt;Comparing a raw file digest with a Git object identifier often reveals unexpected mismatches. Developers frequently notice that running a standard local hashing tool on a file produces a different checksum than the one returned by Git or the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/rest/git/blobs&quot;&gt;GitHub Git blobs REST API&lt;/a&gt;. This discrepancy occurs because Git does not hash raw file bytes directly. Instead, Git constructs an object header that includes the object type and byte length, prepends it to the content, and hashes the combined payload.&lt;/p&gt;
&lt;p&gt;Quick solution:&lt;br&gt;
To calculate the exact Git blob object ID locally, prepend the object type and byte length header followed by a null byte before hashing the raw content.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;printf &quot;blob %d\0%s&quot; &quot;$(wc -c &amp;lt; file.txt)&quot; &quot;$(cat file.txt)&quot; | shasum
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prerequisites include a standard Unix shell environment with &lt;code&gt;shasum&lt;/code&gt; or &lt;code&gt;sha1sum&lt;/code&gt;, and &lt;code&gt;wc&lt;/code&gt; for counting exact byte lengths. Be aware that trailing newlines or varying shell implementations of &lt;code&gt;printf&lt;/code&gt; can alter the generated byte stream.&lt;/p&gt;
&lt;h3 id=&quot;why-does-hashing-raw-file-bytes-produce-another-value-&quot;&gt;Why Does Hashing Raw File Bytes Produce Another Value?&lt;/h3&gt;
&lt;p&gt;When you run a command like &lt;code&gt;openssl dgst -sha1 file.txt&lt;/code&gt; or &lt;code&gt;shasum file.txt&lt;/code&gt;, the utility reads the raw byte sequence of the file and feeds it directly into the cryptographic hash function. This yields a pure content digest. Git, however, treats files as version-controlled objects rather than flat storage blocks. According to the &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-hash-object&quot;&gt;Git hash-object documentation&lt;/a&gt;, every stored object receives a header composed of its type (such as &lt;code&gt;blob&lt;/code&gt;), a space, the size of the file in bytes, and a null byte (&lt;code&gt;\u0000&lt;/code&gt;) acting as a separator. &lt;/p&gt;
&lt;p&gt;For example, if a file contains the exact text &lt;code&gt;hello&lt;/code&gt;, its byte length is 5. Git constructs the internal byte sequence by joining the string &lt;code&gt;blob 5&lt;/code&gt;, a null byte, and the five characters of content. Hashing this combined stream produces the unique Git object identifier. Because the raw file digest lacks the header, metadata, and null separator, the resulting checksum diverges completely from the Git blob ID. Developers optimizing performance workflows often review prior indexing patterns, such as those detailed in our guide on &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/google-search-console-content-prioritization/&quot;&gt;Google Search Console Content Prioritization&lt;/a&gt;, to ensure metadata indices align properly with storage identifiers.&lt;/p&gt;
&lt;h3 id=&quot;how-is-utf-8-byte-length-different-from-string-length-&quot;&gt;How Is UTF-8 Byte Length Different from String Length?&lt;/h3&gt;
&lt;p&gt;Calculating correct object identifiers requires precise byte counting rather than character or string length measurements. In ASCII text, one character equals one byte. However, modern codebases frequently utilize multi-byte UTF-8 characters, emojis, or specialized symbols. &lt;/p&gt;
&lt;p&gt;Consider a text file containing a single multi-byte character, such as the euro symbol (€). In UTF-8 encoding, this character occupies three bytes. If an application measures string length by counting logical characters, it might report a length of 1. If Git then calculates the header using that incorrect length, the resulting object hash will fail to match the repository database or API response. Engineers must always measure exact byte counts using tools like &lt;code&gt;wc -c&lt;/code&gt; or native byte-length methods in programming languages rather than relying on character-length functions.&lt;/p&gt;
&lt;h3 id=&quot;when-must-application-integrity-use-a-separate-digest-&quot;&gt;When Must Application Integrity Use a Separate Digest?&lt;/h3&gt;
&lt;p&gt;Relying on Git object IDs for external application security or file verification introduces architectural risks. Because Git object identifiers depend on the internal repository header and the chosen hashing algorithm (such as SHA-1 or newer experimental formats like SHA-256), they are tightly coupled to version control internals rather than raw file content.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;Option &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;When to Use It &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Trade-off or Failure Mode &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Recommendation&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Git Blob ID&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Verifying repository object storage and API blob parity &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Tied to Git metadata headers and vulnerable to SHA-1 collision concerns &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Use strictly for Git repository tooling and GitHub REST API integration&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Raw File Digest&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Verifying independent file downloads, caches, or releases &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Ignores repository metadata and commit context &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Use standard SHA-256 or BLAKE2 for general application integrity checks&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Applications that require independent file verification, artifact signing, or secure distribution must rely on a dedicated raw digest computed directly from the file bytes. Treating Git object IDs as universal file checksums creates fragile verification pipelines that break when files are checked out with different line ending configurations, such as CRLF versus LF normalization.&lt;/p&gt;
&lt;h3 id=&quot;additional-scenarios-and-failure-modes&quot;&gt;Additional Scenarios and Failure Modes&lt;/h3&gt;
&lt;p&gt;In addition to the differences between raw file digests and Git blob IDs, there are other scenarios where the two may diverge. For instance, when a file is modified, the Git blob ID will change, but the raw file digest may not if the modification does not affect the file's content. Similarly, if a file is renamed or moved, the Git blob ID will remain the same, but the raw file digest may change if the file's content is modified during the rename or move operation.&lt;/p&gt;
&lt;h3 id=&quot;execution-and-verification-checklist&quot;&gt;Execution and Verification Checklist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Verify empty content matches git hash-object by running an empty file through the header generation command and comparing it against &lt;code&gt;git hash-object --stdin&lt;/code&gt;. Expected result: hashes match the known empty blob identifier &lt;code&gt;e69de29bb2d1d6434b8b29ae775ad8c2e48c5391&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] Test non-ASCII text using exact byte length calculations rather than character counts to ensure multi-byte symbols generate matching object IDs.&lt;/li&gt;
&lt;li&gt;[ ] Confirm a one-byte modification alters both the raw digest and the Git blob identifier, validating that minor edits propagate correctly through the header generation step.&lt;/li&gt;
&lt;li&gt;[ ] Document environment specifics, shell versions, and encoding parameters before deploying any automated script that compares raw file checksums against Git repository objects.&lt;/li&gt;
&lt;li&gt;[ ] Verify the behavior of Git blob IDs and raw file digests in different encoding scenarios, such as UTF-8, UTF-16, and UTF-32.&lt;/li&gt;
&lt;li&gt;[ ] Test the performance impact of using Git blob IDs versus raw file digests for large files and repositories.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In conclusion, the discrepancy between raw file digests and Git blob IDs arises from the inclusion of a header in the Git object identifier. This header contains the object type, byte length, and a null separator, which are not present in the raw file digest. Understanding the differences between these two types of identifiers is crucial for ensuring data integrity and security in version control systems and application development. By using the correct type of identifier for the specific use case, developers can avoid potential pitfalls and ensure the reliability of their workflows.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31609/why-git-blob-ids-mismatch-your-file-checksums</guid>
<pubDate>Mon, 19 Oct 2026 02:25:56 +0000</pubDate>
</item>
<item>
<title>Alternatives to the DevLabs Pull Request Merge Conflict Extension (2026)</title>
<link>https://coderlegion.com/31072/alternatives-to-the-devlabs-pull-request-merge-conflict-extension-2026</link>
<description>&lt;hr&gt;
&lt;p&gt;title: &quot;Alternatives to the DevLabs Pull Request Merge Conflict Extension (2026)&quot;&lt;br&gt;
published: true&lt;br&gt;
description: &quot;Microsoft's DevLabs merge conflict extension for Azure DevOps is no longer maintained. An honest comparison of the alternatives: resolving locally, your IDE, and a maintained extension.&quot;&lt;br&gt;
tags: azuredevops, git, devops, productivity&lt;br&gt;
canonical_url: &lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/guides/devlabs-merge-conflict-extension-alternatives/&quot;&gt;https://jr-devtools.com/guides/devlabs-merge-conflict-extension-alternatives/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;cover-image-https-jr-devtools-com-merge-conflict-resolver-screenshot-editor-png&quot;&gt;cover_image: &lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/merge-conflict-resolver/screenshot-editor.png&quot;&gt;https://jr-devtools.com/merge-conflict-resolver/screenshot-editor.png&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/guides/devlabs-merge-conflict-extension-alternatives/&quot;&gt;jr-devtools.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For years, the free &lt;em&gt;Pull Request Merge Conflict Extension&lt;/em&gt; from Microsoft DevLabs (&lt;code&gt;ms-devlabs.conflicts-tab&lt;/code&gt;) was how teams resolved conflicts without leaving Azure DevOps. It is no longer maintained. Here's what's going on and what you can use instead.&lt;/p&gt;
&lt;p&gt;Disclosure: we make Merge Conflict Resolver, one of the options below. We've tried to keep this comparison fair; corrections are welcome at &lt;a rel=&quot;nofollow&quot; href=&quot;mailto:support@jr-devtools.com&quot;&gt;support@jr-devtools.com&lt;/a&gt;. Last checked: October 2026.&lt;/p&gt;
&lt;h3 id=&quot;is-the-devlabs-extension-still-supported-&quot;&gt;Is the DevLabs extension still supported?&lt;/h3&gt;
&lt;p&gt;No. Its last update was in November 2023, and Microsoft wrote in the extension's Q&amp;amp;A that &lt;em&gt;“there are no current development resources for this extension”&lt;/em&gt;. Questions like “Is this even supported anymore?” go unanswered. Since 2024 its reviews average about 1.6 out of 5. The most reported problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the tab freezes or keeps “taking longer than expected to load”, and work is lost when it crashes;&lt;/li&gt;
&lt;li&gt;non-ASCII characters (ä, é, č, Hebrew, Chinese …) are changed, especially in UTF-8 and ANSI files;&lt;/li&gt;
&lt;li&gt;you can only take a whole file from one side, not choose per conflict or keep both;&lt;/li&gt;
&lt;li&gt;the resolution lives only in the pull request's merge commit, so reviewers and builds never see it;&lt;/li&gt;
&lt;li&gt;large files such as &lt;code&gt;package-lock.json&lt;/code&gt;, or many conflicts, make it hang.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It still works for some teams on simple conflicts, so there's no need to remove it in a hurry. But if you run into the problems above, there won't be a fix.&lt;/p&gt;
&lt;h3 id=&quot;the-alternatives&quot;&gt;The alternatives&lt;/h3&gt;
&lt;h4 id=&quot;1-resolve-locally-with-git-free-&quot;&gt;1. Resolve locally with Git (free)&lt;/h4&gt;
&lt;p&gt;Merge the target branch into your source branch, resolve in your editor, and push. See our &lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/guides/resolve-merge-conflicts-azure-devops/&quot;&gt;step-by-step guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; free, always works, you can build and test before pushing. &lt;strong&gt;Less good:&lt;/strong&gt; you need the repository cloned and a working Git setup; slow for a two-line fix or for someone else's pull request.&lt;/p&gt;
&lt;h4 id=&quot;2-your-ide-s-git-integration-free-&quot;&gt;2. Your IDE's Git integration (free)&lt;/h4&gt;
&lt;p&gt;Visual Studio, VS Code and JetBrains IDEs all have 3-way merge editors. In Visual Studio, check out the source branch, merge the target branch from &lt;em&gt;Git → Manage Branches&lt;/em&gt;, and resolve in the merge editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; comfortable editor with IntelliSense. &lt;strong&gt;Less good:&lt;/strong&gt; same as locally: you need a clone, and it's per developer, not something a reviewer can do from the pull request.&lt;/p&gt;
&lt;h4 id=&quot;3-merge-conflict-resolver-free-plan-pro-from-12-per-organization-per-month-&quot;&gt;3. Merge Conflict Resolver (free plan, Pro from $12 per organization per month)&lt;/h4&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/merge-conflict-resolver/&quot;&gt;Merge Conflict Resolver&lt;/a&gt; is an independent, actively maintained extension that adds a &lt;strong&gt;Resolve conflicts&lt;/strong&gt; tab to pull requests, built to fix the problems above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3-way editor (Monaco, the editor of VS Code) with &lt;em&gt;Accept target / source / both&lt;/em&gt; per conflict;&lt;/li&gt;
&lt;li&gt;drafts are saved in the browser, so a reload or crash never loses work;&lt;/li&gt;
&lt;li&gt;UTF-8, UTF-8 with BOM, UTF-16 and Windows-1252 files and their line endings are written back byte for byte;&lt;/li&gt;
&lt;li&gt;edit/delete, renames, binary files, Git LFS and submodules each get a clear choice;&lt;/li&gt;
&lt;li&gt;stays responsive with hundreds of conflicts and multi-megabyte lockfiles;&lt;/li&gt;
&lt;li&gt;Pro: commit the resolution to the source branch, a summary comment on the pull request, bulk and whitespace auto-resolve.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; no clone needed, anyone on the pull request can resolve. &lt;strong&gt;Less good:&lt;/strong&gt; Azure DevOps Services only for now (not Azure DevOps Server); Pro is paid. The free plan covers 10 pull requests per organization per month, and every organization gets a 14-day Pro trial.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://jr-devtools.com/merge-conflict-resolver/screenshot-editor.png&quot; alt=&quot;Merge Conflict Resolver's 3-way editor in an Azure DevOps pull request&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;side-by-side&quot;&gt;Side by side&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;DevLabs extension&lt;/th&gt;&lt;th&gt;Locally / IDE&lt;/th&gt;&lt;th&gt;Merge Conflict Resolver&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Resolve in the pull request, no clone| ✔| –| ✔&lt;br&gt;
Choose per conflict| Whole file only| ✔| ✔&lt;br&gt;
Work survives a reload or crash| –| ✔| ✔&lt;br&gt;
Encodings kept intact| Reported to break| Depends on editor| ✔&lt;br&gt;
Reviewers and builds see the resolution| –| ✔| ✔ (Pro)&lt;br&gt;
Maintained| No (last update 2023)| ✔| ✔&lt;br&gt;
Price| Free| Free| Free plan; Pro $12/month per organization&lt;/p&gt;
&lt;h3 id=&quot;switching-from-the-devlabs-extension&quot;&gt;Switching from the DevLabs extension&lt;/h3&gt;
&lt;p&gt;Both extensions can be installed side by side, so you can try before you remove anything. An organization admin installs Merge Conflict Resolver from the Visual Studio Marketplace; there's nothing to configure, and the 14-day Pro trial starts the first time someone opens the tab.&lt;/p&gt;
&lt;h3 id=&quot;related-guides&quot;&gt;Related guides&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/guides/resolve-merge-conflicts-azure-devops/&quot;&gt;How to resolve merge conflicts in an Azure DevOps pull request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://jr-devtools.com/merge-conflict-resolver/&quot;&gt;Merge Conflict Resolver: features, pricing and free plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Not affiliated with Microsoft. “Azure DevOps”, “Excel” and “Office” are trademarks of Microsoft.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31072/alternatives-to-the-devlabs-pull-request-merge-conflict-extension-2026</guid>
<pubDate>Wed, 14 Oct 2026 13:44:34 +0000</pubDate>
</item>
<item>
<title>Use Git Sparse-Checkout to Scope Agent Worktrees</title>
<link>https://coderlegion.com/31065/use-git-sparse-checkout-to-scope-agent-worktrees</link>
<description>&lt;p&gt;A &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/ai-coding-agent-vps-or-mac/&quot;&gt;coding agent&lt;/a&gt; assigned to one package may not need every sibling directory in a large repository working tree. Git worktrees provide separate working directories associated with one repository, while sparse-checkout limits which tracked paths are populated in a particular working tree. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick solution:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git worktree add -b agent/payments ../agent-payments main
cd ../agent-payments
git sparse-checkout init --cone
git sparse-checkout set packages/payments packages/shared
git sparse-checkout list
git status --short
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setting-up-scoped-worktrees&quot;&gt;Setting Up Scoped Worktrees&lt;/h3&gt;
&lt;p&gt;When managing large monorepos, populating every single directory for every agent task consumes unnecessary disk space and adds noise to the agent context. You can isolate tasks by combining worktrees with path restrictions. Start with a dedicated branch and worktree, then configure the specific paths the task needs.&lt;/p&gt;
&lt;p&gt;In cone mode, account for root-level files and the selected directories when reviewing what appears in the working tree. Add only the build, test, and shared files the task requires. The official &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-sparse-checkout&quot;&gt;Git sparse-checkout documentation&lt;/a&gt; describes pattern behavior and cone mode operations. Additionally, the &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-worktree&quot;&gt;Git worktree documentation&lt;/a&gt; explains linked working directories and shared repository data, while the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.blog/open-source/git/bring-your-monorepo-down-to-size-with-sparse-checkout/&quot;&gt;GitHub sparse-checkout guide&lt;/a&gt; provides broader monorepo context.&lt;/p&gt;
&lt;h3 id=&quot;keeping-visibility-separate-from-enforcement&quot;&gt;Keeping Visibility Separate from Enforcement&lt;/h3&gt;
&lt;p&gt;Sparse-checkout is a workspace convenience, not a security boundary. An agent or tool with &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/configuring-automated-repository-access/&quot;&gt;repository access&lt;/a&gt; can change sparse patterns or use Git commands to inspect other tracked content. If a task must not modify unrelated paths, enforce a separate allowlist in the integration system and reject any diff that changes paths outside the task approved set. Protect credentials and sensitive files with actual access controls or sandboxing rather than relying on workspace population rules.&lt;/p&gt;
&lt;p&gt;A diff allowlist can catch an out-of-scope change before integration. It does not prevent the agent from reading or attempting to edit that file earlier, so treat it as a merge gate rather than a permission system.&lt;/p&gt;
&lt;h3 id=&quot;verification-checklist&quot;&gt;Verification Checklist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Confirm the worktree uses the intended branch and base commit.&lt;/li&gt;
&lt;li&gt;List the sparse paths and inspect which root files remain present.&lt;/li&gt;
&lt;li&gt;Run the required build and tests in that worktree.&lt;/li&gt;
&lt;li&gt;Compare the final diff to a separately enforced path allowlist.&lt;/li&gt;
&lt;li&gt;Record measured file or context changes only when the measurement method is reproducible.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31065/use-git-sparse-checkout-to-scope-agent-worktrees</guid>
<pubDate>Wed, 14 Oct 2026 12:16:17 +0000</pubDate>
</item>
<item>
<title>Managing Agent Worktrees in Git</title>
<link>https://coderlegion.com/30524/managing-agent-worktrees-in-git</link>
<description>&lt;p&gt;Running parallel AI coding agents across shared repositories via Git worktrees prevents duplicate cloning, but it exposes critical operational edge cases. When multiple automated workers operate simultaneously within the same repository infrastructure, subtle concurrency conflicts can compromise shared history. Managing safe cleanup and ephemeral lifecycles for these parallel AI coding agents without corrupting shared Git refs or reflogs requires strict boundaries between working tree modifications and global repository state. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/shared-git-state-in-parallel-agent-worktrees/&quot;&gt;Shared Git State In Parallel Agent&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-silent-hazard-of-shared-git-state&quot;&gt;The Silent Hazard of Shared Git State&lt;/h3&gt;
&lt;p&gt;Git worktrees share the underlying object database and repository reflogs, even though they maintain separate working directories. When an agent runs a destructive command like &lt;code&gt;git reset --hard&lt;/code&gt; or &lt;code&gt;git branch -D&lt;/code&gt;, it silently mutates history that every sibling worktree and human developer observes. This creates insidious, invisible state drift that only surfaces during high-pressure rollbacks or post-mortems.&lt;/p&gt;
&lt;p&gt;Furthermore, when multiple agents spawn simultaneously and attempt to claim branch names dynamically at runtime, they crash because Git strictly refuses to check out the same branch in more than one worktree at a time. Agents frequently produce broken code, failed test runs, or malformed diffs that require discarding, but giving an autonomous agent access to global reset commands gives it the power to destroy commit pointers across the entire shared repository.&lt;/p&gt;
&lt;h3 id=&quot;the-command-boundary&quot;&gt;The Command Boundary&lt;/h3&gt;
&lt;p&gt;To prevent reflog contamination, agents must be strictly prohibited from executing ref-mutating commands. Instead, agent tool definitions only permit scoped working tree operations that isolate changes to the local filesystem boundary. Whitelisting specific recovery commands protects the underlying repository while still giving workers the ability to clear their scratchpads.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git restore .
git clean -fd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;git restore .&lt;/code&gt; reverts modified tracked files back to the current HEAD of that worktree without touching commit history or reflogs. Meanwhile, &lt;code&gt;git clean -fd&lt;/code&gt; purges untracked build artifacts and scratchpad directories. This guarantees that local mistakes cannot leak beyond the worktree boundary or pollute the shared audit trail.&lt;/p&gt;
&lt;h3 id=&quot;the-cattle-pattern-for-agent-worktrees&quot;&gt;The Cattle Pattern for Agent Worktrees&lt;/h3&gt;
&lt;p&gt;When an agent encounters a catastrophic error requiring a full structural rollback across multiple commits, the agent does not attempt in-place recovery. The central orchestrator treats the worktree as disposable cattle rather than a persistent environment.&lt;/p&gt;
&lt;p&gt;When a failure occurs, the orchestrator terminates the agent, forcefully deletes the worktree, prunes worktree metadata, and provisions a fresh worktree from a verified canonical commit.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Orchestrator-managed cleanup from outside the worktree
git worktree remove --force agent-task-742
git worktree prune
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This external destruction pattern isolates failures completely. By executing these commands from the parent control plane, the system avoids leaving dangling locks or corrupted references in the &lt;code&gt;.git/worktrees/&lt;/code&gt; directory.&lt;/p&gt;
&lt;h3 id=&quot;deterministic-scheduling&quot;&gt;Deterministic Scheduling&lt;/h3&gt;
&lt;p&gt;To completely eliminate branch checkout races, branch naming must be removed from the agent's responsibility entirely. If agents generate branch names at runtime, simultaneous requests inevitably collide.&lt;/p&gt;
&lt;p&gt;The orchestrator should instead generate deterministic, collision-free branch names before any worktree is created. By assigning namespaces using a predictable format such as &lt;code&gt;agent/&amp;lt;task-id&amp;gt;/&amp;lt;timestamp&amp;gt;&lt;/code&gt;, the system ensures zero runtime conflicts during parallel initialization. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/audit-macos-system-data-before-deleting-developer-caches/&quot;&gt;Audit Macos System Data Before Deleting&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Safely scaling parallel AI coding agents inside Git worktrees requires shifting away from manual developer habits. By separating scoped working tree cleanup from global reference mutations, treating broken worktrees as disposable cattle, and pre-allocating branch namespaces centrally, engineering teams can maintain clean reflogs and reliable auditability without sacrificing execution speed.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30524/managing-agent-worktrees-in-git</guid>
<pubDate>Sat, 10 Oct 2026 00:03:48 +0000</pubDate>
</item>
<item>
<title>My First Project: From a Local Folder to GitHub Using Git and SSH</title>
<link>https://coderlegion.com/30212/my-first-project-from-a-local-folder-to-github-using-git-and-ssh</link>
<description>&lt;h2 id=&quot;my-first-project-from-a-local-folder-to-git-hub-using-git-and-ssh&quot;&gt;My First Project: From a Local Folder to Git hub Using Git and SSH&lt;/h2&gt;
&lt;h3 id=&quot;git&quot;&gt;GIT&lt;/h3&gt;
&lt;h4 id=&quot;introduction&quot;&gt;Introduction&lt;/h4&gt;
&lt;p&gt;Git is an open source version control system used to manage projects. It is fast and efficient to manage projects using git. You can easily track the project version, history, modifications, and many more project activities. It allows for ease of collaboration while working on projects as teams.&lt;/p&gt;
&lt;h4 id=&quot;installing-git&quot;&gt;Installing Git&lt;/h4&gt;
&lt;p&gt;Download and install git from &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/install/windows&quot;&gt;Git&lt;/a&gt;. Ensure you download for your respective operating system.&lt;/p&gt;
&lt;h4 id=&quot;configuring-git&quot;&gt;Configuring Git&lt;/h4&gt;
&lt;p&gt;Before using Git effectively, you have to configure your identity by setting up your &lt;em&gt;username&lt;/em&gt; and &lt;em&gt;email&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This allows to easily track any changes to your project and the associate, that is; who made the changes?&lt;/p&gt;
&lt;p&gt;To set up your identity, follow the following steps, using Git Bash a git terminal:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Steps&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To set your username&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git config --global user.name &quot;YOUR NAME&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/d3wqbnuhko7gwa79qq0z.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;In place of &lt;em&gt;YOUR NAME&lt;/em&gt; type in your correct user name that will be associated with your github account activity.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To set your email&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git config --global user.email &quot;youemail@example.com&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ajn068mbc9hco8giv5ql.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; It is recommended to use your correct email that is associated with your GitHub account.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Rename you GitHub branch to &lt;em&gt;main&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git config --global init.Branch main&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/zqxh5s6g0u5xabnqjrgm.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;After setting up your identity you can verify your configurations to confirm your identity for each field set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To check your:&lt;/p&gt;
&lt;p&gt;User name&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`git config --global user.name`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Email&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`git config --global user.email`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Branch&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global init.Branch`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can use the following command to check all you account details instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`git config --global --lists`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/e2izfyzfr0rd5sqmr6sg.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ssh&quot;&gt;SSH&lt;/h3&gt;
&lt;p&gt;SSH key is a secure credential used to authentication to accessing network resources confidentially without need of a password. The ssh-key contains two keys. A &lt;em&gt;&lt;strong&gt;Public&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;&lt;strong&gt;Private&lt;/strong&gt;&lt;/em&gt; key. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Public key&lt;/strong&gt; is used publicly, that is it is shared between peers. Used to identify you remote resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Private key&lt;/strong&gt; it is a pair key that is kept as a secret for identifying your local machine.&lt;/p&gt;
&lt;h4 id=&quot;generating-an-ssh-key&quot;&gt;Generating an SSH Key&lt;/h4&gt;
&lt;p&gt;Using Git Bash&lt;/p&gt;
&lt;p&gt;Before generating a new SSH Key, it is recommended to check and confirm if you have an existing key which you can use.&lt;/p&gt;
&lt;p&gt;To check if you have an existing key use the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`ls -al ~/.ssh`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command will list all existing ssh keys. Here sample output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rfnrwqgtgllt5u087bbh.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;If there is no existing key, you can generate a new key that you will use to link your computer with your github account.&lt;/p&gt;
&lt;p&gt;To generate a new ssh key, follow the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Genereate a new ssh-key&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ssh-keygen -t ed25529 -C &quot;youremail@example.com&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The email should be the one associated with with your github account.&lt;/p&gt;
&lt;p&gt;Follow the prompt by adding the name of your key, and also you can add the passphrase which is optional.&lt;/p&gt;
&lt;p&gt;The passhrase should be memorable, you will need to use it each time you want to access you github account.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add your key to the SSH-Agent.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Open your Powershell Terminal and follow the steps below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- Run the following command to start your SSH Agent

	`Get-Service -Name ssh-agent | Set-Service StartupType Manual`

- Add your key to the  path using the  command

	`ssh-add &amp;lt;PATH/TO/YOUR/PRIVATE_KEY&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the command, you should specifify the path to your private key and add it to your host machine's ssh-agent. i.e. &lt;code&gt;ssh-add &quot;C:/Users/&amp;lt;YOUR_USER_NAME/.ssh/&amp;lt;YOUR_PRIVATE_KEY&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you don't know your computer's user name, you can use the &lt;code&gt;whoami&lt;/code&gt; command to see your username.&lt;/p&gt;
&lt;p&gt;Your _PRIVATE_KEY&lt;em&gt;NAME&lt;/em&gt; is the name that you gave to your key.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- Once you're done, close your Powershell terminal and return to Git Bash.

- Type the following command

	`git config --global core.sshCommand &quot;C:/Windows/System32/OpenSSH/ssh.exe`

- Then Clip and copy your key using the following commands:

	`clip &amp;lt; ~/.ssh/&amp;lt;PUBLIC_KEY.pub&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i.e &lt;code&gt;clip &amp;lt; /.ssh/my_key.pub&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`cat ~/.ssh/&amp;lt;PUBLIC_KEY.pub&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i.e. 'cat ~/.ssh/my_key.pub` to view your public ssh key&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- After displaying your key, select and copy the key to add to your GitHub account.
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;adding-your-key-to-github&quot;&gt;Adding Your Key to GitHub&lt;/h4&gt;
&lt;p&gt;To connect your computer and git-hub is necessary to facilitate access to your remote resources from your local computer.&lt;/p&gt;
&lt;p&gt;You need to add your SSH Keys to GitHub to facilitate the connection. To achieve this, follow the steps:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you have an existing GitHub account:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;a. Login to your account using your email/username and password.&lt;/p&gt;
&lt;p&gt;b. From the profile section, go to &lt;em&gt;Settings&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;c. Scroll to &lt;em&gt;SSH and GPG Key&lt;/em&gt;. Navigate and click on &lt;em&gt;New SSH Key&lt;/em&gt; on the SSH Key Section to your right.&lt;/p&gt;
&lt;p&gt;e. Add the &lt;em&gt;Title/Name&lt;/em&gt; of the key i.e. &lt;em&gt;Ubuntu Laptop&lt;/em&gt;, the key will be used for &lt;em&gt;Authentication&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;f. Paste your key to the available textbox.&lt;/p&gt;
&lt;p&gt;g. Finally, click on &lt;em&gt;Add Key&lt;/em&gt; to save your key. You'll be prompted to re-enter your git-hub account password (login password), to confirm your authenticity before saving.&lt;/p&gt;
&lt;p&gt;Now you are ready to go. Before you start, it is good you test your connection to check if it was successful.&lt;/p&gt;
&lt;p&gt;Run Powershell as an Administrator and use the following command to test connection:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`ssh -T git@github.com`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the connection is successful, your username will be displayed along with a success message.&lt;/p&gt;
&lt;p&gt;If successful, you are good to go and work on your project.&lt;/p&gt;
&lt;h3 id=&quot;working-with-folders&quot;&gt;Working With Folders&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;To keep you project structural and more organized; you've to keep its files and scripts in folders.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This will make it easier to navigate and access the project. Follow the steps below to work with folders in Windows.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Git Bash Terminal(Opened normally)&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To navigate and change location use the following command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`cd &amp;lt;path_location&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To select the current location&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`cd .`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get to the previous location one step back use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`cd ..`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting back to the root directory use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`cd ~`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also move to a specific folder by specifying the path to that folder&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`cd &amp;lt;path/to/folder&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;example: &lt;code&gt;cd &quot;C:/Users/Admin/Desktop/My-Project&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My-Project/&lt;/em&gt; is a folder located in Desktop, which is for the user with the username &lt;em&gt;Admin&lt;/em&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To create a directory or folder use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`mkdir &amp;lt;directory_name&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;i.e. Create a directory named &lt;em&gt;My-First-Project&lt;/em&gt; in Desktop &lt;code&gt;mkdir My-First-Project&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to your created folder. Assuming you are on Desktop, use the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`cd My-First-Project/`
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So your bash terminal path will look like &lt;code&gt;~/Desktop/My-First-Project&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To list and view items use the following command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`ls`
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It will list all files and folders excluding the hidden files and folders. As shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/yodsemd3r03fx0ltw8lk.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;To list and view all files and folders including the hidden ones you add &lt;em&gt;-a&lt;/em&gt; attribute to the &lt;code&gt;ls&lt;/code&gt; command as below&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`ls -a`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/m0ik6vtw5w4ymbjcdzdo.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;To list files and display their attributes we add the &lt;em&gt;-l&lt;/em&gt; attribute to the &lt;code&gt;ls&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`ls -l`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/g98xshsqzai093amcfeu.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; You can combine the attributes to list all files and their attributes i.e. as &lt;code&gt;ls -al&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can delete an empty folder using &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`rm -r &amp;lt;folder_name&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To delete a folder together with its contents we use the &lt;code&gt;rm -r &amp;lt;non-empty_folder_name&amp;gt;&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Example: Create a folder in you my project folder using the &lt;code&gt;mkdir&lt;/code&gt; command, inspect the folder created and its contents, then delete the folder using the rm command:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Steps&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create scripts: &lt;code&gt;mkdir scripts&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to scripts and back to your project folder: &lt;code&gt;cd scripts/&lt;/code&gt; and &lt;code&gt;cd ..&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inspect scripts: &lt;code&gt;ls scripts/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete scripts: &lt;code&gt;rm -r scripts/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/02201e2p7xh3f724jq9d.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; &lt;code&gt;rm&lt;/code&gt; without any attribute will no delete a directory/folder even if empty, the attribute &lt;em&gt;-r&lt;/em&gt; tells the command &lt;code&gt;rm&lt;/code&gt; that what you're deleting is a directory.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can copy and paste contents between different folders.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To copy contents moves a duplicate of the files to another location leaving the original intact.&lt;/p&gt;
&lt;p&gt;While using the &lt;code&gt;cp&lt;/code&gt; command you have to specify the file/contents path and the destination address. Example:&lt;/p&gt;
&lt;p&gt;To copy some file named hello.txt to the scripts folder; you need to recreate the folder and create the file in the root directory then copy it to scripts folder in your projects folder&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Steps&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In your project folder create scripts &lt;code&gt;mkdir scripts&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to your project root folder and create the file &lt;em&gt;hello.txt&lt;/em&gt; &lt;code&gt;touch hello.txt&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the file to scripts &lt;code&gt;cp hello.txt ~/scripts&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inspect both locations to see that the file exists in both location. &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;ls scripts&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; You can specify the file path in the cp command if the file is in a different location and the destination path address &lt;/p&gt;
&lt;p&gt;i.e. &lt;code&gt;cp &amp;lt;~/path/to/file_to_be_copied.txt&amp;gt; &amp;lt;~/path/to_destination/&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can move a file or any content completely from the source to an new destination using the &lt;code&gt;mv&lt;/code&gt; command.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example: Move &lt;em&gt;hello.txt&lt;/em&gt; file from the project's folder to a new folder named &lt;em&gt;docs&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`mv hello.txt docs/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; You can also specify the file path in case the files and destination address are in different locations i.e. &lt;code&gt;mv &amp;lt;~/path/from_source/hello.txt&amp;gt; &amp;lt;~/path/to_destination_folder/&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;working-with-files&quot;&gt;Working With Files&lt;/h3&gt;
&lt;p&gt;While working in your project you will need to create different files for your project.&lt;/p&gt;
&lt;p&gt;The different files will be saved in different directories depending on their type and use. &lt;/p&gt;
&lt;p&gt;The organization will help us to achieve a proper project structure, easier to navigate and access our project files.&lt;/p&gt;
&lt;p&gt;We will create our files using Git Bash terminal.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To create a file we'll use the &lt;code&gt;touch&lt;/code&gt; command. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create a file named &lt;em&gt;hello-world.txt&lt;/em&gt; in your project root directory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`touch hello-world.txt`
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;code&gt;ls&lt;/code&gt; command to confirm your file has been created.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To view the contents of the file we use the &lt;code&gt;cat&lt;/code&gt; command, since our file is empty it will display 'nothing'.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`cat hello-world.txt`
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To add contents to our file we use the &lt;code&gt;echo&lt;/code&gt; command.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add the following text to the file &lt;em&gt;hello-world.txt&lt;/em&gt;; &quot;Hello, World&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`echo &quot;Hello, World!&quot; &amp;gt; hello-world.txt`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/uvhxthqnnamj5zwqfzo3.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;We use the &lt;em&gt;&amp;gt;&lt;/em&gt; to redirect the output to our file. &lt;em&gt;&amp;gt;&lt;/em&gt; Rewrites any content in the file. &lt;/p&gt;
&lt;p&gt;If you want to add more content to the file use &lt;em&gt;&amp;gt;&amp;gt;&lt;/em&gt;, more text will be added to our file. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/zrmqjjh6fsn44xh9hepq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Example; add the following text to our file &quot;I am happy to learn SSH and Git&quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`echo &quot;I am happy to learn Git and SSH&quot; &amp;gt;&amp;gt; hello-world.txt`
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cat hello-world.txt&lt;/code&gt; to view the contents of the file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/hio4sg9opdiguztkbne7.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;em&gt; Editing you file using nano &lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Nano is an inbuilt text editor in Git Bash.&lt;/p&gt;
&lt;p&gt;It helps us to navigate through our files; scripts, text file, .... once we make mistakes.&lt;/p&gt;
&lt;p&gt;To open our script or text file we use the &lt;code&gt;nano&lt;/code&gt; command in git bash then specify the file we want to open, for example open &lt;em&gt;hello-world.txt&lt;/em&gt; using nano:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`nano hello-world.txt'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/atsfbilrrzl8gqw8swht.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are many more commands for nano that will help you edit your text file. For example the most common commands are:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. Ctr + O - To save your file

2. Ctrl + X - To exit your nano text editor

3. Ctrl + K - To Cut text in the editor

4. Ctrl + U - To Paste your text

5. Ctrl + G - To find help for you nano editor.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are many more commands of help that you can use to edit you files in nano editor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/gyapzhzkcv6s1msgglo4.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;your-first-project&quot;&gt;Your First Project&lt;/h3&gt;
&lt;h4 id=&quot;initialize-your-first-project-with-git&quot;&gt;Initialize your first project with git&lt;/h4&gt;
&lt;p&gt;Before running the command, check to confirm that you are in your project folder using the &lt;code&gt;pwd&lt;/code&gt; command. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pwd&lt;/code&gt; command means &lt;em&gt;print working directory&lt;/em&gt;  it shows your current location with regard to the root directory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vj63ecqsyjcin6wrsoct.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;If in your project directory, the path will end with the name of the project folder. I.e. &lt;em&gt;My-First-Project&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;git init&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ejy9wh4olyjgdhwjcm7l.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The command is used to initialize our project directory as a git repository.&lt;/p&gt;
&lt;h4 id=&quot;git-repository-structure-&quot;&gt;Git repository structure.&lt;/h4&gt;
&lt;p&gt;After running the above command, you may notice notification  of git initializing an empty git repo...&lt;/p&gt;
&lt;p&gt;If you list to view the structure using the &lt;code&gt;ls&lt;/code&gt; command you may notice a &lt;em&gt;.git/&lt;/em&gt; folder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/17fnm7nczt2emkd8e2m2.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The folder contains Git's internal repository information, which git uses to manage activities such as: push, commits, pull, Branches, project history, repository configurations,....&lt;/p&gt;
&lt;h4 id=&quot;checking-the-project-status&quot;&gt;Checking the project status&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;	`git status`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Used to show what's happening with your project currently. &lt;/p&gt;
&lt;p&gt;It can show things such as untracked files, staged files to be committed yet, if the working tree is clean meaning everything has been committed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/739uf4cr7umrhtlgwauv.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The above show that there is nothing that has been staged for commit, there are three untracked files in the initialized repository.&lt;/p&gt;
&lt;h4 id=&quot;staging-your-project-files&quot;&gt;Staging your project files&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;	`git add .`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/bd1enh0j8cunas7fsgq4.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;It adds your project files to git for tracking.&lt;/p&gt;
&lt;p&gt;Checking on the status of the staged file, you should get such an output with &lt;code&gt;git status&lt;/code&gt; command&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/q52vf48i6k1hglqddkj7.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;It prepares all you changes for the next commit.&lt;/p&gt;
&lt;p&gt;You can stage a single file by specifying the file/folder you want to stage i.e. &lt;code&gt;git add &amp;lt;file_name.txt&amp;gt;&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/omjr8oo506a1e1ulotbf.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;You should see almost a similar output when using &lt;code&gt;git status&lt;/code&gt; command after staging a single file&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/h2jalyli8gb854gc8mm2.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Using the &lt;em&gt;.&lt;/em&gt; means you stage everything in your project folder.&lt;/p&gt;
&lt;h4 id=&quot;commiting-you-staged-files&quot;&gt;Commiting you staged files&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;	`git commit -m &quot;&amp;lt;commit message&amp;gt;&quot;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creates a saved checkpoint.&lt;/p&gt;
&lt;p&gt;The message should be precise and clear, related to the activity carried out. i.e. &quot;My First Project Using Git Commit&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/tkz6an35n2381ldspn67.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;It does not upload anything to git-hub yet.&lt;/p&gt;
&lt;h4 id=&quot;uploading-to-git-hub&quot;&gt;Uploading to git-hub&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;	`git push -u origin &amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ijtfrh9q1669086oqqe0.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Sends all the committed changes to GitHub. However, in our case we get the above error because we have not linked our local project repo to a git-hub remote repository.&lt;/p&gt;
&lt;p&gt;We will be linking our repositories in the next section.&lt;/p&gt;
&lt;p&gt;After running this command, you can easily check on the changes whether they reflect on GitHub via your browser.&lt;/p&gt;
&lt;p&gt;However, for us we are yet to see these changes. This may not work since we are yet to create a remote repository which we will then connect to our local project in our laptop.&lt;/p&gt;
&lt;h4 id=&quot;creating-a-remote-repository&quot;&gt;Creating a remote repository&lt;/h4&gt;
&lt;p&gt;To create a remote repository, follow the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Login to your git-hub account&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In your account click on &quot;Create New Repository&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter the name of your Repository as &quot;My-First-Project&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do not create a README, uncheck the checkbox&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provide a brief descriptions for your project. The description is: &quot;My First Project: From a Local Folder to Git hub Using Git and SSH&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then click on Create Repository&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A new empty repository is created with the name &quot;My-First-Project&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7rg1zy9xdj87p53dezpc.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Navigate to your repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;em&gt;code&lt;/em&gt; section, then click on &lt;em&gt;ssh&lt;/em&gt; tab and copy the &lt;em&gt;ssh code&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/dpdkinyysthajwlk94xq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;We'll use the ssh code to connect our remote and local repositories before pushing our project.&lt;/p&gt;
&lt;h4 id=&quot;connecting-our-local-and-remote-repositories&quot;&gt;Connecting our local and remote repositories&lt;/h4&gt;
&lt;p&gt;Navigate to your git initialized  project folder&lt;/p&gt;
&lt;p&gt;Type in the following command and thereafter paste the ssh code for your remote repository and hit enter.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`git remote add origin &amp;lt;your_remote_ssh_code&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i.e. &lt;code&gt;git remote add origin git@github.com:MakoriNyachaki/My-First-Github-Project.git&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/fv5763zw6bnl09wtykjt.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;rename-your-branch&quot;&gt;Rename your branch&lt;/h4&gt;
&lt;p&gt;If your remote repository's branch is not &lt;em&gt;main&lt;/em&gt; rename it to &lt;em&gt;main&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	`git branch -m main`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/d27kjda6cdi1awwjhl84.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;check-the-remote&quot;&gt;Check the remote&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;	`git remote -v`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/mcklkfey0gthgqyvqati.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;push-your-project-to-git-hub&quot;&gt;Push your project to git-hub&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;	`git push -u origin &amp;lt;branch_name&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i.e. &lt;code&gt;git push -u origin main&lt;/code&gt; this works to upload our committed changes to our git-hub repository.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3isilkfwccmxlgra9kif.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;An introductory article for learning GIT and SSH. It is thrilling practical exercise for generating ssh keys, working with folders and files, connecting local project folders with remote repositories, and working with git. The introductory article requires one to do further research on so many commands that are available for the different tools we have used here in order to be an expert. Let's learn forward.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30212/my-first-project-from-a-local-folder-to-github-using-git-and-ssh</guid>
<pubDate>Thu, 08 Oct 2026 04:21:17 +0000</pubDate>
</item>
<item>
<title>I wanted Git to show me what would happen before changing history, so I built it</title>
<link>https://coderlegion.com/29970/i-wanted-git-to-show-me-what-would-happen-before-changing-history-so-i-built-it</link>
<description>&lt;p&gt;I use Git every day, but one thing has always bothered me:&lt;/p&gt;
&lt;p&gt;when you do something that changes history, a lot of Git tools still basically ask you to understand the command first and trust that the result is what you expected.&lt;/p&gt;
&lt;p&gt;I wanted something more visual.&lt;/p&gt;
&lt;p&gt;So while building &lt;strong&gt;RepoBeam&lt;/strong&gt;, I added a before/after preview for operations like going back to an older commit.&lt;/p&gt;
&lt;p&gt;Instead of clicking the action and hoping I understood it correctly, RepoBeam first shows me what the repository will look like afterwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rx5b0flb4q1km0dt0ugp.png&quot; alt=&quot;RepoBeam showing a Before and After commit-history preview for a “Back to this commit” operation, so the user can see exactly how the Git history will change before confirming.&quot;&gt;&lt;/p&gt;
&lt;p&gt;For example, when going back to a commit I can choose whether I want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;restore the files as normal unstaged changes&lt;/li&gt;
&lt;li&gt;restore them already staged&lt;/li&gt;
&lt;li&gt;create a new restore commit when rewriting published history would be unsafe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And RepoBeam shows the expected history before I confirm anything.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;show me what Git is going to do before it does it.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;RepoBeam actually started for a different reason.&lt;/p&gt;
&lt;p&gt;I used &lt;strong&gt;Fork&lt;/strong&gt; for a long time on Windows. When I started working more on Linux, I tried GitKraken, Sublime Merge, SmartGit, GitButler and a bunch of smaller clients, but I never found one that really felt like the Git GUI I wanted to use every day.&lt;/p&gt;
&lt;p&gt;So I started building my own.&lt;/p&gt;
&lt;p&gt;Over time it turned into a full Git client with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;local changes and staging&lt;/li&gt;
&lt;li&gt;split/unified diffs&lt;/li&gt;
&lt;li&gt;commit history and graph&lt;/li&gt;
&lt;li&gt;branches and remotes&lt;/li&gt;
&lt;li&gt;stashes&lt;/li&gt;
&lt;li&gt;guided conflict resolution&lt;/li&gt;
&lt;li&gt;before/after Git previews&lt;/li&gt;
&lt;li&gt;Coding Sessions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;coding-sessions&quot;&gt;Coding Sessions&lt;/h4&gt;
&lt;p&gt;This is another part I use a lot now.&lt;/p&gt;
&lt;p&gt;When I start working with Codex or Claude, I start a Coding Session in RepoBeam first.&lt;/p&gt;
&lt;p&gt;RepoBeam then organizes everything that changes during that session.&lt;/p&gt;
&lt;p&gt;I can review the files afterwards, undo one change, bring it back, keep the rest, or undo the entire session.&lt;/p&gt;
&lt;p&gt;It does not replace Git or create some separate version-control system. It works with normal Git repositories.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;RepoBeam currently runs on &lt;strong&gt;Linux and Windows&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Linux builds are available as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.deb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;AppImage&lt;/li&gt;
&lt;li&gt;Snap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Private repositories work, no RepoBeam account is required, and the app is free to use including for work.&lt;/p&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://repobeam.com&quot;&gt;https://repobeam.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/jnapyun31qyc4pii98np.png&quot; alt=&quot;RepoBeam’s “Back to this commit” preview showing three safe restore options: restore as unstaged changes, restore staged, or create a restore commit while keeping published history intact.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/l3y2vsbn8rpocdm2106b.png&quot; alt=&quot;RepoBeam’s guided conflict resolver showing Local and Server versions side by side, with options to keep Local, keep Server, keep both, or manually edit the final result before continuing the merge.&quot;&gt;&lt;/p&gt;
&lt;p&gt;I released &lt;strong&gt;1.8.3&lt;/strong&gt; this week and this is the first time I'm seriously putting RepoBeam in front of developers I don't know.&lt;/p&gt;
&lt;p&gt;The thing I'm most curious about:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Would a before/after preview like this actually make you more comfortable with Git history operations, or do you prefer seeing the underlying Git command directly?&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29970/i-wanted-git-to-show-me-what-would-happen-before-changing-history-so-i-built-it</guid>
<pubDate>Tue, 06 Oct 2026 09:55:16 +0000</pubDate>
</item>
<item>
<title>Git Submodules: When and How</title>
<link>https://coderlegion.com/29811/git-submodules-when-and-how</link>
<description>&lt;p&gt;&lt;strong&gt;Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#use-cases&quot;&gt;Use Cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#submodules-subtrees-mono--or-multirepo&quot;&gt;Submodules, Subtrees, Mono- or Multirepo?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#a-practical-setup-example&quot;&gt;A Practical Setup Example&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#forkdev&quot;&gt;fork.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#terminal&quot;&gt;Terminal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#daily-workflow&quot;&gt;Daily Workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Git &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-submodule&quot;&gt;submodules&lt;/a&gt; are a built-in Git mechanism for composing repositories at fixed commits. Unlike a monorepo, separate repositories, or worktrees, they let a root repository record exact revisions of independently owned submodules.&lt;/p&gt;
&lt;p&gt;The basic idea is simple: the root repository records a nested repository at an exact commit. The root repository stores the path and remote URL in &lt;code&gt;.gitmodules&lt;/code&gt;, and records the submodule commit as a &lt;code&gt;gitlink&lt;/code&gt;. A clone can later reconstruct the same combination of repositories.&lt;/p&gt;
&lt;h3 id=&quot;use-cases&quot;&gt;Use Cases&lt;/h3&gt;
&lt;p&gt;Briefly: use a submodule when a root repository must record an approved revision of code maintained elsewhere. Here are some useful applications of submodules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep private documentation and deployment configuration in the root repository, with public source code in a submodule.&lt;/li&gt;
&lt;li&gt;Pin the exact revision of a shared library maintained and released separately.&lt;/li&gt;
&lt;li&gt;Record the approved revisions of independently released services or tools.&lt;/li&gt;
&lt;li&gt;Pin a reviewed commit from a maintained fork of an external project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In product development, the root repository represents the product release context: its documentation, configuration, integration decisions, and approved component revisions. Each submodule represents a component with its own codebase, maintainers, and delivery cycle.&lt;/p&gt;
&lt;h3 id=&quot;submodules-subtrees-mono--or-multirepo-&quot;&gt;Submodules, Subtrees, Mono- or Multirepo?&lt;/h3&gt;
&lt;p&gt;Here are some key distinctions, briefly.&lt;/p&gt;
&lt;p&gt;A submodule keeps source independent while the root repository records the exact revision used by the product. It fits when public source and private product material must remain separate but form one reproducible product revision.&lt;/p&gt;
&lt;p&gt;A subtree copies external source into the root repository. Use it when the root repository should own that source.&lt;/p&gt;
&lt;p&gt;A monorepo works well when code, access rules, and release cadence belong together. &lt;a rel=&quot;nofollow&quot; href=&quot;https://nx.dev/&quot;&gt;Nx&lt;/a&gt; and similar monorepo tools allow for task execution, caching, project boundaries, and releases conveniently from the single root repository.&lt;/p&gt;
&lt;p&gt;A multirepo gives each component its own repository, ownership, and history, but does not by itself record which revisions belong together.&lt;/p&gt;
&lt;h3 id=&quot;a-practical-setup-example&quot;&gt;A Practical Setup Example&lt;/h3&gt;
&lt;p&gt;Consider a product with private documentation and deployment configuration, and public source code maintained in a separate repository.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Root repository:&lt;/strong&gt; private; contains private material and one or more submodule references.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Submodule:&lt;/strong&gt; public; contains source code and is its own source of truth.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;root/
├── .gitmodules
├── docs/                    # private specifications and operational material
├── deployment/              # private deployment configuration
└── submodule/               # separately owned source repository
    ├── README.md
    └── src/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The root repository tracks &lt;code&gt;.gitmodules&lt;/code&gt; and the exact commit recorded at &lt;code&gt;submodule/&lt;/code&gt;. The files inside &lt;code&gt;submodule/&lt;/code&gt; belong to the submodule repository.&lt;/p&gt;
&lt;h4 id=&quot;fork-dev&quot;&gt;fork.dev&lt;/h4&gt;
&lt;p&gt;I use here the &lt;a rel=&quot;nofollow&quot; href=&quot;https://fork.dev/&quot;&gt;Fork&lt;/a&gt; Git client. The setup via terminal is in the next section.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create the local folder structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir root
cd root
mkdir docs deployment submodule
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Fork, create a Git repository in &lt;code&gt;root/&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;submodule/&lt;/code&gt; in Fork and create a separate Git repository there. Add an initial file such as &lt;code&gt;README.md&lt;/code&gt;, commit it, and keep this repository independent from &lt;code&gt;root/&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create two empty repositories in your Git hosting service, for example: private: &lt;code&gt;https://github.com/example/root.git&lt;/code&gt;, public: &lt;code&gt;https://github.com/example/submodule.git&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Fork, add &lt;code&gt;https://github.com/example/root.git&lt;/code&gt; as the &lt;code&gt;origin&lt;/code&gt; remote of the &lt;code&gt;root/&lt;/code&gt; repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add &lt;code&gt;https://github.com/example/submodule.git&lt;/code&gt; as the &lt;code&gt;origin&lt;/code&gt; remote of the &lt;code&gt;submodule/&lt;/code&gt; repository, then push its initial commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return to the &lt;code&gt;root/&lt;/code&gt; repository. In the sidebar, right-click &lt;strong&gt;Submodules&lt;/strong&gt; → &lt;strong&gt;Add New Submodule&lt;/strong&gt;. Enter repository URL: &lt;code&gt;https://github.com/example/submodule.git&lt;/code&gt;, local path: &lt;code&gt;submodule&lt;/code&gt;. Fork registers the existing local repository as the submodule.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Commit &lt;code&gt;.gitmodules&lt;/code&gt; and the new &lt;code&gt;submodule&lt;/code&gt; gitlink in the root repository, then push the root repository to its private &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;terminal&quot;&gt;Terminal&lt;/h4&gt;
&lt;p&gt;The same setup from a terminal, creating both repositories locally first and then attaching their remotes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Create the local folder structure.
mkdir root
cd root
mkdir docs deployment submodule

# Initialize the private root repository.
git init -b main
git remote add origin https://github.com/example/root.git

# Initialize the public submodule repository.
cd submodule
git init -b main
echo &quot;# Submodule&quot; &amp;gt; README.md
git add README.md
git commit -m &quot;Initial commit&quot;
git remote add origin https://github.com/example/submodule.git
git push -u origin main

# Return to the root repository and register the existing local repository
# as a submodule.
cd ..
git submodule add --force https://github.com/example/submodule.git submodule

# Commit the root repository state.
git add docs deployment .gitmodules submodule
git commit -m &quot;Add submodule&quot;
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;daily-workflow&quot;&gt;Daily Workflow&lt;/h3&gt;
&lt;p&gt;Treat the submodule commit as a dependency lock. Change code in the submodule repository, then deliberately update the root repository's pointer.&lt;/p&gt;
&lt;p&gt;With that workflow, the repositories keep their separate ownership, history, and visibility, while the root repository still records the exact combination that belongs to the system.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Doble-click on Submodules-&amp;gt;submodule repository to open its tab. Add its source files, stage them, commit them, and push them to the public &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return to the root repository. Add private files under &lt;code&gt;docs/&lt;/code&gt; and &lt;code&gt;deployment/&lt;/code&gt;. Stage &lt;code&gt;docs/&lt;/code&gt;, &lt;code&gt;deployment/&lt;/code&gt;, commit them, and push them to the private &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For private documentation or configuration changes, work in the root repository directly. For source changes, commit and push them in the submodule first, then commit the changed submodule reference in the root repository.&lt;/p&gt;
&lt;p&gt;When cloning later, use recursive initialization:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone --recurse-submodules &amp;lt;root-repository-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For an existing clone, use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git submodule update --init --recursive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;References: &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-submodule&quot;&gt;Git submodule documentation&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Submodules&quot;&gt;Git submodules guide&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/29811/git-submodules-when-and-how</guid>
<pubDate>Mon, 05 Oct 2026 06:47:37 +0000</pubDate>
</item>
<item>
<title>Handling Git Merge Conflicts in Multi-Agent Worktrees</title>
<link>https://coderlegion.com/29698/handling-git-merge-conflicts-in-multi-agent-worktrees</link>
<description>&lt;p&gt;Scaling multi-agent coding swarms using Git worktrees allows multiple autonomous agents to write code simultaneously without disk exhaustion. However, the throughput bottleneck rapidly shifts to the integration boundary. When dozens of agents modify repositories concurrently, development teams face textual merge conflicts, the hazard of automated models misinterpreting conflict markers, and silent semantic drift where code merges cleanly but breaks runtime contracts.&lt;/p&gt;
&lt;h3 id=&quot;the-integration-wall-in-multi-agent-coding&quot;&gt;The Integration Wall in Multi-Agent Coding&lt;/h3&gt;
&lt;p&gt;Generating code in isolated worktrees is straightforward, but converging those changes back into a shared canonical branch creates friction. The primary challenges include textual merge conflicts when two agents touch the same configuration file or route index, and silent semantic drift where a merge succeeds textually while introducing runtime failures because Agent A updated a function signature while Agent B called the deprecated version.&lt;/p&gt;
&lt;h3 id=&quot;the-myth-of-llm-merge-conflict-resolution&quot;&gt;The Myth of LLM Merge Conflict Resolution&lt;/h3&gt;
&lt;p&gt;Prompting an autonomous coding model to fix git conflicts is inherently dangerous. Models frequently hallucinate resolution logic, drop critical changes committed by peer agents, or accidentally preserve broken conflict markers in production builds. Instead of treating the language model as a conflict resolution tool, architectures must prevent conflicts before they reach the merge stage or enforce rigid programmatic validation.&lt;/p&gt;
&lt;h3 id=&quot;path-reservation-architecture&quot;&gt;Path Reservation Architecture&lt;/h3&gt;
&lt;p&gt;Instead of dealing with conflicts after the fact, the orchestrator implements pessimistic resource locks. When a task is dispatched to an agent, its target file paths and dependencies are claimed. If a subsequent task requires writing to overlapping files, the orchestrator enforces serialized dependency ordering rather than parallel execution. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/agent-orchestrator-vs-model-router/&quot;&gt;Agent Orchestrator Vs Model Router&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;task_id&quot;: &quot;task-8842&quot;,
  &quot;assigned_agent&quot;: &quot;agent-beta&quot;,
  &quot;worktree_path&quot;: &quot;/var/lib/worktrees/agent-beta&quot;,
  &quot;reserved_paths&quot;: [
    &quot;src/api/router.ts&quot;,
    &quot;src/models/user.ts&quot;
  ],
  &quot;lock_status&quot;: &quot;acquired&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-serialized-merge-queue-blueprint&quot;&gt;The Serialized Merge Queue Blueprint&lt;/h3&gt;
&lt;p&gt;Coding runs in parallel across isolated worktrees, but merging into the canonical branch is strictly single-lane. When an agent finishes its work, the orchestrator rebases its branch onto the updated main branch, executes automated tests in the rebased context, and performs a fast-forward merge only if all checks pass. If an automated rebase encounters merge conflicts or test regressions, the system fails closed rather than permitting the agent to guess at conflict markers.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Integrating code from concurrent AI &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/git-worktrees-parallel-ai-coding-agents/&quot;&gt;coding agents&lt;/a&gt; requires moving past the illusion that large language models make good merge conflict resolvers. By combining path reservation at the dispatch boundary with a serialized merge queue and fail-closed rebase testing, teams can maintain a stable canonical branch without sacrificing the throughput benefits of parallel worktree execution.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29698/handling-git-merge-conflicts-in-multi-agent-worktrees</guid>
<pubDate>Sun, 04 Oct 2026 06:04:12 +0000</pubDate>
</item>
<item>
<title>Shared Git State in Parallel Agent Worktrees</title>
<link>https://coderlegion.com/29457/shared-git-state-in-parallel-agent-worktrees</link>
<description>&lt;p&gt;When deploying multiple AI coding agents across isolated Git worktrees, it is common to assume complete independence. While worktrees provide separate filesystems, working directories, and HEAD pointers, they share a single underlying object database and branch namespace. This shared architecture introduces subtle failure modes when multiple agents execute git commands concurrently. Understanding these shared state hazards prevents unexpected ref corruption and silent failures &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/managing-concurrent-git-commits-during-automated-publishing/&quot;&gt;during automated&lt;/a&gt; workflows.&lt;/p&gt;
&lt;h3 id=&quot;the-shared-ref-layer-and-mutable-state&quot;&gt;The Shared Ref Layer and Mutable State&lt;/h3&gt;
&lt;p&gt;The core problem stems from how Git structures worktrees. Although each worktree maintains its own checked-out files and staging index, all worktrees in a repository point to the same &lt;code&gt;.git&lt;/code&gt; directory. The object database, reflogs, and branch references are globally mutable across the entire repository. When one agent executes a destructive command like &lt;code&gt;git reset --hard&lt;/code&gt; or &lt;code&gt;git branch -D&lt;/code&gt;, the mutation immediately propagates to the shared object store. Sibling worktrees referencing those same commits or branches observe the change instantly, which can break active diffing, rebasing, or logging operations.&lt;/p&gt;
&lt;p&gt;Consider a scenario where Agent A and Agent B are spawned in parallel. If Agent A performs a destructive force reset on a shared tracking branch, Agent B can experience sudden ref invalidation. The following table outlines the primary failure taxonomy, their triggers, their blast radius, and their corresponding defensive patterns.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Failure Mode &lt;/th&gt;&lt;th&gt;Trigger &lt;/th&gt;&lt;th&gt;Blast Radius &lt;/th&gt;&lt;th&gt;Defensive Pattern&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Cross-Worktree Ref Mutation &lt;/td&gt;&lt;td&gt;&lt;code&gt;git reset --hard&lt;/code&gt; or &lt;code&gt;git branch -D&lt;/code&gt; inside an active worktree &lt;/td&gt;&lt;td&gt;Immediate invalidation of refs in sibling worktrees &lt;/td&gt;&lt;td&gt;Restrict agents to non-destructive commands; delegate all ref pruning to the orchestrator&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Branch Creation Race &lt;/td&gt;&lt;td&gt;Concurrent &lt;code&gt;git worktree add&lt;/code&gt; calls attempting to create identical branch names &lt;/td&gt;&lt;td&gt;Silent failure or hard error in the second agent spawn &lt;/td&gt;&lt;td&gt;Use orchestrator-assigned unique suffixes or agent-id branch prefixes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reflog Contamination &lt;/td&gt;&lt;td&gt;Frequent concurrent commits and fast-forwards to the same local branch &lt;/td&gt;&lt;td&gt;Confused log histories and difficult rollbacks &lt;/td&gt;&lt;td&gt;Isolate working branches strictly per agent instance &lt;/td&gt;&lt;td&gt;For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/git-worktrees-parallel-ai-coding-agents/&quot;&gt;Git Worktrees Parallel Ai Coding Agents&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;preventing-branch-name-collisions&quot;&gt;Preventing Branch Name Collisions&lt;/h3&gt;
&lt;p&gt;Branch name collisions represent another frequent point of failure in automated orchestration. Git prohibits checking out the same branch in more than one worktree simultaneously. If an automation script spawns two agents and both attempt to create or check out &lt;code&gt;agent/fix-login&lt;/code&gt;, the second operation fails. Relying on agents to pick their own names or depending solely on static task descriptions often results in race conditions.&lt;/p&gt;
&lt;p&gt;To eliminate this collision window at the naming layer, branch creation must be owned by the orchestrator prior to spawning the agent. Injecting a short unique suffix into the branch name ensures uniqueness without sacrificing readability.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail

TASK_NAME=&quot;fix-login&quot;
UNIQUE_SUFFIX=&quot;a7b3&quot;
BRANCH_NAME=&quot;agent/${TASK_NAME}-${UNIQUE_SUFFIX}&quot;
WORKTREE_PATH=&quot;.worktrees/${TASK_NAME}-${UNIQUE_SUFFIX}&quot;

git branch &quot;${BRANCH_NAME}&quot; main
git worktree add &quot;${WORKTREE_PATH}&quot; &quot;${BRANCH_NAME}&quot;

echo &quot;Spawned agent in worktree at ${WORKTREE_PATH} on branch ${BRANCH_NAME}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script demonstrates how an orchestrator can provision an isolated worktree with a collision-safe branch name before handing execution over to an agent process. By preventing agents from executing branch creation or deletion commands directly, the repository maintains structural integrity.&lt;/p&gt;
&lt;h3 id=&quot;securing-multi-agent-workflows&quot;&gt;Securing Multi-Agent Workflows&lt;/h3&gt;
&lt;p&gt;Managing shared Git state effectively requires treating the object database as a global resource that only a centralized orchestrator should modify. Agents should be constrained to local commits within their assigned branch. Once an agent completes its task, the orchestrator verifies the changes, merges the results, and handles branch deletion during the cleanup phase. Adopting this defensive lifecycle prevents race conditions and ensures that parallel workflows scale reliably without stepping on each other refs.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29457/shared-git-state-in-parallel-agent-worktrees</guid>
<pubDate>Fri, 02 Oct 2026 02:00:46 +0000</pubDate>
</item>
<item>
<title>How to Show Engineering Ownership When the Repositories Are Private</title>
<link>https://coderlegion.com/27763/how-to-show-engineering-ownership-when-the-repositories-are-private</link>
<description>&lt;blockquote&gt;&lt;p&gt;Originally published on 30 July 2026 at &lt;a rel=&quot;nofollow&quot; href=&quot;https://efe-genc-portfolio.vercel.app/writing/showing-ownership-private-repositories/&quot;&gt;efe-genc-portfolio.vercel.app&lt;/a&gt;. That version is the maintained one; the figures below are measurements taken on the dates stated and are not updated here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have spent more than four years shipping production software, and I have no public repositories worth showing you. Everything substantial I have written at work lives in private repositories that belong to the companies I wrote it for, and confidentiality obligations do not lapse when a role ends. So I cannot hand you the code, and I am not going to.&lt;/p&gt;
&lt;p&gt;I do not say that to sound careful. An engineer who sends a prospective employer another company's source code has told you exactly what they will do with yours. The restraint is part of what you are assessing, and I would want the same restraint applied to my own work later.&lt;/p&gt;
&lt;p&gt;That leaves a real problem. &quot;I was the principal author of the web application&quot; and &quot;I contributed to the web application&quot; are the same sentence to a reader who cannot check either one. Most engineering CVs resolve this by getting louder. The only honest resolution I have found is to publish the &lt;strong&gt;measurement method&lt;/strong&gt; instead of the artefact, in enough detail that someone who knows the tools can judge the figure on its own terms without ever seeing the repository.&lt;/p&gt;
&lt;p&gt;Here is the method I used, what it does badly, and the places where applying it forced me to shrink a claim I would have preferred to keep.&lt;/p&gt;
&lt;h3 id=&quot;three-measurements-in-increasing-order-of-rigour&quot;&gt;Three measurements, in increasing order of rigour&lt;/h3&gt;
&lt;p&gt;The first is the all-branch commit count: total authored activity. Do not use it. It counts abandoned branches, spikes and experiments, and it inflates in favour of whoever is most willing to commit. It is the number that makes contribution graphs look impressive and says the least about a codebase.&lt;/p&gt;
&lt;p&gt;The second is the production-branch commit count, scoped to tenure: what reached customers while you were there. This is better, because it excludes work that never shipped, and because scoping to tenure stops you from taking credit for years of history that predate you, or being penalised for them. But it still counts commits, and a commit is a unit of activity, not a unit of code. Commit share rewards granular committers and punishes people who work in larger increments. It is the weakest of the three, and unfortunately the easiest to produce, which is why it is the one people quote.&lt;/p&gt;
&lt;p&gt;The third is &lt;code&gt;git blame&lt;/code&gt; on the production branch: which lines are alive in production today, and who wrote them. This is the authoritative one, because it measures surviving authorship. The code was written, shipped, survived every later refactor by everyone else, and is running now. Its weakness is that it is slow, so it has to be sampled.&lt;/p&gt;
&lt;p&gt;Two details make the blame figure defensible, and they carry the whole exercise.&lt;/p&gt;
&lt;p&gt;The flags. I ran &lt;code&gt;git blame -w -M&lt;/code&gt; against the production branch. &lt;code&gt;-w&lt;/code&gt; ignores whitespace-only changes and &lt;code&gt;-M&lt;/code&gt; detects lines moved or copied within a file. Both make the result &lt;em&gt;more conservative&lt;/em&gt; rather than more flattering, because reformatted or relocated code is attributed to whoever originally wrote it instead of whoever last touched it. If I had wanted a bigger number I would have left both flags off, and anybody who knows the command can verify that without access to anything.&lt;/p&gt;
&lt;p&gt;The sampling. Deterministic, every Nth file across the sorted file list. I did not hand-pick a directory, and I did not take a random draw that I could have re-rolled until it flattered me. Anyone with the same repository can reproduce it. Sampling by directory is where this kind of measurement usually goes wrong, because directories are exactly where authorship clusters.&lt;/p&gt;
&lt;h3 id=&quot;what-that-produced-and-why-the-gap-is-the-interesting-part&quot;&gt;What that produced, and why the gap is the interesting part&lt;/h3&gt;
&lt;p&gt;At &lt;a rel=&quot;nofollow&quot; href=&quot;https://efe-genc-portfolio.vercel.app/projects/cendra/&quot;&gt;Cendra&lt;/a&gt;, where I was one of two founding engineers, blame analysis on a deterministic one-in-seven sample of the production TypeScript sources (several hundred files) attributes &lt;strong&gt;84.7%&lt;/strong&gt; of surviving frontend code to me, with the remainder spread across eight other contributors. My production-branch commit share over the same period, scoped to my tenure, is &lt;strong&gt;80.7%&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The script is public now as &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Bubblegunn/surviving-lines&quot;&gt;surviving-lines&lt;/a&gt;: one file, no dependencies, the same deterministic sample and the same &lt;code&gt;git blame -w -M&lt;/code&gt;, with the caveats printed under every table so the number cannot travel without them. The whole report, six figures with the command behind each and a hash that anyone with the repository can verify, is &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Bubblegunn/workproof&quot;&gt;workproof&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The gap between those two numbers is the claim, and it points in the direction most people would not expect. My share of surviving lines is &lt;em&gt;higher&lt;/em&gt; than my share of commits, which means code written by others was disproportionately replaced by code written by me. I did more than add volume to a growing surface; I rewrote and consolidated the one that was already there. That is an argument about architectural ownership rather than throughput, and it is why I describe myself as principal author instead of the person who happened to commit the most.&lt;/p&gt;
&lt;p&gt;Had the ratio gone the other way, with commit share above blame share, the honest reading would be that I produced a lot of code that other people later replaced. That is a real outcome and it happens. A method that could not have shown it would not be a measurement.&lt;/p&gt;
&lt;p&gt;Some figures need no method at all. Every commit to the &lt;code&gt;ios/&lt;/code&gt; and &lt;code&gt;android/&lt;/code&gt; projects across the entire history of the repository is mine, 47 and 28 respectively, along with the Capacitor configuration and the Playwright end-to-end suites at 11 of 11 each. No other author has ever committed to those paths. That is the least arguable claim I have, and it is arithmetic rather than analysis.&lt;/p&gt;
&lt;p&gt;I measured all of these while I held authorised access, using ordinary &lt;code&gt;git&lt;/code&gt; invocations. I am not offering to demonstrate them live, and you should be wary of anyone who would. Opening a former employer's private repository in a screen share is a breach in itself.&lt;/p&gt;
&lt;h3 id=&quot;the-method-is-most-useful-when-it-makes-you-shrink-things&quot;&gt;The method is most useful when it makes you shrink things&lt;/h3&gt;
&lt;p&gt;The uncomfortable half of publishing a method is that it applies to the parts of the work you were less central to, and it does not let you round them up.&lt;/p&gt;
&lt;p&gt;The same blame analysis run against the production C# sources attributes &lt;strong&gt;23.2%&lt;/strong&gt; of surviving backend code to me, with a 30.9% commit share over my tenure. I joined the backend roughly six months into a twelve-month tenure. It had been under active development for eighteen months before I touched it, by engineers who are still there. So the accurate sentence is that I was a substantial contributor to a codebase I joined late, and I was not its architect. I have tried hard not to imply otherwise anywhere.&lt;/p&gt;
&lt;p&gt;Where that contribution sits turns out to be more informative than the total. My share of commits since joining runs highest in the test suite at 83.2%, the domain services at 53.6% and the event-processing consumer at 55.4%, and lowest in the infrastructure at 24.1%, the core domain at 20.8% and the CQRS feature handlers at 16.6%. Low in shared CRUD surfaces, high where architectural judgement was required. I was not the highest-volume backend contributor. I owned the parts that needed design, and the test suite the rest of the team relied on. Other contributors' shares are their own information, and they are not mine to publish.&lt;/p&gt;
&lt;p&gt;Across all four repositories, scoped to my tenure, the combined figure is 5,717 of 10,402 production-branch commits, or 55.0%. That is the most conservative aggregate available, and I record it for that reason, but I would not lead an argument with it. A single number that spans a frontend I was principal author of and an integration service I touched occasionally is not really a claim about either. Any aggregate over unlike repositories describes them worse than the per-repository figures it averages. That is why the breakdown above exists, and why the 84.7% is the number I would defend first.&lt;/p&gt;
&lt;p&gt;The method cannot measure two more boundaries, but honesty requires them.&lt;/p&gt;
&lt;p&gt;The core AI architecture at Cendra was not mine. Our AI engineer owned the LangGraph services, the agent workflows and the RAG pipelines. I built the product layer those capabilities were surfaced through (agent configuration, the knowledge base, the agentic rule-authoring interface and the in-product assistant) and the transport between the two, over AG-UI, server-sent events and the realtime hub. I contributed to how the rule and guardrail semantics were shaped, because building an interface through which a non-technical operator expresses a rule an engine will enforce requires understanding those semantics as deeply as the person implementing the enforcement. But I did not architect the orchestration. &quot;I worked on the AI product&quot; is the easiest sentence in this industry to say and the least informative.&lt;/p&gt;
&lt;p&gt;Cendra's product-level metrics are not mine either. Automation rates and platform reach are company figures published by the company. I contributed to the systems behind them. I did not measure them, and presenting a company outcome as a personal one is the specific move that makes every other number on a CV suspect.&lt;/p&gt;
&lt;h3 id=&quot;where-commit-share-is-all-you-have&quot;&gt;Where commit share is all you have&lt;/h3&gt;
&lt;p&gt;At &lt;a rel=&quot;nofollow&quot; href=&quot;https://efe-genc-portfolio.vercel.app/projects/readyfly/&quot;&gt;ReadyFly&lt;/a&gt;, a part-time founding-engineer role, I have commit shares and nothing better: 157 of 209 commits to the web application, roughly three quarters, and I made the first one; and 31 of 209 to the Python API. So I say principal author of the first and contributor to the second, and I do not dress the 15% up. The AI models themselves, semantic matching and candidate evaluation, were owned by the team's AI engineer. I built the product layer they were surfaced through, and the browser side of that story is in &lt;a rel=&quot;nofollow&quot; href=&quot;https://efe-genc-portfolio.vercel.app/writing/an-interview-inside-the-browser/&quot;&gt;An Interview Inside the Browser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Commit share is the weakest of the three measurements. When it is the only one available, the right response is to say so, rather than to present it in the same voice as a blame figure.&lt;/p&gt;
&lt;h3 id=&quot;what-closes-the-gap&quot;&gt;What closes the gap&lt;/h3&gt;
&lt;p&gt;Method plus disclosure gets you to a claim that can be &lt;em&gt;judged&lt;/em&gt;. It does not get you to a claim that has been independently &lt;em&gt;confirmed&lt;/em&gt;, and I do not think any amount of self-published measurement does.&lt;/p&gt;
&lt;p&gt;Three things do. Process artefacts that exist independently of my description of them: &lt;a rel=&quot;nofollow&quot; href=&quot;https://efe-genc-portfolio.vercel.app/writing/the-spec-is-the-fast-path/&quot;&gt;255 design specifications, 207 implementation plans and 17 production runbooks&lt;/a&gt;, alongside 377 production releases coordinated across four repositories over 239 active development days in a twelve-month tenure. Two Cendra co-founders who have agreed to act as references and can confirm scope and ownership without me in the room. And an offer that costs me nothing to make, because it is the part I am confident about: a walkthrough of the systems that are mine, line by line, including the parts I would now build differently.&lt;/p&gt;
&lt;p&gt;The measurements are there so the conversation can start somewhere better than mutual assertion. They are not the evidence. They describe how I would go about finding out, and I have published them so that you can disagree with them.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27763/how-to-show-engineering-ownership-when-the-repositories-are-private</guid>
<pubDate>Sat, 19 Sep 2026 19:40:20 +0000</pubDate>
</item>
<item>
<title>The Three States of a Secret: Tracked, Ignored, and the One That Ships It</title>
<link>https://coderlegion.com/27186/the-three-states-of-a-secret-tracked-ignored-and-the-one-that-ships-it</link>
<description>&lt;hr&gt;
&lt;p&gt;Every Git tutorial teaches three states. Modified, staged, committed: the working directory, the staging area, the repository. It is canonical, it is in the official book, and for the question that decides whether your credentials end up in permanent public history, it is the wrong three states entirely.&lt;/p&gt;
&lt;p&gt;The axis that matters for a secret is different. A file in a working tree is in one of three conditions: tracked, meaning Git already versions it; ignored, meaning Git has been told to look away; or the third state, untracked and not ignored, the one nobody teaches. A file in the third state shows up in every status listing. It is included in every &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git add -A&lt;/code&gt;. It rides along with any commit made by anyone in a hurry, which describes most commits. The first two states are decisions. The third state is an accident waiting for a keystroke.&lt;/p&gt;
&lt;p&gt;Last week we found a credential living in exactly that state, in our own shared repository.&lt;/p&gt;
&lt;h3 id=&quot;a-list-with-a-gap&quot;&gt;A list with a gap&lt;/h3&gt;
&lt;p&gt;The file was a QA auto-login token: a per-machine secret, minted automatically, that lets a local test harness skip a login screen. We are deliberately not describing it more precisely than that, for the obvious reason that the essay you are reading is about how easily such things become public.&lt;/p&gt;
&lt;p&gt;What made the find instructive was not the token. It was the neighborhood. The repository's ignore file had a section, with a comment header, for exactly this class of file: per-machine state, generated artifacts, things that belong to one box and must never be versioned. The token's class-siblings were all correctly listed there. A deploy marker: ignored. A generated test-state directory: ignored. The token itself: absent from the list. Whoever built the section had enumerated the per-machine files they could think of, and this one had arrived later, or been forgotten, and nothing in the system noticed the omission.&lt;/p&gt;
&lt;p&gt;So the failure was clerical, not conceptual. Nobody misunderstood security. Nobody made a bad call. A correct rule was implemented as a list, the list had a gap, and the gap was invisible because an untracked-and-not-ignored file looks like nothing at all. It does not fail a build. It does not trip a scanner that only reads committed content. It sits in the status listing of a busy repository, camouflaged among the genuinely new files that appear and disappear all day, one &lt;code&gt;add -A&lt;/code&gt; away from being versioned, pushed, and preserved.&lt;/p&gt;
&lt;p&gt;That phrase, one commit away from permanent, sounds like rhetoric. It is not, and the reason it is not is worth two minutes of your attention.&lt;/p&gt;
&lt;h3 id=&quot;permanent-means-permanent-by-design&quot;&gt;Permanent means permanent, by design&lt;/h3&gt;
&lt;p&gt;In 2024, researchers at Truffle Security documented what they called Cross Fork Object Reference: on GitHub, a commit made to any repository in a fork network can be accessed from any other repository in that network, by supplying the commit hash directly. The consequences are stranger than they sound. Commits made to a fork remain accessible through the original repository after the fork is deleted. Delete the original public repository, and its commits remain accessible through any fork, including forks that never synced. Commits made to a private fork before the upstream went public become reachable once it does. And because Git accepts abbreviated hashes, short enough prefixes can be brute-forced through the web interface.&lt;/p&gt;
&lt;p&gt;To demonstrate, the researchers reviewed three commonly forked public repositories from a major AI company and found forty valid API keys in commits that existed only in deleted forks. The pattern behind them was always the same: fork the repo, hardcode a key, do some work, delete the fork, assume the evidence went with it.&lt;/p&gt;
&lt;p&gt;GitHub, responding through its vulnerability disclosure program, said the behavior is an intentional design decision and is working as expected. Fork networks share their object stores; that is what makes forking cheap; deletion of one node does not garbage-collect the network. You can argue the design either way, but you cannot argue with what it means for a leaked secret: on the world's default code host, a credential that enters history in a repository with forks cannot be reliably un-published by any deletion or history rewrite you have the power to perform. The scrub tools clean your copy. The network remembers.&lt;/p&gt;
&lt;p&gt;Which reframes the little file in our status listing. The distance between &quot;sitting untracked on one machine&quot; and &quot;recoverable forever by strangers&quot; was not a security boundary. It was one inattentive command by any collaborator, on any morning.&lt;/p&gt;
&lt;h3 id=&quot;the-scale-of-the-middle-state&quot;&gt;The scale of the middle state&lt;/h3&gt;
&lt;p&gt;If this were rare, it would be an anecdote. GitGuardian's State of Secrets Sprawl report for 2026 counted 28.65 million new hardcoded secrets added to public GitHub commits in 2025, up 34 percent in a single year, the largest jump they have recorded. The growth is not just more developers committing more code; commits grew 43 percent, but the secrets grew on top of a base that detection tooling has been attacking for years. Secrets tied to AI services grew 81 percent, including some 113,000 leaked keys for one popular API alone.&lt;/p&gt;
&lt;p&gt;Two of the report's findings matter more than the raw count.&lt;/p&gt;
&lt;p&gt;First: private repositories are roughly six times more likely than public ones to contain hardcoded secrets. Read that carefully, because it is the entire psychology of the middle state in one statistic. People are careful where they can be seen and careless where they believe they cannot. The private repo feels like a drawer; developers treat it like one. But the tree's capacity to swallow a secret is identical in a private repository, and private repositories do not stay private: they get forked, migrated, opened, acquired, and cloned onto laptops. The QA token in our tree was &quot;only&quot; on a shared internal repo. Six-x is what &quot;only internal&quot; does to discipline, measured at population scale.&lt;/p&gt;
&lt;p&gt;Second: leaked credentials do not die. Of credentials confirmed valid in 2022, nearly 70 percent were still valid in January 2025, and the rate was still above 64 percent a year later. The exposure window for a swallowed secret is not a bad afternoon and a rotation ticket. Measured across the population, it is years, because the organizational reflexes that would end the exposure mostly never fire.&lt;/p&gt;
&lt;p&gt;Put the two datasets together and the middle state stops looking like a pedantic edge case. Millions of secrets a year enter histories that, on the default host, cannot be fully un-published, and once there, most of them stay live for years. Every one of them passed through the third state on the way.&lt;/p&gt;
&lt;h3 id=&quot;the-mirror&quot;&gt;The mirror&lt;/h3&gt;
&lt;p&gt;One more number from the same report, and it is about us. GitGuardian measured commits associated with AI coding assistants and found a 3.2 percent secret-leak rate, against a 1.5 percent baseline across public commits generally. Roughly double. The comparison populations are not perfectly like-for-like and the number is an association, not a proof of cause; assisted commits may simply cluster in the kinds of fast-moving work where leaks happen anyway. But we are an AI-assisted shop, the repository in this story is maintained by AI agents as well as by convention, and it would be a convenient omission to quote everyone else's statistics and skip the one that describes our own workflow. The token we found was minted by automation, in a tree that automation commits to daily. Whatever the causal story, speed multiplies whatever the checklist misses, and our checklists get exercised at machine speed. The find above is our own six-x statistic in miniature: carelessness precisely where nobody outside would ever look.&lt;/p&gt;
&lt;h3 id=&quot;deny-by-default-and-the-scope-of-the-fix&quot;&gt;Deny by default, and the scope of the fix&lt;/h3&gt;
&lt;p&gt;Here is what we did not do: we did not rotate the token. Before anyone quotes the incident-response handbook, notice the scope. This credential never left the third state. Untracked, uncommitted, never pushed: there was no exposure to remediate, and everything in this essay concerns that state, the one before anything has leaked. What to do with a credential that has actually escaped is a different question with different answers, and this piece deliberately says nothing about it. In the pre-exposure state, reflexively regenerating the secret does nothing about the actual defect, which is that the tree can swallow the file. Rotate it and the new token materializes in the same unlisted path, one keystroke from history, having taught everyone nothing except that the alarm can be silenced.&lt;/p&gt;
&lt;p&gt;The fix for the state is structural, and it is the same fix the six-x statistic points at: stop depending on someone remembering to extend a list. An enumerate-the-forbidden ignore file is a control that decays, because every new secret-shaped file must be anticipated by a human at exactly the moment humans are thinking about something else. The durable versions are deny-by-default. Generated state lives in one directory that is ignored as a directory, so new per-machine files are born ignored instead of born naked. Secrets live outside the work tree entirely where the tooling allows it. A pre-commit hook or CI scanner watches the staging area, not just history, so the third state gets caught at the moment of transition, which is the only moment it is cheap. And the status listing itself is treated as a signal: a repository whose &lt;code&gt;git status&lt;/code&gt; is habitually noisy is a repository where one more untracked file is invisible, and invisible is the whole problem.&lt;/p&gt;
&lt;p&gt;None of this is exotic. That is rather the point. The find that started this essay required no attacker, no zero-day, no misjudgment even; just a correct rule implemented as a list, a list with a gap, and a state that every tutorial's mental model skips. Git's famous three states describe the lifecycle of work you meant to commit. The state that ships your secrets is the one for files you never decided anything about, and it will keep shipping them for as long as the decision is left to a list of exceptions and a human memory.&lt;/p&gt;
&lt;p&gt;Check your own status listing today. Not the history: the listing. Count the files that are neither tracked nor ignored, and ask, for each one, what happens on the day someone in a hurry types &lt;code&gt;git add -A&lt;/code&gt;. If the answer for any of them is &quot;that must never be committed,&quot; you are one omitted line away from a statistic that stays valid for years. The line costs ten seconds. The middle state is the only place it can still be written that cheaply.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;sources-1&quot;&gt;Sources&lt;/h3&gt;
&lt;p&gt;· GitGuardian, &lt;em&gt;The State of Secrets Sprawl 2026&lt;/em&gt; — the 28.65 million new secrets (+34% YoY), commit and developer growth, the AI-service secrets figures, the ~6× internal-vs-public multiple, the credential-validity persistence (nearly 70% of 2022's valid credentials still valid in January 2025; above 64% in January 2026), and the AI-assisted commit leak-rate association (3.2% vs 1.5% baseline).&lt;/p&gt;
&lt;p&gt;· Truffle Security, &lt;em&gt;Anyone can Access Deleted and Private Repository Data on GitHub&lt;/em&gt; — the Cross Fork Object Reference write-up: the three persistence paths, the forty valid API keys recovered from deleted forks, and GitHub's disclosure-program response that the behavior is an intentional design decision.&lt;/p&gt;
&lt;p&gt;· Pro Git (git-scm.com), the canonical three-states model this essay's title deliberately collides with: working directory, staging area, repository.&lt;/p&gt;
&lt;p&gt;· The QA-token find is our own, from a shared internal repository, described generically throughout for the reason the essay gives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Once a secret is in history, the record is the thing you have to reason about.&lt;/strong&gt; Chain of Consciousness records what an agent actually did, step by step, so the reading and the decision live in the same artifact.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/hosted-coc/&quot;&gt;Hosted Chain of Consciousness&lt;/a&gt;  ·  &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/verify/&quot;&gt;Verify a record&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install chain-of-consciousness&lt;/code&gt;  ·  &lt;code&gt;npm install chain-of-consciousness&lt;/code&gt;&lt;/p&gt;
</description>
<category>Security</category>
<guid isPermaLink="true">https://coderlegion.com/27186/the-three-states-of-a-secret-tracked-ignored-and-the-one-that-ships-it</guid>
<pubDate>Mon, 14 Sep 2026 18:23:38 +0000</pubDate>
</item>
<item>
<title>Building KOS: A Journey Through Zero Dependencies (And the Bugs That Taught Me Everything)</title>
<link>https://coderlegion.com/26489/building-kos-a-journey-through-zero-dependencies-and-the-bugs-that-taught-me-everything</link>
<description>&lt;p&gt;I didn't set out to reject &lt;code&gt;go get&lt;/code&gt;. I just wanted to build something clean. But somewhere between the first snapshot corruption and the fourth complete rewrite, I realized: &lt;strong&gt;zero dependencies wasn't a constraint. It was a feature.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the real story of KOS—what I built, what broke, why I fixed it the way I did, and how you can use it yourself.&lt;/p&gt;
&lt;h3 id=&quot;what-is-kos-&quot;&gt;What is KOS?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/04l75tb068knzhkb54sp.jpg&quot; alt=&quot;Kos-front&quot;&gt;&lt;/p&gt;
&lt;p&gt;KOS is a portable, encrypted version control system. It's not trying to replace Git. It's trying to be something Git isn't:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single file&lt;/strong&gt;: Everything lives in one &lt;code&gt;main.go&lt;/code&gt; (~2000 lines)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero dependencies&lt;/strong&gt;: 100% Go standard library&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encrypted&lt;/strong&gt;: AES-256-CFB with HMAC-SHA256 integrity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portable&lt;/strong&gt;: Runs from a USB stick, no installation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple&lt;/strong&gt;: &lt;code&gt;kos init&lt;/code&gt; → &lt;code&gt;kos save &quot;message&quot;&lt;/code&gt; → &lt;code&gt;kos view&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It stores your project snapshots in a global vault (&lt;code&gt;~/.kos-global/&lt;/code&gt;) indexed by UUID. No Git databases. No complex merge logic. Just snapshots and timelines.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/u81ozodhj2nk6737zxcw.jpg&quot; alt=&quot;Kos-ecosystem&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;getting-started-how-to-use-kos&quot;&gt;Getting Started: How to Use KOS&lt;/h3&gt;
&lt;h4 id=&quot;installation&quot;&gt;Installation&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/kosmoscpp/kos
cd kos
make install  # Linux/Mac
# or: build.bat  # Windows
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it. One binary. No dependencies to install. You can even compile it on a machine without Go and ship the binary to another machine.&lt;/p&gt;
&lt;h4 id=&quot;basic-workflow&quot;&gt;Basic Workflow&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Initialize a project:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos init
# ✓ Initialized empty KOS repository in /home/user/.kos-global/abc123...
# Project UUID: abc123-def456-ghi789-...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Create snapshots:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos save &quot;Initial commit&quot;
kos save &quot;Added authentication&quot;
kos save &quot;Fixed bug in auth flow&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;View your timeline:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos view
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;╔══════════════════════════════════════════════════════════╗
║                     KOS TIMELINE                         ║
╚══════════════════════════════════════════════════════════╝

  ● abc1e2f1 ┃ Jan 08, 14:32 (just now) ┃ Fixed bug in auth flow
  │           ┃  42 files
  │
  ● a2d3f4b5 ┃ Jan 08, 14:15 (15 mins ago) ┃ Added authentication
  │           ┃  41 files
  │
  ● 8c7e9a0b ┃ Jan 08, 12:00 (2 hours ago) ┃ Initial commit
  │           ┃  40 files
  └── (End of history)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Compare snapshots:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos diff abc1e2f1 a2d3f4b5
# Shows: + Added (0), ~ Modified (2), - Deleted (0)

kos diff abc1e2f1 a2d3f4b5 auth.go
# Shows line-by-line diff for auth.go
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Tag releases:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos tag v1.0
kos tag stable abc1e2f1  # Tag a specific snapshot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Restore your work:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos checkout abc1e2f1                    # Restore entire snapshot
kos checkout abc1e2f1 src/main.go        # Restore single file
kos checkout v1.0                        # Restore by tag
kos checkout HEAD~3                      # Go back 3 commits
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Encrypted snapshots:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/nbtfzj2enjn944oidxe2.jpg&quot; alt=&quot;Kos-Security&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos save --encrypt &quot;Secret credentials&quot;
# Password: ****
# Confirm: ****
# 🔐 Saved encrypted snapshot: Secret credentials (15 files)

kos share abc1e2f1  # Export as snap_abc1e2f1.tar.gz.enc
# Can email/USB/cloud this file. Recipient loads with:
kos load snap_abc1e2f1.tar.gz.enc
# Password: ****
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Project management:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos projects           # List all projects in global store
kos rename &quot;my-blog&quot;   # Rename current project
kos stats              # Show project statistics
kos search &quot;refactor&quot;  # Search commit messages
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-bugs-and-why-they-matter-&quot;&gt;The Bugs (And Why They Matter)&lt;/h3&gt;
&lt;p&gt;This is where it gets real. Because building from scratch means building through everything.&lt;/p&gt;
&lt;h4 id=&quot;bug-1-head-recognition-fails-the-identity-crisis-&quot;&gt;Bug #1: HEAD Recognition Fails (The Identity Crisis)&lt;/h4&gt;
&lt;p&gt;Early on, I implemented KOS to share and diff by snapshot ID. Simple, right?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos share HEAD        # Error: Snapshot 'HEAD' not found
kos diff v1.0 v1.1   # Error: Snapshot 'v1.0' not found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only raw IDs like &lt;code&gt;18d03947&lt;/code&gt; worked. Tags and HEAD references were rejected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The root cause:&lt;/strong&gt; I wrote separate lookup functions that did direct ID matching instead of using a reference resolver:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// WRONG: Direct lookup, doesn't handle special refs
func findSnapshot(id string) *Snapshot {
    for i := range manifest.Snapshots {
        if manifest.Snapshots[i].ID == id {
            return &amp;amp;manifest.Snapshots[i]
        }
    }
    return nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; I extracted reference resolution into a single function that handles all cases:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// CORRECT: Handles IDs, tags, HEAD, and HEAD~N
func resolveRef(ref string) *Snapshot {
    snaps := manifest.Snapshots
    if ref == &quot;HEAD&quot; {
        return &amp;amp;snaps[len(snaps)-1]
    }
    if strings.HasPrefix(ref, &quot;HEAD~&quot;) {
        parts := strings.Split(ref, &quot;~&quot;)
        var offset int
        fmt.Sscanf(parts[1], &quot;%d&quot;, &amp;amp;offset)
        idx := len(snaps) - 1 - offset
        if idx &amp;gt;= 0 &amp;amp;&amp;amp; idx &amp;lt; len(snaps) {
            return &amp;amp;snaps[idx]
        }
    }
    for i := range snaps {
        if snaps[i].ID == ref || snaps[i].Tag == ref {
            return &amp;amp;snaps[i]
        }
    }
    return nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now every command uses &lt;code&gt;resolveRef()&lt;/code&gt;. Git-like references work everywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; Centralize your logic. Don't repeat the same lookup in ten places. You'll catch edge cases faster and users won't hit weird state machines.&lt;/p&gt;
&lt;h4 id=&quot;bug-2-encryption-produces-garbage-the-defer-nightmare-&quot;&gt;Bug #2: Encryption Produces Garbage (The Defer Nightmare)&lt;/h4&gt;
&lt;p&gt;Encrypted snapshots were completely corrupted. Trying to load them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Error: unexpected EOF when trying to decrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was encrypting an incomplete file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The root cause:&lt;/strong&gt; In &lt;code&gt;cmdSave()&lt;/code&gt;, I used &lt;code&gt;defer&lt;/code&gt; for everything:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// WRONG: Defer happens at function exit, but we encrypt before that!
file, err := os.Create(filepathTar)
defer file.Close()

gzw := gzip.NewWriter(file)
defer gzw.Close()

tw := tar.NewWriter(gzw)
defer tw.Close()

// ... add files to tar ...

if encrypt {
    encryptFile(filepathTar, encPath, password)  // File still open!
    // Defer hasn't run yet. File is incomplete. We're encrypting garbage.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file was still being written when I tried to encrypt it. The tar writer hadn't flushed. The gzip writer hadn't compressed. The file wasn't closed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Explicit close order before encryption:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// CORRECT: Close in dependency order before encryption
tw.Close()      // Tar closes first
gzw.Close()     // Gzip flushes compression
file.Close()    // File closes last

if encrypt {
    encryptFile(filepathTar, encPath, password)  // Now it's complete
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a painful lesson in writer layering. Each writer depends on the one below it. You have to close from the top down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; Don't let &lt;code&gt;defer&lt;/code&gt; fool you into false security. When you have layered resources (writer → compressor → file), you need explicit, ordered cleanup. &lt;code&gt;defer&lt;/code&gt; is great for simple cases. For complex ones, be explicit.&lt;/p&gt;
&lt;h4 id=&quot;bug-3-encrypted-load-shows-zero-commits-the-skip-logic-backfire-&quot;&gt;Bug #3: Encrypted Load Shows Zero Commits (The Skip Logic Backfire)&lt;/h4&gt;
&lt;p&gt;When loading encrypted snapshots:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kos load snap_abc123.tar.gz.enc
# ✓ Loaded encrypted project successfully!
# Commits: 0  ← WRONG! Should be 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All the commits disappeared.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The root cause:&lt;/strong&gt; The extract function had smart logic to skip &lt;code&gt;.kos/&lt;/code&gt; metadata when extracting:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// This is correct for normal use
if strings.HasPrefix(header.Name, &quot;.kos/&quot;) {
    continue  // Skip metadata files
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the encrypted load code needed to read &lt;code&gt;.kos/manifest.json&lt;/code&gt; to show how many commits existed. Since it was being skipped, the manifest was empty.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Use a different extraction function for encrypted loads that doesn't skip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// extractFileFromTar reads directly from tar without skip logic
manifestContent, err := extractFileFromTar(decryptedPath, &quot;.kos/manifest.json&quot;)
json.Unmarshal([]byte(manifestContent), &amp;amp;embeddedManifest)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now encrypted snapshots preserve their full history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; When you have branching logic (normal extract vs. encrypted load), they need different paths. Don't let one code path's assumptions break another's.&lt;/p&gt;
&lt;h4 id=&quot;bug-4-password-masking-without-x-term-the-syscall-journey-&quot;&gt;Bug #4: Password Masking Without x/term (The Syscall Journey)&lt;/h4&gt;
&lt;p&gt;I wanted masked password input (asterisks instead of plaintext). The easy way: &lt;code&gt;golang.org/x/term&lt;/code&gt;. But that breaks zero dependencies.&lt;/p&gt;
&lt;p&gt;So I went low-level. POSIX syscalls. &lt;code&gt;SYS_IOCTL&lt;/code&gt;. Terminal state manipulation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;func readPassword() string {
    fd := int(os.Stdin.Fd())
    oldState, err := getTerminalState(fd)
    if err != nil {
        // Fallback if we can't control terminal
        var password string
        fmt.Scanln(&amp;amp;password)
        return password
    }
    
    newState := oldState
    newState.Lflag &amp;amp;^= syscall.ECHO  // Disable echo
    setTerminalState(fd, newState)
    defer setTerminalState(fd, oldState)
    
    var password []byte
    buf := make([]byte, 1)
    for {
        n, err := os.Stdin.Read(buf)
        if err != nil || n == 0 || buf[0] == '\n' {
            break
        }
        if buf[0] == 127 || buf[0] == 8 {  // Backspace
            if len(password) &amp;gt; 0 {
                password = password[:len(password)-1]
                fmt.Print(&quot;\b \b&quot;)  // Visual backspace
            }
        } else {
            password = append(password, buf[0])
            fmt.Print(&quot;*&quot;)
        }
    }
    return string(password)
}

func getTerminalState(fd int) (syscall.Termios, error) {
    var state syscall.Termios
    _, _, errno := syscall.Syscall6(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(syscall.TCGETS),
        uintptr(unsafe.Pointer(&amp;amp;state)),
        0, 0, 0,
    )
    if errno != 0 {
        return state, errno
    }
    return state, nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The tricky part:&lt;/strong&gt; One wrong flag bit and users see their passwords on screen. The POSIX terminal model is finicky. This code works on Linux. Windows POSIX layer might have issues (but that's acceptable for a hackathon).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; Sometimes the &quot;easy&quot; dependency exists for a reason. But understanding the low-level mechanism? That's worth more than convenience. Now I know how terminal echo works. I know what &lt;code&gt;TCGETS&lt;/code&gt; and &lt;code&gt;TCSETS&lt;/code&gt; do. I know the bitfield layout of &lt;code&gt;Termios&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;bug-5-the-pbkdf2-myth-and-correcting-a-senior-engineer-&quot;&gt;Bug #5: The PBKDF2 Myth (And Correcting a Senior Engineer)&lt;/h4&gt;
&lt;p&gt;During review, someone asked: &quot;Why SHA256 for key derivation instead of PBKDF2?&quot;&lt;/p&gt;
&lt;p&gt;They said: &quot;It's in the standard library.&quot;&lt;/p&gt;
&lt;p&gt;It's not.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;crypto/pbkdf2&lt;/code&gt; doesn't exist in stdlib. It's in &lt;code&gt;golang.org/x/crypto&lt;/code&gt;, which is external. I had to correct them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// This is what I use (stdlib)
func deriveKey(password string) []byte {
    hash := sha256.Sum256([]byte(password))
    return hash[:]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Felt weird correcting a senior engineer, but I was right. I triple-checked. &lt;code&gt;go doc crypto/pbkdf2&lt;/code&gt; returns nothing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The choice:&lt;/strong&gt; SHA256 is fast. PBKDF2 is slower (which is good for passwords—more resistant to brute force). But for a hackathon tool, the trade-off is acceptable. And it keeps zero dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; Don't assume. Don't trust memory. Actually verify what's in the standard library. And sometimes you do know things that others don't.&lt;/p&gt;
&lt;h4 id=&quot;bug-6-ghost-projects-in-kos-global-the-cleanup-problem-&quot;&gt;Bug #6: Ghost Projects In ~/.kos-global/ (The Cleanup Problem)&lt;/h4&gt;
&lt;p&gt;After months of testing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ls ~/.kos-global/
abc123-def456-...
import-test-1
import-test-2
my-awesome-project-v1
my-awesome-project-v2
test-uuid-alpha
test-uuid-beta
... fifteen more orphaned directories
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each test run created new UUIDs. Each UUID got added to &lt;code&gt;projects.json&lt;/code&gt;. But I never deleted old ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The root cause:&lt;/strong&gt; During testing, I created projects, then deleted them locally, but their entries in &lt;code&gt;projects.json&lt;/code&gt; remained. Then their directories stayed in the global store, orphaned and forgotten.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Had to manually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rm -rf ~/.kos-global/test-uuid-*
rm -rf ~/.kos-global/import-test-*
vim ~/.kos-global/projects.json  # Remove orphaned entries
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then rebuild the index.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; Testing creates state. State accumulates. You need cleanup procedures or eventual garbage collection. In production, KOS should have a &lt;code&gt;kos gc&lt;/code&gt; (garbage collect) command to clean up orphaned projects.&lt;/p&gt;
&lt;h3 id=&quot;the-real-lessons&quot;&gt;The Real Lessons&lt;/h3&gt;
&lt;h4 id=&quot;1-writer-layering-matters&quot;&gt;1. &lt;strong&gt;Writer Layering Matters&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Gzip writes to a tar writer. The tar writer writes to a file. You have to close them in order: tar → gzip → file. Not all at once with defer.&lt;/p&gt;
&lt;h4 id=&quot;2-centralize-reference-resolution&quot;&gt;2. &lt;strong&gt;Centralize Reference Resolution&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Don't lookup snapshots in ten places. Do it in one place (resolveRef). Now HEAD works everywhere. Tags work everywhere. No weird edge cases.&lt;/p&gt;
&lt;h4 id=&quot;3-skip-logic-can-break-assumptions&quot;&gt;3. &lt;strong&gt;Skip Logic Can Break Assumptions&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Your extract function skips &lt;code&gt;.kos/&lt;/code&gt; files? That's correct for normal use. But encrypted loads need them. Different paths for different use cases.&lt;/p&gt;
&lt;h4 id=&quot;4-syscalls-teach-you-real-things&quot;&gt;4. &lt;strong&gt;Syscalls Teach You Real Things&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Using &lt;code&gt;golang.org/x/term&lt;/code&gt; is easy. Using &lt;code&gt;syscall.Syscall6&lt;/code&gt; with &lt;code&gt;TCGETS&lt;/code&gt; is harder. But now I understand terminal echo at the OS level. That knowledge sticks.&lt;/p&gt;
&lt;h4 id=&quot;5-test-state-accumulates&quot;&gt;5. &lt;strong&gt;Test State Accumulates&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Build testing cleanup into your workflow. Delete projects between tests. Clean up &lt;code&gt;projects.json&lt;/code&gt;. Otherwise you end up with fifteen ghost projects.&lt;/p&gt;
&lt;h3 id=&quot;why-zero-dependencies-was-worth-it&quot;&gt;Why Zero Dependencies Was Worth It&lt;/h3&gt;
&lt;p&gt;I could have used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;github.com/urfave/cli&lt;/code&gt; for CLI parsing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;golang.org/x/term&lt;/code&gt; for password masking&lt;/li&gt;
&lt;li&gt;&lt;code&gt;github.com/fatih/color&lt;/code&gt; for colors&lt;/li&gt;
&lt;li&gt;&lt;code&gt;github.com/google/uuid&lt;/code&gt; for UUIDs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;github.com/sergi/go-diff&lt;/code&gt; for diffs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each would have saved time on that specific feature. But:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I wouldn't have understood how any of them work&lt;/li&gt;
&lt;li&gt;The binary would be much larger&lt;/li&gt;
&lt;li&gt;The attack surface (for a security tool!) would be huge&lt;/li&gt;
&lt;li&gt;I wouldn't have learned anything&lt;/li&gt;
&lt;li&gt;Deployment would be more complex&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, I built a 4.2 MB binary that runs anywhere. I understand every line. I can audit it myself. I can deploy it to air-gapped networks. I can ship it on a USB stick.&lt;/p&gt;
&lt;h3 id=&quot;how-to-try-it&quot;&gt;How to Try It&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Clone and build
git clone https://github.com/kosmoscpp/kos
cd kos
go build -o kos .

# Initialize a project
mkdir my-project
cd my-project
../kos init

# Save some work
echo &quot;Hello, KOS&quot; &amp;gt; readme.md
../kos save &quot;Initial commit&quot;

# See the timeline
../kos view

# Make a change
echo &quot;Hello, KOS with updates!&quot; &amp;gt; readme.md
../kos save &quot;Updated readme&quot;

# View timeline again
../kos view

# Check what changed
../kos diff &amp;lt;snapshot-id-1&amp;gt; &amp;lt;snapshot-id-2&amp;gt;

# Restore a version
../kos checkout &amp;lt;snapshot-id&amp;gt;
cat readme.md  # Back to original
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;Building KOS taught me that constraints aren't prison walls. They're focusing tools.&lt;/p&gt;
&lt;p&gt;When I couldn't reach for &lt;code&gt;go get&lt;/code&gt;, I had to understand my problems deeply. I had to learn about terminal I/O, writer layering, reference resolution, and encryption. I had to debug by understanding, not by reading someone else's code.&lt;/p&gt;
&lt;p&gt;The result isn't just a tool. It's a tool I completely understand. It's a tool that's portable and secure. It's a tool that can run where nothing else can run.&lt;/p&gt;
&lt;p&gt;That's worth the bugs. That's worth the deep dives. That's worth saying no to convenience.&lt;/p&gt;
&lt;p&gt;Try KOS. Use it. Break it (please report issues). Learn from it.&lt;/p&gt;
&lt;p&gt;And maybe, just maybe, build something with zero dependencies yourself. You'll be surprised what you learn when you can't &lt;code&gt;go get&lt;/code&gt; your way out.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/kosmoscpp/kos&quot;&gt;kosmoscpp/kos&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick Start:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/kosmoscpp/kos &amp;amp;&amp;amp; cd kos &amp;amp;&amp;amp; make install
kos init
kos save &quot;My first snapshot&quot;
kos view
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it. No setup. No config. No dependencies. Just version control, zero friction.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26489/building-kos-a-journey-through-zero-dependencies-and-the-bugs-that-taught-me-everything</guid>
<pubDate>Tue, 08 Sep 2026 12:13:13 +0000</pubDate>
</item>
<item>
<title>Why GitWand is Rust, not Electron: what native performance actually buys a Git client</title>
<link>https://coderlegion.com/25097/why-gitwand-is-rust-not-electron-what-native-performance-actually-buys-a-git-client</link>
<description>&lt;p&gt;It's been a while since the last post here (v3.5, July 10). Since then, three releases have shipped: v3.6.0 (a handful of workflow polish items), v3.6.6 (a pure dev-loop and CI performance pass, no product surface), and v3.7.0 (Commit Review, a local AI pass over your staged changes). A straight changelog recap would cover all three, but it would miss the thing that actually connects them.&lt;/p&gt;
&lt;p&gt;Every one of those releases is a direct consequence of a decision made on day one: build on Tauri 2 and Rust instead of Electron, and keep the actual conflict-resolution logic in a deliberately portable, zero-dependency TypeScript package instead of folding it into the Rust backend. That split sounds like an implementation detail. It isn't. It's the reason a 5-repo Launchpad refresh takes 30ms instead of most of a second, the reason a 30-second timeout bug fixed a few days ago (#166) had a one-line fix instead of a rewrite, and the reason the new Commit Review feature never has to touch a GitWand server. This post is about that decision, with real numbers pulled straight from the codebase instead of the usual &quot;native is faster, trust us.&quot;&lt;/p&gt;
&lt;h3 id=&quot;the-bet-and-who-else-made-it&quot;&gt;The bet, and who else made it&lt;/h3&gt;
&lt;p&gt;GitWand ships as roughly an 8 MB installed binary. GitKraken, built on Electron, ships in the ~150 MB class: a full bundled Chromium plus a full bundled Node.js runtime, running alongside each other, for an app that mostly renders text and spawns &lt;code&gt;git&lt;/code&gt;. GitHub Desktop is the same story. The gap shows up at startup (GitWand is sub-1 second) and it shows up on large repos, where an Electron app's single JS heap has to hold and garbage-collect everything the UI touches.&lt;/p&gt;
&lt;p&gt;To be fair to the field: GitWand isn't the only one making this bet anymore. GitButler is Tauri + Rust too. GitComet skips the webview entirely and goes pure Rust with GPUI. Strand is Tauri + React. Being native, in 2026, is closer to table stakes for a new Git client than a differentiator; RelaGit is the interesting counter-example, choosing Electron even with a SolidJS frontend for its fine-grained reactivity. The real question isn't &quot;who avoided Electron,&quot; it's what each of these tools actually does with the native layer once it has it. That's the part worth walking through.&lt;/p&gt;
&lt;h3 id=&quot;what-rust-buys-fearless-concurrency-with-a-number-attached&quot;&gt;What Rust buys: fearless concurrency, with a number attached&lt;/h3&gt;
&lt;p&gt;Rust's ownership model turns a whole class of concurrency bugs into compile errors instead of production incidents. That's a nice sentence to put in a pitch deck. Here's what it looks like in a file that ships in the app today.&lt;/p&gt;
&lt;p&gt;GitWand's multi-repo dashboard (Launchpad) needs the status of every repo in your workspace: branch, ahead/behind, modified-file count. The naive version spawns four &lt;code&gt;git&lt;/code&gt; subprocesses per repo, one after another. GitWand's Rust backend does two things instead: each repo's status comes from a single in-process &lt;code&gt;libgit2&lt;/code&gt; call rather than four subprocess spawns, and every repo in the workspace gets processed in parallel via &lt;code&gt;rayon&lt;/code&gt;'s &lt;code&gt;into_par_iter()&lt;/code&gt;, one core per repo instead of one core doing all of them in sequence. The comment sitting next to that code in the source states the measured result plainly: a 5-repo workspace listing went from about 750ms wall-clock to about 30ms in the typical case. That's not a marketing number, it's a comment next to a &lt;code&gt;#[tauri::command]&lt;/code&gt; that ships today.&lt;/p&gt;
&lt;p&gt;The same pattern shows up all over the backend, because once the tool exists, using it costs nothing extra: PR &lt;code&gt;+/-&lt;/code&gt; stat enrichment across GitHub, GitLab, Azure DevOps, and Bitbucket all fan out per-PR diff computation across cores instead of looping; submodule status checks across a repo's submodules run in parallel; per-branch top-author lookups for the branch picker do too. A few days ago, a regression brought back a 30-second &lt;code&gt;gh_list_prs&lt;/code&gt; timeout: the per-PR stats loop had gone back to running one &lt;code&gt;git diff --numstat&lt;/code&gt; at a time. The fix wasn't a rewrite or a new thread pool, it was swapping a &lt;code&gt;for&lt;/code&gt; loop for &lt;code&gt;.par_iter_mut()&lt;/code&gt;. None of these needed hand-rolled synchronization or a bespoke worker pool. The parallel version and the serial version are the same number of lines, and if two of those parallel closures ever raced on the same mutable data, that's a compile error, not a bug report from a user with a corrupted status badge.&lt;/p&gt;
&lt;h3 id=&quot;what-rust-doesn-t-need-to-buy-the-conflict-engine-stays-typescript-on-purpose&quot;&gt;What Rust doesn't need to buy: the conflict engine stays TypeScript, on purpose&lt;/h3&gt;
&lt;p&gt;Here's the part a &quot;Rust is fast&quot; pitch usually skips: the actual pattern-classification engine, the code that decides &lt;code&gt;same_change&lt;/code&gt; versus &lt;code&gt;one_side_change&lt;/code&gt; versus &lt;code&gt;complex&lt;/code&gt; and merges JSON, YAML, and lockfiles key by key, is not written in Rust. It lives in &lt;code&gt;@gitwand/core&lt;/code&gt;, and that package is deliberately zero-Node, browser-safe TypeScript.&lt;/p&gt;
&lt;p&gt;That's not a compromise, it's a second, equally deliberate architectural line. Classifying a few hundred lines of diff text against nine pattern rules finishes in single-digit milliseconds no matter what language it's written in; Rust's raw throughput wouldn't move that number in any way a user could feel. What Rust &lt;em&gt;would&lt;/em&gt; have cost is portability: because the engine has no Node dependency, the exact same code runs inside the desktop app, in the CLI (&lt;code&gt;npx @gitwand/cli resolve&lt;/code&gt;), inside the MCP server that AI agents call, and could run directly in a browser tomorrow with no changes. The rule GitWand actually follows isn't &quot;everything is Rust.&quot; It's narrower and, we think, more useful: Rust owns the boundary with the operating system (spawning &lt;code&gt;git&lt;/code&gt;, walking the filesystem, talking to forge APIs, the whole app shell), and the deterministic algorithmic core stays a small, dependency-light, portable package that isn't tied to any one runtime.&lt;/p&gt;
&lt;h3 id=&quot;the-unglamorous-release-v3-6-6&quot;&gt;The unglamorous release: v3.6.6&lt;/h3&gt;
&lt;p&gt;v3.6.6 shipped no new product surface at all, just a dev-loop and CI performance pass, and it's the release that best shows the bet compounding on itself. Because the entire backend is one Rust crate, the same profiling discipline used to make the app fast also applies to making the &lt;em&gt;build&lt;/em&gt; fast: a &lt;code&gt;[profile.dev]&lt;/code&gt; tuned for lighter debug info cut a post-edit &lt;code&gt;cargo build&lt;/code&gt; from about 21 seconds to about 6.5. On the CI side, a &lt;code&gt;desktop&lt;/code&gt; job that produced three fully-signed release bundles on every single push to &lt;code&gt;main&lt;/code&gt;, bundles nobody downloaded, was replaced with a fast &lt;code&gt;rust-check&lt;/code&gt; job (fmt, clippy, check, test) that finally runs the app's 217 Rust unit tests in CI for the first time, saving roughly 230 billed CI minutes per push. On the TypeScript side, Vitest's global &lt;code&gt;jsdom&lt;/code&gt; environment (needed by maybe a fifth of the test files, paid by all of them) was scoped down per-file, cutting environment setup time from about 96 seconds to about 17 across the suite.&lt;/p&gt;
&lt;p&gt;Worth mentioning: before touching any of it, every claim in the plan for this work got re-measured against the actual codebase, not assumed. Several turned out to be wrong. &quot;Any edit recompiles and relinks everything&quot; wasn't true, &lt;code&gt;cargo check&lt;/code&gt; after a real edit came back in under 2 seconds; the real cost was in the codegen-and-link step of a full &lt;code&gt;cargo build&lt;/code&gt;. &quot;The old &lt;code&gt;jsdom&lt;/code&gt; setup costs 100 to 300ms per file&quot; was an underestimate by roughly 10x once actually profiled. That's the same discipline the conflict engine's confidence scores are built on: don't ship a number you haven't checked.&lt;/p&gt;
&lt;h3 id=&quot;commit-review-and-why-local-first-is-the-real-differentiator&quot;&gt;Commit Review, and why local-first is the real differentiator&lt;/h3&gt;
&lt;p&gt;v3.7.0's headline feature, Commit Review, runs an AI pass over your staged changes right in the Changes panel before you commit, with inline findings and a &quot;Fix with agent&quot; action that hands them to a terminal AI session (Claude Code, opencode, or Codex) already running against your repo. What matters about it architecturally is what it &lt;em&gt;doesn't&lt;/em&gt; do: it never calls a GitWand-run server. The review runs against whichever AI CLI you already have installed, on your machine, over your own already-open agent session.&lt;/p&gt;
&lt;p&gt;That's the same &quot;zero mandatory cloud&quot; value the conflict engine already embodies, applied to review instead of merge. It's also the sharper contrast with where a chunk of the market is headed: tools positioning code-intelligence features as a metered cloud API (Greptile's own &quot;Genius API&quot; is priced at $0.45 per request) are betting that developers will pay per call for something GitWand ships as a local, free, opt-in feature built on tools you already run. Being local-first for conflict resolution and local-first for review isn't two separate decisions, it's the same one, applied twice.&lt;/p&gt;
&lt;h3 id=&quot;the-small-stuff-v3-6-0&quot;&gt;The small stuff: v3.6.0&lt;/h3&gt;
&lt;p&gt;Between the two, v3.6.0 shipped things that don't need an architecture section to justify: a one-click &quot;Update branch&quot; prompt when you check out something behind its upstream, and a non-blocking handoff when an interactive rebase hits a conflict (issue #128), so the conflict editor no longer traps you behind a modal. These are the kind of small interactions that only feel good because there's no Electron process and no garbage-collector pause standing between the click and the result. The architecture doesn't just enable the big numbers, it's also why the boring stuff feels instant.&lt;/p&gt;
&lt;h3 id=&quot;where-the-bet-goes-next&quot;&gt;Where the bet goes next&lt;/h3&gt;
&lt;p&gt;The roadmap's next stop, v3.9.0 (Live Repo), applies the same philosophy one layer up: a Rust &lt;code&gt;notify&lt;/code&gt; crate watcher replaces the current 2-second status poll with real filesystem events, more read paths move to in-process &lt;code&gt;libgit2&lt;/code&gt;, and separately, the portable TypeScript core's diff/parse hot path gets moved off the main thread into a Web Worker. Same rule, two languages: Rust handles OS-level events natively and cheaply, and the portable core gets pushed off the render thread the way any JavaScript does it. One architecture, one non-negotiable: the UI thread never waits on either of them.&lt;/p&gt;
&lt;h3 id=&quot;the-market-read&quot;&gt;The market read&lt;/h3&gt;
&lt;p&gt;The Git GUI space is splitting into two camps: Electron incumbents charging a subscription for cloud AI features (GitKraken at $8/mo, with its advanced and AI features gated behind an account), and a newer wave of native, mostly free, often open-source tools built on Rust or a comparable native stack (GitButler, GitComet, Strand, and GitWand, with RelaGit staying on Electron by choice). Being native is fast becoming the price of entry for that second group, not what sets any one of them apart. What actually differentiates GitWand inside it is what the native layer gets used for: a conflict-resolution engine that classifies instead of guesses, multi-repo workflows that get genuinely (and measurably) faster because of how the backend is built, and an AI layer that never assumes your code should leave your machine.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Download GitWand for macOS, Linux, or Windows from &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/devlint/GitWand/releases&quot;&gt;GitHub Releases&lt;/a&gt;, read the &lt;a rel=&quot;nofollow&quot; href=&quot;/changelog&quot;&gt;full changelog&lt;/a&gt;, or see how the numbers stack up against specific tools on the &lt;a rel=&quot;nofollow&quot; href=&quot;/compare/&quot;&gt;compare pages&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25097/why-gitwand-is-rust-not-electron-what-native-performance-actually-buys-a-git-client</guid>
<pubDate>Fri, 21 Aug 2026 15:52:00 +0000</pubDate>
</item>
<item>
<title>Git &amp; GitHub Basics Every Beginner Developer Should Know</title>
<link>https://coderlegion.com/22997/git-github-basics-every-beginner-developer-should-know</link>
<description>&lt;h2 id=&quot;introduction-1&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I avoided learning Git properly for way longer than I should have. I'd just zip my project folder and rename it &lt;strong&gt;project-final-v2-ACTUAL-FINAL&lt;/strong&gt; whenever I wanted a backup. It worked, technically, until it very much didn't.&lt;/p&gt;
&lt;p&gt;Git solves exactly this problem, and you genuinely only need about 10 commands to be productive with it. Here's everything you actually need, skipping the theory you won't use day-to-day.&lt;/p&gt;
&lt;h2 id=&quot;git-vs-github-what-s-the-difference-&quot;&gt;Git vs GitHub — What's the Difference?&lt;/h2&gt;
&lt;p&gt;Git is the version control tool that runs on your computer. It tracks changes to your files over time.&lt;br&gt;
GitHub is a website that hosts your Git projects online, so you can back them up, share them, and collaborate with others.&lt;/p&gt;
&lt;p&gt;You can use Git without GitHub. You can't really use GitHub without understanding Git.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-one-time-&quot;&gt;Setting Up (One-Time)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git config --global user.name &quot;Your Name&quot;
git config --global user.email &quot;*Emails are not allowed*&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-core-workflow&quot;&gt;The Core Workflow&lt;/h2&gt;
&lt;h3 id=&quot;1-initialize-a-repository&quot;&gt;1. Initialize a repository&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run this once, inside your project folder, to start tracking it with Git.&lt;/p&gt;
&lt;h3 id=&quot;2-check-what-s-changed&quot;&gt;2. Check what's changed&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Shows you which files are modified, new, or staged. You'll run this constantly — it's your &quot;what's going on right now&quot; command.&lt;/p&gt;
&lt;h3 id=&quot;3-stage-your-changes&quot;&gt;3. Stage your changes&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This stages all changed files, preparing them to be committed. Use git add filename.js to stage just one file.&lt;/p&gt;
&lt;h3 id=&quot;4-commit-your-changes&quot;&gt;4. Commit your changes&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git commit -m &quot;Add contact form component&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This saves a snapshot of your staged changes, with a message describing what you did. Write clear, specific messages — future you will thank present you.&lt;/p&gt;
&lt;h2 id=&quot;connecting-to-github&quot;&gt;Connecting to GitHub&lt;/h2&gt;
&lt;h3 id=&quot;5-create-a-repository-on-github&quot;&gt;5. Create a repository on GitHub&lt;/h3&gt;
&lt;p&gt;Go to github.com, click &quot;New Repository,&quot; give it a name. Don't initialize it with a README if you already have a local project — you'll connect them next.&lt;/p&gt;
&lt;h3 id=&quot;6-link-your-local-project-to-github&quot;&gt;6. Link your local project to GitHub&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git remote add origin https://github.com/yourusername/your-repo.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;7-push-your-code&quot;&gt;7. Push your code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;-u&lt;/strong&gt; flag remembers this connection, so future pushes just need &lt;strong&gt;git push&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;branches-working-without-breaking-things&quot;&gt;Branches — Working Without Breaking Things&lt;/h3&gt;
&lt;p&gt;A branch lets you work on a new feature without touching your main, working code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout -b new-feature
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creates and switches to a new branch called &lt;strong&gt;new-feature&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Switches back to your main branch.&lt;/p&gt;
&lt;p&gt;Once your feature works, merge it back:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout main
git merge new-feature
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; if you're experimenting with something risky, do it on a branch. If it breaks, your main branch stays untouched and safe.&lt;/p&gt;
&lt;h3 id=&quot;pulling-changes&quot;&gt;Pulling Changes&lt;/h3&gt;
&lt;p&gt;If you're working across multiple machines, or collaborating with others:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Downloads and merges the latest changes from GitHub into your local project.&lt;/p&gt;
&lt;h2 id=&quot;common-beginner-mistakes&quot;&gt;Common Beginner Mistakes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Committing giant, vague changes. &quot;fixed stuff&quot; tells you nothing six months later. Commit small, logical chunks with clear messages.&lt;/li&gt;
&lt;li&gt;Forgetting to pull before pushing when working with others — leads to avoidable merge conflicts.&lt;/li&gt;
&lt;li&gt;Committing sensitive files like &lt;strong&gt;.env&lt;/strong&gt; with API keys. Always set up a &lt;strong&gt;.gitignore&lt;/strong&gt; file before your first commit.&lt;/li&gt;
&lt;li&gt;Never using branches. Working directly on &lt;strong&gt;main&lt;/strong&gt; for every experiment is how small mistakes become big headaches.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;a-basic-gitignore-for-frontend-projects&quot;&gt;A Basic .gitignore for Frontend Projects&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;node_modules/
.env
dist/
.DS_Store
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save this as &lt;strong&gt;.gitignore&lt;/strong&gt; in your project root before your first commit — it stops these files/folders from ever being tracked.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;You don't need to memorize every Git command that exists&lt;strong&gt;. status, add, commit, push, pull,&lt;/strong&gt; and basic branching cover probably 90% of real, everyday use. Learn these solidly, and the rest you can look up as you go.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Muhammad Farhan&lt;/strong&gt; is a Frontend Developer specializing in React.js and Tailwind CSS, based in Dera Ismail Khan, Pakistan.&lt;br&gt;
&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;em&gt;muhammad-farhan-dev.github.io/muhammadfarhan.dev&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22997/git-github-basics-every-beginner-developer-should-know</guid>
<pubDate>Tue, 04 Aug 2026 05:00:00 +0000</pubDate>
</item>
<item>
<title>Git Worktrees Are Underrated</title>
<link>https://coderlegion.com/22952/git-worktrees-are-underrated</link>
<description>&lt;p&gt;If you've ever stashed half-finished work to review a PR, or lost a train of thought switching branches, you should know about git worktrees. They've been in git since 2015, but most developers I talk to have never heard of them.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-with-branches&quot;&gt;The problem with branches&lt;/h3&gt;
&lt;p&gt;The standard git workflow is linear. You're on a branch, you need to context-switch, so you stash your changes (or worse, make a throwaway commit), switch branches, do your thing, switch back, and pop the stash. If you're lucky, nothing conflicts.&lt;/p&gt;
&lt;p&gt;This gets worse when you're juggling multiple things at once. Reviewing a colleague's PR while your feature branch is mid-refactor. Running tests on one branch while coding on another. Hotfixing production while your main work sits frozen.&lt;/p&gt;
&lt;p&gt;A git repository has one working directory. One branch checked out at a time. Everything else has to wait.&lt;/p&gt;
&lt;h3 id=&quot;worktrees&quot;&gt;Worktrees&lt;/h3&gt;
&lt;p&gt;Git worktrees let you check out multiple branches simultaneously, each in its own directory. They share the same &lt;code&gt;.git&lt;/code&gt; history and objects, so you're not cloning the entire repo, but each one has its own working directory and index.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git worktree add ../feat-login feat/login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;feat/login&lt;/code&gt; is checked out in &lt;code&gt;../feat-login&lt;/code&gt;. You can open it in a separate editor window, run tests there, or just let it sit while you work on something else.&lt;/p&gt;
&lt;p&gt;When you're done:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git worktree remove ../feat-login
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;why-nobody-uses-them&quot;&gt;Why nobody uses them&lt;/h3&gt;
&lt;p&gt;If worktrees are so useful, why aren't they more popular? I think it comes down to two things.&lt;/p&gt;
&lt;p&gt;First, the directory problem. Every time you create a worktree, you have to pick a path. There's no convention for where they go. After a week you end up with directories scattered across your filesystem: &lt;code&gt;../feat-login&lt;/code&gt;, &lt;code&gt;~/tmp/hotfix-auth&lt;/code&gt;, &lt;code&gt;../../pr-review&lt;/code&gt;. Good luck remembering where anything is.&lt;/p&gt;
&lt;p&gt;Second, there are no ergonomics. You can't easily list your worktrees with status info, cd into one quickly, or run something like &lt;code&gt;npm install&lt;/code&gt; in the new directory after checkout. The raw git commands work, but they don't make worktrees feel like a real workflow.&lt;/p&gt;
&lt;p&gt;These aren't fundamental problems with worktrees. They're tooling gaps. That's why I built &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/morellodev/arbor&quot;&gt;arbor&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;a-better-workflow-with-arbor&quot;&gt;A better workflow with arbor&lt;/h3&gt;
&lt;p&gt;Arbor is a CLI that manages git worktrees. It keeps them organized in a central directory (&lt;code&gt;~/.arbor/worktrees/&lt;/code&gt;) and handles the stuff that plain git doesn't.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;# Clone a repo (sets up a bare repo + default branch worktree)
arbor clone user/my-app

# Create a worktree for a new feature
arbor add feat/login

# Shell integration auto-cds you into the worktree
# You're now in ~/.arbor/worktrees/my-app/feat-login

# Need to review a PR? Open another worktree
arbor add fix/auth-bug

# See all your worktrees with dirty/clean status
arbor ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every worktree for &lt;code&gt;my-app&lt;/code&gt; lives under &lt;code&gt;~/.arbor/worktrees/my-app/&lt;/code&gt;. Branch slashes become dashes in directory names, so the filesystem stays tidy. When you're done with a branch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;arbor rm -d feat/login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That removes the worktree and the local branch in one step.&lt;/p&gt;
&lt;h3 id=&quot;what-makes-it-stick&quot;&gt;What makes it stick&lt;/h3&gt;
&lt;p&gt;I've been using worktrees for a while, but I kept falling off because of the friction. A few things in arbor made the difference for me.&lt;/p&gt;
&lt;p&gt;Shell integration. &lt;code&gt;arbor init&lt;/code&gt; sets up a wrapper so that &lt;code&gt;arbor add&lt;/code&gt; and &lt;code&gt;arbor switch&lt;/code&gt; automatically cd you into the worktree directory. Without it you create the worktree and then have to &lt;code&gt;cd&lt;/code&gt; into it yourself every single time, which gets old fast.&lt;/p&gt;
&lt;p&gt;Post-create hooks. You can add an &lt;code&gt;.arbor.toml&lt;/code&gt; to your repo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[hooks]
post_create = &quot;npm install&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every new worktree gets its dependencies installed automatically. You can chain commands too:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[hooks]
post_create = [&quot;npm install&quot;, &quot;cp .env.example .env&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;arbor ls --all&lt;/code&gt; shows the state of every worktree across all your repos, which is nice for a quick scan before you call it a day.&lt;/p&gt;
&lt;h3 id=&quot;where-i-use-them-most&quot;&gt;Where I use them most&lt;/h3&gt;
&lt;p&gt;PR reviews. &lt;code&gt;arbor add&lt;/code&gt; the branch, review it in a separate editor window, remove it when done. My own work is untouched.&lt;/p&gt;
&lt;p&gt;Long test suites. Tests running on one branch, coding on another. No waiting around.&lt;/p&gt;
&lt;p&gt;Hotfixes. &lt;code&gt;arbor add hotfix/critical&lt;/code&gt;, fix it, push it, remove it. Back to what I was doing.&lt;/p&gt;
&lt;p&gt;Comparing behavior. Two worktrees open side by side, one on &lt;code&gt;main&lt;/code&gt; and one on my feature branch. Way easier than switching back and forth.&lt;/p&gt;
&lt;p&gt;Big migrations. Upgrading a framework to a new major version in a worktree while the rest of the team keeps shipping from &lt;code&gt;main&lt;/code&gt;, the way I kicked the tires on &lt;a rel=&quot;nofollow&quot; href=&quot;https://morello.dev/blog/solidjs-2-react-developers-first-look&quot;&gt;SolidJS 2.0&lt;/a&gt; and dug into &lt;a rel=&quot;nofollow&quot; href=&quot;https://morello.dev/blog/solidjs-2-async-data&quot;&gt;its async data model&lt;/a&gt;. No half-broken build blocking anyone.&lt;/p&gt;
&lt;h3 id=&quot;git-worktree-best-practices&quot;&gt;Git worktree best practices&lt;/h3&gt;
&lt;p&gt;A few habits keep worktrees from turning into the mess that makes people give up on them, whether you're on arbor or raw git.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep them in one predictable place.&lt;/strong&gt; Pick a home for your worktrees and stick to it: a &lt;code&gt;~/worktrees/&lt;/code&gt; directory, or let arbor manage &lt;code&gt;~/.arbor/worktrees/&lt;/code&gt; for you. Scattering them across &lt;code&gt;../&lt;/code&gt;, &lt;code&gt;~/tmp&lt;/code&gt;, and &lt;code&gt;../../&lt;/code&gt; is how you lose track of what lives where.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One branch per worktree.&lt;/strong&gt; Git won't let you check out the same branch in two worktrees without &lt;code&gt;--force&lt;/code&gt;, and that guardrail is worth respecting. Keep the mapping one branch to one directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create the branch and the worktree together.&lt;/strong&gt; &lt;code&gt;git worktree add -b feat/login ../feat-login&lt;/code&gt; makes the branch and checks it out in the new directory in a single command, instead of creating the branch first and adding it after.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remove worktrees with git, not &lt;code&gt;rm -rf&lt;/code&gt;.&lt;/strong&gt; Run &lt;code&gt;git worktree remove ../feat-login&lt;/code&gt; instead of deleting the directory by hand. A manual delete leaves stale metadata behind in &lt;code&gt;.git/worktrees&lt;/code&gt;, which you then have to clear out with &lt;code&gt;git worktree prune&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Run &lt;code&gt;git worktree list&lt;/code&gt; when you lose the thread.&lt;/strong&gt; It prints every worktree with its branch and commit, so you can see what's checked out where before you go hunting through directories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don't nest them.&lt;/strong&gt; Give each worktree its own top-level directory rather than tucking one inside another worktree or the main repo. Nesting confuses tooling, and worktrees on a repo with submodules are only partly supported anyway.&lt;/p&gt;
&lt;h3 id=&quot;try-it&quot;&gt;Try it&lt;/h3&gt;
&lt;p&gt;Arbor is open source, written in Rust. Install with Homebrew:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;brew install morellodev/tap/arbor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or grab a binary from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/morellodev/arbor/releases/latest&quot;&gt;releases page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;arbor init&lt;/code&gt; after installing to set up shell integration. Takes about 30 seconds.&lt;/p&gt;
&lt;p&gt;If you've never tried worktrees, give them a shot. And if you have but gave up because of the directory mess, this might be worth another look.&lt;/p&gt;
&lt;p&gt;The repo is at &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/morellodev/arbor&quot;&gt;github.com/morellodev/arbor&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22952/git-worktrees-are-underrated</guid>
<pubDate>Sat, 18 Jul 2026 22:31:32 +0000</pubDate>
</item>
<item>
<title>Git Branch Pinning and Auto-Archiving: What's New in Tower 13 for Windows</title>
<link>https://coderlegion.com/22761/git-branch-pinning-and-auto-archiving-whats-new-in-tower-13-for-windows</link>
<description>&lt;p&gt;Every long-running repo ends up with the same problem: a branch list full of stuff that's already merged or long abandoned, with the branches you actually need buried somewhere in the middle. &lt;/p&gt;
&lt;p&gt;Tower 13 for Windows adds two features built specifically to deal with that — Branch Pinning and Automatic Branch Management. Both have existed on Tower for Mac for a while (Pinning since Tower 8, Auto Branch Management since Tower 15) and are now available on Windows.&lt;/p&gt;
&lt;h3 id=&quot;branch-pinning&quot;&gt;Branch Pinning&lt;/h3&gt;
&lt;p&gt;Right-click a branch to pin it, and it moves into a dedicated &quot;Pinned&quot; section at the top of the sidebar. You can pin or unpin several branches in one go, pinned branches are automatically protected from auto-archiving, and pins are stored in your Git config so they persist reliably.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11151841218578869273&quot; alt=&quot;Branch Pinning&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;fully-merged-and-stale-badges&quot;&gt;Fully Merged and Stale badges&lt;/h3&gt;
&lt;p&gt;New badges flag branches as &lt;strong&gt;Fully Merged&lt;/strong&gt; (every commit integrated into the parent, and the tracked remote branch already deleted) or &lt;strong&gt;Stale&lt;/strong&gt; (no recent activity). Selecting a flagged branch brings up a hint view with a one-click delete option, so you can confirm cleanup without digging through menus.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7001012891353511530&quot; alt=&quot;Fully Merged and Stale badges&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;automatic-branch-management&quot;&gt;Automatic Branch Management&lt;/h3&gt;
&lt;p&gt;A new &quot;Archived Branches&quot; view holds branches you've set aside — added manually via drag-and-drop or the context menu, or moved there automatically based on a stale interval you configure in settings. Clicking the notification counter in the footer opens a dialog to review and approve archiving in bulk before anything happens. Archived branches can be unarchived at any time, and any branch can be flagged &quot;Skips Auto-Archiving&quot; if you want to protect it without formally pinning it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17161274194899496502&quot; alt=&quot;Automatic Branch Management&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;other-changes-in-this-release&quot;&gt;Other changes in this release&lt;/h3&gt;
&lt;p&gt;Untracked files now show their contents directly in the diff view, the Windows build has been updated to run on .NET 10, and there are various fixes across the diff view, submodule handling, and multi-branch/tag operations.&lt;/p&gt;
&lt;p&gt;Full details: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.git-tower.com/blog/tower-windows-13&quot;&gt;Tower 13 for Windows release notes&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22761/git-branch-pinning-and-auto-archiving-whats-new-in-tower-13-for-windows</guid>
<pubDate>Wed, 15 Jul 2026 17:21:47 +0000</pubDate>
</item>
<item>
<title>Git Will Humble You (And That's Okay) </title>
<link>https://coderlegion.com/22711/git-will-humble-you-and-thats-okay</link>
<description>&lt;p&gt;Hey fam &lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is my first ever dev article, and yes - I'm nervous.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So real talk — nobody warned me that learning to code would sometimes feel like arguing with a robot that's always right and never explains why you're wrong.&lt;/p&gt;
&lt;p&gt;That robot? Git.&lt;/p&gt;
&lt;p&gt;If you're a first-time developer, you've probably already had that moment. You type some command with full confidence, hit enter, and then... chaos. Your files are gone. Or duplicated. Or you're somehow on a branch that doesn't exist. Or the classic — you pushed to main when you absolutely should not have.&lt;/p&gt;
&lt;p&gt;We've all been there. This post is for you. &lt;/p&gt;
&lt;p&gt; What Even IS Git?&lt;/p&gt;
&lt;p&gt;Git is a version control system — basically a time machine for your code. It tracks every change you make, lets you collaborate with other developers, and (in theory) saves you from destroying your own project.&lt;/p&gt;
&lt;p&gt;The key word there is in theory.&lt;/p&gt;
&lt;p&gt; The Commands That Will Ruin Your Day (If You're Not Careful)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;git push --force&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Oh, this one. This is the &quot;delete everything and ask questions never&quot; command. If you force push to a shared branch without knowing what you're doing, your teammates will find you. Don't do it until you really understand what it does.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;git reset --hard&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hard reset sounds powerful because it is. It throws away all your uncommitted changes — no warning, no undo. Ran it once thinking it would fix my mess. It made a bigger mess. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;git merge vs git rebase&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These two will have you in a full philosophy debate at 2am. Both combine branches. Both will cause merge conflicts that look like ancient hieroglyphics. Learn the difference early — your future self will thank you.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Committing with no message (or a terrible one)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;git commit -m &quot;fix&quot; — said every developer who regretted it 3 weeks later. Be descriptive. Always.&lt;/p&gt;
&lt;p&gt; Why Mastering Git Early Actually Matters&lt;/p&gt;
&lt;p&gt;When you're just starting out, it's easy to think Git is just a way to upload your code to GitHub or Gitea and look productive. But here's the thing — Git is your safety net. The better you understand it:&lt;/p&gt;
&lt;p&gt;The less likely you are to lose work&lt;br&gt;
The easier it is to collaborate without chaos&lt;br&gt;
The more confident you look in team settings (yes, employers notice)&lt;br&gt;
The faster you can recover when things break — and they will break&lt;/p&gt;
&lt;p&gt;Git isn't just a tool. It's a habit. Build it early.&lt;/p&gt;
&lt;p&gt; Resources to Actually Get Good&lt;/p&gt;
&lt;p&gt;Here are some solid places to level up your Git, Go, Shell, and Rust game:&lt;/p&gt;
&lt;p&gt;Git:&lt;/p&gt;
&lt;p&gt;Pro Git Book — free, thorough, the bible&lt;br&gt;
Oh My Git! — learn Git through a game (yes, really)&lt;br&gt;
Learn Git Branching — visual and interactive&lt;/p&gt;
&lt;p&gt;Go (Golang):&lt;/p&gt;
&lt;p&gt;Go by Example — clean, no fluff&lt;/p&gt;
&lt;p&gt;Shell / Bash:&lt;/p&gt;
&lt;p&gt;The Linux Command Line by William Shotts — free online&lt;/p&gt;
&lt;p&gt;Rust:&lt;/p&gt;
&lt;p&gt;The Rust Book — the official one and it's actually great&lt;/p&gt;
&lt;p&gt; Now I Wanna Hear From YOU&lt;/p&gt;
&lt;p&gt;Drop it in the comments:&lt;/p&gt;
&lt;p&gt; What was your first Git disaster?&lt;br&gt;
 What's the command that made you question your life choices?&lt;br&gt;
 Are you learning Go, Shell, or Rust right now? How's it going?&lt;/p&gt;
&lt;p&gt;Let's laugh about it together — because if you're not breaking things, you're not learning.&lt;/p&gt;
&lt;p&gt;If this helped you even a little, hit that ❤️, drop a comment, and let's connect!&lt;/p&gt;
&lt;p&gt; Find me on LinkedIn → Mayan Okul — @mayan Okul — come say hi, seriously.&lt;/p&gt;
&lt;p&gt;Tags: #git #golang #shell #rust #Zone01 #codenewbie #devjourney #programming #firstpost&lt;/p&gt;
&lt;p&gt;&quot;Push code, not limits — unless it's a feature branch. Then push everything.&quot; &lt;br&gt;
— stay debugging, stay dangerous ⚡&lt;/p&gt;
&lt;p&gt;— Mayan, your fav/baddest developer__&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22711/git-will-humble-you-and-thats-okay</guid>
<pubDate>Wed, 15 Jul 2026 05:28:26 +0000</pubDate>
</item>
<item>
<title>The Hidden Engineering Behind Git Merge Conflicts</title>
<link>https://coderlegion.com/22483/the-hidden-engineering-behind-git-merge-conflicts</link>
<description>&lt;p&gt;If you've worked with Git for even a few weeks, you've probably seen this message:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;CONFLICT (content): Merge conflict
Automatic merge failed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your heart sinks.&lt;/p&gt;
&lt;p&gt;You stare at the screen and think:&lt;/p&gt;
&lt;p&gt;👉 &quot;What did I do wrong?&quot;&lt;/p&gt;
&lt;p&gt;The truth is...&lt;/p&gt;
&lt;p&gt;Most merge conflicts aren't caused by bad developers.&lt;/p&gt;
&lt;p&gt;They're simply a side effect of multiple people working on the same codebase.&lt;/p&gt;
&lt;p&gt;Let's see what's actually happening behind the scenes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-git-doesn-t-understand-your-code&quot;&gt;💡 Git Doesn't Understand Your Code&lt;/h3&gt;
&lt;p&gt;Here's something many developers don't realize.&lt;/p&gt;
&lt;p&gt;Git doesn't understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git simply sees...&lt;/p&gt;
&lt;p&gt;👉 Text files.&lt;/p&gt;
&lt;p&gt;It doesn't know what a function is or what a variable means.&lt;/p&gt;
&lt;p&gt;It only knows:&lt;/p&gt;
&lt;p&gt;&quot;This line changed.&quot;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-a-simple-example&quot;&gt;👨💻 A Simple Example&lt;/h3&gt;
&lt;p&gt;Imagine your &lt;code&gt;login.js&lt;/code&gt; file contains:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function login() {
   return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now two developers start working.&lt;/p&gt;
&lt;h4 id=&quot;developer-a&quot;&gt;Developer A&lt;/h4&gt;
&lt;p&gt;Changes it to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;return false;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Meanwhile...&lt;/p&gt;
&lt;h4 id=&quot;developer-b&quot;&gt;Developer B&lt;/h4&gt;
&lt;p&gt;Changes it to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;return validateUser();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both changes happen on different branches.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-now-git-gets-confused&quot;&gt;🤔 Now Git Gets Confused&lt;/h3&gt;
&lt;p&gt;When the branches are merged...&lt;/p&gt;
&lt;p&gt;Git asks itself:&lt;/p&gt;
&lt;p&gt;&quot;I have two different changes for the same line.&quot;&lt;/p&gt;
&lt;p&gt;Which one should I keep?&lt;/p&gt;
&lt;p&gt;Developer A's?&lt;/p&gt;
&lt;p&gt;Or Developer B's?&lt;/p&gt;
&lt;p&gt;Git has no idea.&lt;/p&gt;
&lt;p&gt;So instead of guessing...&lt;/p&gt;
&lt;p&gt;It stops.&lt;/p&gt;
&lt;p&gt;And asks you to decide.&lt;/p&gt;
&lt;p&gt;That's a merge conflict.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-why-git-doesn-t-auto-fix-everything&quot;&gt;🚀 Why Git Doesn't Auto-Fix Everything&lt;/h3&gt;
&lt;p&gt;People often ask:&lt;/p&gt;
&lt;p&gt;&quot;Why can't Git just combine both changes?&quot;&lt;/p&gt;
&lt;p&gt;Because sometimes there isn't a correct answer.&lt;/p&gt;
&lt;p&gt;Imagine:&lt;/p&gt;
&lt;p&gt;One developer deletes a function.&lt;/p&gt;
&lt;p&gt;Another developer adds new logic inside that same function.&lt;/p&gt;
&lt;p&gt;Should Git:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep the deletion?&lt;/li&gt;
&lt;li&gt;Keep the new logic?&lt;/li&gt;
&lt;li&gt;Combine both?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only the developer understands the intent.&lt;/p&gt;
&lt;p&gt;Git doesn't.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-those-weird-markers-mean&quot;&gt;⚙️ What Those Weird Markers Mean&lt;/h3&gt;
&lt;p&gt;When a conflict happens, you'll see something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Developer A's code
=======
Developer B's code
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; feature-branch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git is simply saying:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Here are both versions.&lt;br&gt;
You choose the correct one.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After editing the file and removing these markers, the conflict is resolved.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-why-merge-conflicts-increase-in-large-teams&quot;&gt;🤝 Why Merge Conflicts Increase in Large Teams&lt;/h3&gt;
&lt;p&gt;In small projects...&lt;/p&gt;
&lt;p&gt;Conflicts are rare.&lt;/p&gt;
&lt;p&gt;In large projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple developers&lt;/li&gt;
&lt;li&gt;Multiple features&lt;/li&gt;
&lt;li&gt;Long-lived branches&lt;/li&gt;
&lt;li&gt;Frequent releases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The chances of two people modifying the same file become much higher.&lt;/p&gt;
&lt;p&gt;That's why enterprise teams often merge changes frequently instead of waiting for weeks.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-good-teams-reduce-merge-conflicts&quot;&gt;💡 How Good Teams Reduce Merge Conflicts&lt;/h3&gt;
&lt;p&gt;Experienced teams don't eliminate conflicts completely.&lt;/p&gt;
&lt;p&gt;Instead, they reduce them by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating smaller pull requests&lt;/li&gt;
&lt;li&gt;Merging branches frequently&lt;/li&gt;
&lt;li&gt;Keeping branches short-lived&lt;/li&gt;
&lt;li&gt;Improving communication&lt;/li&gt;
&lt;li&gt;Breaking large features into smaller tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn't &quot;no conflicts.&quot;&lt;/p&gt;
&lt;p&gt;The goal is &quot;easy conflicts.&quot;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-bigger-lesson&quot;&gt;🎯 The Bigger Lesson&lt;/h3&gt;
&lt;p&gt;A merge conflict isn't Git being broken.&lt;/p&gt;
&lt;p&gt;It's Git protecting your work.&lt;/p&gt;
&lt;p&gt;Instead of making the wrong decision...&lt;/p&gt;
&lt;p&gt;Git lets the developer make the final call.&lt;/p&gt;
&lt;p&gt;That's actually a safety feature.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-final-thought&quot;&gt;🚀 Final Thought&lt;/h3&gt;
&lt;p&gt;The next time Git says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Automatic merge failed.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Don't panic.&lt;/p&gt;
&lt;p&gt;It's not telling you that your project is broken.&lt;/p&gt;
&lt;p&gt;It's simply saying:&lt;/p&gt;
&lt;p&gt;👉 &lt;strong&gt;&quot;Two smart developers made different changes. I need your help deciding which one is correct.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And that's the hidden engineering behind one of the most common messages every developer eventually learns to respect.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;💬 &lt;strong&gt;What's the longest merge conflict you've ever had to resolve?&lt;/strong&gt; 😄&lt;/p&gt;
&lt;h2 id=&quot;git-github-softwareengineering-developers-programming-versioncontrol-coding-webdevelopment-developerlife&quot;&gt;Git #GitHub #SoftwareEngineering #Developers #Programming #VersionControl #Coding #WebDevelopment #DeveloperLife&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22483/the-hidden-engineering-behind-git-merge-conflicts</guid>
<pubDate>Mon, 13 Jul 2026 12:12:16 +0000</pubDate>
</item>
<item>
<title>Continuous Delivery Office Hours Ep.3: Branching strategies</title>
<link>https://coderlegion.com/22461/continuous-delivery-office-hours-ep-3-branching-strategies</link>
<description>&lt;p&gt;Your branching strategy can support Continuous Delivery, or make it an impossible goal. You should assess the impact of how you branch on your ability to deliver software at all times, and you'll find some branching techniques that work, while others that make software delivery more like walking in the dark through a field of rakes.&lt;/p&gt;
&lt;p&gt;Continuous Integration is the practice of integrating code changes frequently, typically multiple times a day. This means you should check your code multiple times a day and keep your main branch deployable. Continuous Integration is a prerequisite for Continuous Delivery.&lt;/p&gt;
&lt;p&gt;The name &quot;Continuous Integration&quot; provides solid hints about the crucial parts of the practice. Everyone should &lt;em&gt;integrate&lt;/em&gt; their code into a shared branch (feature branches don't count) and this should be done &lt;em&gt;continuously&lt;/em&gt;, which means you're doing it all the time; at least once a day, but ideally more often.&lt;/p&gt;
&lt;p&gt;You'll often speak to developers who want to stretch the definition of Continuous Integration, but you can't escape those foundations. Merging the main branch into a long-lived branch feels like Continuous Integration, but you'll notice no changes come back for ages until someone finally merges to main. Then you have to perform a large, complex merge, which you should avoid.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://dora.dev/capabilities/continuous-delivery/&quot;&gt;DORA research on Continuous Delivery&lt;/a&gt; includes the capabilities of Continuous Integration and trunk-based development. The statistics suggest you can get similar benefits as long as you limit yourself to 3 (or fewer) short-lived branches (less than a day old).&lt;/p&gt;
&lt;h3 id=&quot;watch-the-episode&quot;&gt;Watch the episode&lt;/h3&gt;
&lt;p&gt;You can watch the episode below, or read on to find some of the key discussion points.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=WDHZAHGxpR8&quot;&gt;Watch Continuous Delivery Office Hours Ep.3&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-worst-branching-strategy&quot;&gt;The worst branching strategy&lt;/h3&gt;
&lt;p&gt;Since the ability to branch code was invented, developers have applied a great deal of creativity to how to use branches. The most common approach is feature branching, where each feature gets a branch of main that evolves separately until the feature is complete, and it's merged back into main. Release branching allows development to continue on main by taking a cut of the main branch that will be released. This allows hotfixes to be applied to the release branch without further destabilizing it.&lt;/p&gt;
&lt;p&gt;The utility of branching strategies is often eroded by the coordination overhead of maintaining the separate branches and merging different changes back together. The more complex the branching strategy, the more likely it is that you'll have merge conflicts and lost bug fixes. For example, you might fix a release branch and forget to merge the fix back to main, so the next release reintroduces the bug.&lt;/p&gt;
&lt;p&gt;This is why the worst branching strategy is Gitflow. This is a complicated branching strategy that creates dedicated branches for features, releases, and hotfixes alongside permanent main and develop branches. The overhead of Gitflow vastly outweighs its benefits.&lt;/p&gt;
&lt;p&gt;This is where trunk-based development shines, as it removes unnecessary complexity.&lt;/p&gt;
&lt;h3 id=&quot;the-best-strategy-is-trunk-based-development&quot;&gt;The best strategy is trunk-based development&lt;/h3&gt;
&lt;p&gt;Trunk-based development is the process of making all commits directly to the main branch. This is complemented by out-of-band reviews that don't block merging and by feature toggles that decouple deployments from releases. It should be possible to deploy your software from the main branch at all times, even if features aren't complete.&lt;/p&gt;
&lt;p&gt;The DORA research allows up to 3 short-lived branches, which can be useful for teams working remotely (like open-source project teams), who can use branches and pull requests to coordinate their work. Even so, the goal is to keep branches short-lived and to merge frequently.&lt;/p&gt;
&lt;p&gt;Trunk-based development is complemented by automated builds and checks that run when code is committed to the main branch. If a problem is found during these checks, the team should prioritize the fix over other development work.&lt;/p&gt;
&lt;h3 id=&quot;elite-performance-comes-from-small-batches&quot;&gt;Elite performance comes from small batches&lt;/h3&gt;
&lt;p&gt;Teams with the best software delivery performance work in small steps. Trunk-based development and Continuous Integration are crucial practices for controlling batch size. Problems are discovered sooner and are easier to fix when you only have a small amount of change to reason about.&lt;/p&gt;
&lt;p&gt;Making frequent commits makes it easy to back out a bad change. If a test fails, you can discard changes since the last commit and try again, instead of trying to debug the problem. This is especially true when using AI coding assistants or other code generation techniques.&lt;/p&gt;
&lt;p&gt;Ultimately, for trunk-based development to succeed without friction, the entire team must be aligned on the process. It doesn't work if only some of the team are on board.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Continuous Delivery Office Hours is a series of conversations about software delivery, with Tony Kelly, Bob Walker, and Steve Fenton.&lt;/p&gt;
&lt;p&gt;You can find more episodes on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/playlist?list=PLAGskdGvlaw3CrxkUOAMmiy928lr5D4oh&quot;&gt;YouTube&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://podcasts.apple.com/us/podcast/continuous-delivery-office-hours/id1872101651&quot;&gt;Apple Podcasts&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://pca.st/hwjaox59&quot;&gt;Pocket Casts&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22461/continuous-delivery-office-hours-ep-3-branching-strategies</guid>
<pubDate>Mon, 13 Jul 2026 11:13:02 +0000</pubDate>
</item>
<item>
<title>Codicology for Compiled Code: Triangulating Authorship When Git Blame Lies</title>
<link>https://coderlegion.com/21921/codicology-for-compiled-code-triangulating-authorship-when-git-blame-lies</link>
<description>&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/codicology-for-compiled-code/&quot;&gt;Vibe Agent Making&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Triangulating authorship when git blame lies — what medieval manuscript authentication teaches software forensics about the evidence nobody designed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Open a thirteenth-century parchment codex to any two-page spread and look at the surface, not the text. On the left page, tiny follicle marks where hair once grew — a faint roughness, a slightly yellowed cast. On the right page, the same. Flip forward: smooth, cream-colored flesh side faces flesh side. This alternation — hair to hair, flesh to flesh — runs through the entire book with the regularity of a heartbeat.&lt;/p&gt;
&lt;p&gt;No medieval scribe thought of this as a security feature. They were making a beautiful book. Hair side is rougher and darker than flesh side; alternating them within a spread would look jarring, so bookmakers arranged their parchment sheets to keep each opening visually consistent. Caspar René Gregory, an American textual scholar working in nineteenth-century Germany on New Testament manuscripts, was the first to formalize what this incidental regularity meant for authentication. When a leaf has been inserted into a quire — a forgery, a later interpolation, a replacement — the hair-flesh alternation breaks. A checksum failure, centuries before checksums.&lt;/p&gt;
&lt;p&gt;Gregory’s Rule, as codicologists now call it, illustrates a principle that software forensics has been slow to learn: the most diagnostic evidence of tampering is often the evidence nobody designed.&lt;/p&gt;
&lt;h3 id=&quot;the-paleographic-playbook&quot;&gt;The paleographic playbook&lt;/h3&gt;
&lt;p&gt;Manuscript scholars never trust a single dating indicator. The standard method — described in paleography handbooks from Bernhard Bischoff’s &lt;em&gt;Latin Palaeography&lt;/em&gt; (Cambridge UP, 1990) onward — triangulates across multiple independent evidence types.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Script.&lt;/strong&gt; A trained paleographer dates Latin manuscripts to within a quarter-century by letterform alone, for most of the period after 800 CE. The angle of the pen, the proportions of ascenders and descenders, the treatment of ligatures — these are as individual as handwriting, because they &lt;em&gt;are&lt;/em&gt; handwriting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Watermark.&lt;/strong&gt; For paper codices, Charles-Moïse Briquet’s &lt;em&gt;Les Filigranes&lt;/em&gt; (1907) catalogued European paper-mill watermarks so thoroughly that matching one narrows the date to a five-to-twenty-year window and a particular mill’s region.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ruling pattern.&lt;/strong&gt; Before writing, scribes ruled lines into the parchment with a stylus, lead, or ink. The spacing, the number of columns, the margin conventions — these are datable independent of the script and by different specialists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quire construction.&lt;/strong&gt; A quire is a gathering of folded parchment sheets sewn together. Quire boundaries often show changes of ink, hand, or layout. An inserted leaf whose ruling doesn’t match its quire is diagnostic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parchment preparation.&lt;/strong&gt; Regional and period-specific. Coarse versus fine, thick versus thin — the substrate itself carries temporal and geographic signal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ownership marks.&lt;/strong&gt; The first recorded owner gives a &lt;em&gt;terminus ante quem&lt;/em&gt;: the book must have existed before that person could sign it.&lt;/p&gt;
&lt;p&gt;No one of these is conclusive. A responsible manuscript catalog entry hedges deliberately: “s. xii², perhaps third quarter, northern France (Champagne?), probably Cistercian provenance.” That phrasing reflects the underlying evidence, not the cataloguer’s timidity. It is the most honest thing anyone has said about dating in any discipline.&lt;/p&gt;
&lt;h3 id=&quot;when-git-blame-lies&quot;&gt;When git blame lies&lt;/h3&gt;
&lt;p&gt;Now consider how software development handles the equivalent question — &lt;em&gt;who wrote this code, and when?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Almost exclusively, the answer is git blame. One command, one indicator: the commit that last modified each line. And for most purposes, it works. But a paleographer looking at this arrangement would wince. git blame is ownership marks with no supporting evidence — the medieval equivalent of trusting a colophon and nothing else.&lt;/p&gt;
&lt;p&gt;The fragility is well-documented. git rebase and git filter-branch can rewrite entire commit histories — the software equivalent of a scribe erasing and rewriting a colophon. A single prettier or black run reassigns every line to the formatter’s commit, erasing the attribution trail as thoroughly as a later scribe rebinding and reruling a codex. Squash merges collapse multiple authors’ work into a single commit — a patron recorded as “author” of a compilation manuscript. And now, AI assistance: code generated by Copilot or Claude Code is committed under the human’s name, the human’s hand pushing someone else’s text.&lt;/p&gt;
&lt;p&gt;The stylometric research bears this out. On single-author, whole-file attribution, code authorship methods achieve accuracy above 90% (Caliskan-Islam et al.; Kalgutkar et al., ACM Computing Surveys 52(1)). But on collaborative code segments — the kind git blame actually deals with — Dauber and Caliskan found accuracy drops to 50–60% (“Git Blame Who?,” arXiv:1701.05681, 2017). And adversarial edits defeat current attribution methods with unsettling regularity. As a 2024 survey in MDPI &lt;em&gt;Information&lt;/em&gt; put it: “As of now, there is no code authorship attribution method capable of effectively handling such attacks.”&lt;/p&gt;
&lt;p&gt;One indicator. Easily faked. Easily broken by routine operations. This is the problem.&lt;/p&gt;
&lt;h3 id=&quot;the-fingerprints-you-didn-t-know-you-were-leaving&quot;&gt;The fingerprints you didn’t know you were leaving&lt;/h3&gt;
&lt;p&gt;Here’s where it gets interesting. Two studies from 2026 have mapped the fingerprinting landscape for AI-generated code, and the findings would delight a paleographer.&lt;/p&gt;
&lt;p&gt;The first — “Fingerprinting AI Coding Agents on GitHub” (arXiv:2601.17406) — analyzed 33,580 pull requests across five AI coding agents: OpenAI Codex, GitHub Copilot, Devin, Cursor, and Claude Code. An XGBoost classifier achieved 97.2% F1-score in identifying which agent produced the code. But the counterintuitive finding was &lt;em&gt;where&lt;/em&gt; the fingerprints live: commit message conventions were more discriminative than the code itself. The multiline commit ratio alone carried 44.7% of the discriminative power. Each agent had its own metadata signature — OpenAI Codex wrote verbose multiline commits, Cursor peppered PR bodies with bullet points and hyperlinks, Claude Code showed high conditional density and elevated comment rates.&lt;/p&gt;
&lt;p&gt;A paleographer would nod. Colophons — scribal subscriptions at the end of manuscripts — often reveal more about authorship than the text itself. The scribe’s personal formula, their dating convention, their invocation: these are the metadata of the medieval book, and they fingerprint the maker more reliably than the content does.&lt;/p&gt;
&lt;p&gt;The second study — “Code Fingerprints: Disentangled Attribution” (DCAN, arXiv:2603.04212, March 2026) — found something equally striking: attribution accuracy &lt;em&gt;improves with task complexity&lt;/em&gt;. Easy tasks produce canonical solutions that look the same regardless of who or what wrote them. Hard tasks amplify stylistic variation. The paleographic parallel is exact: standard Caroline minuscule — the workhorse book script of Carolingian Europe — is notoriously difficult to attribute to a specific scriptorium, precisely because it was designed for uniformity. But an idiosyncratic display script, a scribe’s vernacular hand, an experimental ligature — those localize immediately. The mundane is the hardest to fingerprint, in any medium.&lt;/p&gt;
&lt;p&gt;And perhaps most remarkably, DCAN found that LLM fingerprints persist across programming languages — 93.48% cross-language attribution accuracy with comments included. Claude writes identifiably like Claude in Python, Java, Go, and C, just as a medieval scribe’s hand is recognizable whether they’re copying Latin or vernacular French. The authorial identity runs deeper than the surface language.&lt;/p&gt;
&lt;h3 id=&quot;the-translation&quot;&gt;The translation&lt;/h3&gt;
&lt;p&gt;What manuscript studies offer software forensics is not a metaphor. It’s a methodology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Script analysis → Code stylometry.&lt;/strong&gt; Token frequencies, abstract syntax tree shapes, naming conventions, indentation patterns — these are the ductus of code, the pen-angle and stroke-order that identify the hand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Watermark dating → Build-tool-chain fingerprints.&lt;/strong&gt; Compiler flags, bundler versions, lockfile formats — the toolchain leaves marks as specific as a paper mill’s wire pattern.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ruling pattern → Formatting configuration.&lt;/strong&gt; .editorconfig, .prettierrc, linter rules — the invisible grid that structures the page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gregory’s Rule → Substrate invariants.&lt;/strong&gt; Line-ending consistency (CRLF versus LF), encoding (UTF-8 BOM presence), whitespace regularity, entropy patterns. Nobody designs these as authentication mechanisms. They function as checksums anyway. AI-generated code insertions break them because the generating model doesn’t preserve the repository’s incidental regularities — just as a forger focused on imitating a script might not think to match the parchment orientation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parchment preparation → Dependency fingerprints.&lt;/strong&gt; Package ecosystem, version pinning patterns, registry choices. The supply-chain provenance of a codebase’s dependencies is as regional and temporal as the preparation of a calfskin leaf.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ownership marks → Git metadata.&lt;/strong&gt; Author email, timezone, GPG signatures. And as easily forged as an ex-libris plate glued into a stolen book.&lt;/p&gt;
&lt;p&gt;The through-line is the paleographic maxim: &lt;em&gt;every single indicator can be faked in isolation, but faking all of them simultaneously is exponentially harder.&lt;/em&gt; A manuscript forger can imitate a twelfth-century script, but matching the script plus the correct watermark for the region plus period-appropriate ruling plus proper quire construction plus consistent ink chemistry requires expertise across five distinct subspecialties. A git history rewriter can fake commits, but simultaneously preserving consistent stylometric patterns, matching build-tool artifacts, maintaining timezone coherence, and keeping dependency version patterns plausible requires awareness of forensic techniques that most rewriters don’t anticipate.&lt;/p&gt;
&lt;p&gt;And here’s the argument’s sharpest edge: digital substrates need &lt;em&gt;more&lt;/em&gt; triangulation than physical ones, not less. Parchment has an inherent audit trail — you can’t un-scrape a surface, un-bond iron-gall ink from the fibers, un-disturb the collagen structure. Code has no such substrate memory. Every bit is perfectly copyable, every history perfectly rewritable. The more amnesiac the substrate, the more independent indicators you need.&lt;/p&gt;
&lt;h3 id=&quot;where-the-analogy-breaks&quot;&gt;Where the analogy breaks&lt;/h3&gt;
&lt;p&gt;Three ways, ordered by severity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First: physical irreversibility has no digital equivalent.&lt;/strong&gt; A paleographer examining a palimpsest — a scraped-and-rewritten parchment — can recover the original text through multispectral imaging because the ink chemically bonded with the collagen. A rewritten git history leaves no such trace. The reflog helps for a while, but it’s local, temporary, and trivially deletable. The substrate’s amnesia is fundamental, not incidental.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second: the adversarial gap.&lt;/strong&gt; Current code attribution methods fold under targeted adversarial attacks at rates that have no paleographic equivalent. You cannot automate the production of convincing medieval parchment, period-appropriate iron-gall ink, and a twelfth-century hand. Adversarial stylometric evasion, on the other hand, is a script away. The attacker’s cost is categorically different in the two domains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third: scale and velocity.&lt;/strong&gt; The largest medieval library held thousands of codices. A modern enterprise codebase has millions of files, thousands of contributors, and continuous automated modifications. Any triangulation framework for code must operate at a scale that would make autoptic examination — Gregory’s careful, physical handling of each codex — impossible. The method must be automated, or it will remain an academic exercise.&lt;/p&gt;
&lt;p&gt;These are real limits. They mean the translation cannot be literal. But they don’t invalidate the methodology — they intensify the need for it. The paleographic playbook was developed precisely because no single indicator was trustworthy. Software forensics faces the same problem with weaker substrate guarantees, and yet it mostly still relies on the equivalent of reading the colophon and calling it a day.&lt;/p&gt;
&lt;h3 id=&quot;the-incidental-invariant&quot;&gt;The incidental invariant&lt;/h3&gt;
&lt;p&gt;Gregory wasn’t looking for a security mechanism. He was a textual scholar trying to understand how New Testament manuscripts were physically constructed. The hair-flesh alternation pattern he documented was a byproduct of bookmaking aesthetics, not a designed authentication feature. Its forensic power was discovered, not invented.&lt;/p&gt;
&lt;p&gt;Every codebase is full of the same kind of unconscious regularity. The way one team consistently names error variables. The timezone pattern in commit metadata. The specific version of a lockfile format that pins the code to an era of toolchain development. The entropy signature that distinguishes handwritten logic from generated boilerplate.&lt;/p&gt;
&lt;p&gt;None of these were designed as forensic indicators. None of them need to be. The question is whether, eight hundred years into the tradition of making sense of other people’s texts, we’ll have learned to read what was there all along — or whether we’ll still be trusting the colophon.&lt;/p&gt;
&lt;h3 id=&quot;sources-2&quot;&gt;Sources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dauber &amp;amp; Caliskan, “Git Blame Who?” (arXiv:1701.05681, 2017)&lt;/li&gt;
&lt;li&gt;“Fingerprinting AI Coding Agents on GitHub” (arXiv:2601.17406, 2026)&lt;/li&gt;
&lt;li&gt;“Code Fingerprints: DCAN” (arXiv:2603.04212, March 2026)&lt;/li&gt;
&lt;li&gt;“Authorship Attribution Methods, Challenges, and Future Research Directions” (MDPI Information 15(3), 2024)&lt;/li&gt;
&lt;li&gt;Kennedys Law, “86% fake — 100% admissible?” (2026)&lt;/li&gt;
&lt;li&gt;Gregory’s Rule — Goucher College faculty resource (faculty.goucher.edu)&lt;/li&gt;
&lt;li&gt;Bischoff, B., &lt;em&gt;Latin Palaeography: Antiquity and the Middle Ages&lt;/em&gt; (Cambridge UP, 1990)&lt;/li&gt;
&lt;li&gt;Briquet, C. M., &lt;em&gt;Les Filigranes&lt;/em&gt; (1907)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The evidence nobody designed is the evidence that matters. No single indicator — git blame, a commit message, an author field — survives scrutiny alone. You need triangulation across independent evidence types, where faking all of them simultaneously is exponentially harder than faking any one. That is the codicologist’s lesson for software forensics.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21921/codicology-for-compiled-code-triangulating-authorship-when-git-blame-lies</guid>
<pubDate>Sun, 05 Jul 2026 01:30:37 +0000</pubDate>
</item>
<item>
<title>The real risk was not the code I could see. It was the code Git could run for me.</title>
<link>https://coderlegion.com/21711/the-real-risk-was-not-the-code-i-could-see-it-was-the-code-git-could-run-for-me</link>
<description>&lt;h2 id=&quot;the-malware-was-not-in-the-app-it-was-in-git-hooks-&quot;&gt;The malware was not in the app. It was in .git/hooks.&lt;/h2&gt;
&lt;p&gt;My first job hunt taught me to inspect the repo before trusting the task&lt;/p&gt;
&lt;p&gt;I am currently looking for my first job So when a recruiter messaged me on LinkedIn and sent over a take-home repository, I treated it seriously. This was one of the first real task-based opportunities I had received, so I wanted to do everything properly.&lt;/p&gt;
&lt;p&gt;I reviewed a take-home repository that came through recruiter outreach.&lt;/p&gt;
&lt;p&gt;It was a web3 front end with WalletConnect, Transak, Uniswap widgets, Infura, Etherscan, and configuration driven REACT_APP_* env vars. The kind of stack you would expect from a real production app. That was exactly why I almost missed the part that mattered. Before running anything, I started checking the repo structure, package scripts, and Git metadata. I did not run install commands. I did not start the app. I did not assume the repository was safe just because it came through a recruiter.&lt;/p&gt;
&lt;p&gt;At first glance, it looked normal enough for a production web3 front end. WalletConnect. Transak. Uniswap widgets. Infura. Etherscan. Vercel Analytics. Env-driven config for chain IDs, contract addresses, fees, and backend URLs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That part did not bother me.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The red flag was lower level.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Inside the repo-local .git/hooks directory, I found executable pre-commit, pre-push, and post-checkout scripts. They did not validate code. They did not format files. They did not run tests.&lt;/p&gt;
&lt;p&gt;They fetched remote content from a hardcoded IP address and piped it into a shell or command interpreter.&lt;/p&gt;
&lt;p&gt;That is not a build step.&lt;/p&gt;
&lt;p&gt;Actual Screenshot of post-checkout, pre-commit, pre-push file&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/6a0105e2e3eebc2e20d5da8c/f8f12b59-4377-4dde-aad4-c5a565003273.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That is a downloader.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Git hooks matter because Git runs them automatically on specific events. pre-commit runs before commits. pre-push runs before pushes. post-checkout runs after a checkout. Git also allows&lt;/p&gt;
&lt;p&gt;hooks to live in &lt;strong&gt;.git/hooks or in a configured core.hooksPath&lt;/strong&gt;, and git init can copy hook templates into a repo.&lt;/p&gt;
&lt;p&gt;So if a malicious hook sits in the repo, the attack surface is not just the app. It is the developer workflow.&lt;/p&gt;
&lt;p&gt;That is the part people miss.&lt;/p&gt;
&lt;p&gt;The workflow pattern Here is the basic flow I observed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The candidate is approached like a normal hire The conversation starts like a standard recruiter or hiring process. The task is framed as a practical engineering evaluation, not a public challenge.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/6a0105e2e3eebc2e20d5da8c/99cc2048-674e-450b-8f93-08f68b968bea.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.google.com/document/d/1UTEmB8WRIEP1XQb9R2kVhCNLPkqxBo37WhS2unfhYJQ/edit?usp=sharing&quot;&gt;https://docs.google.com/document/d/1UTEmB8WRIEP1XQb9R2kVhCNLPkqxBo37WhS2unfhYJQ/edit?usp=sharing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This lowers suspicion because it feels like a legitimate private assessment.&lt;/p&gt;
&lt;p&gt;The Person or Profile that blocks me after I confirmed it;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/paula-andrea-d%C3%ADaz-loaiza-481ba9232/&quot;&gt;https://www.linkedin.com/in/paula-andrea-díaz-loaiza-481ba9232/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;align=&amp;quot;center&amp;quot;&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/6a0105e2e3eebc2e20d5da8c/24177cb1-4e48-4894-9118-5d2d4594fdc4.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/6a0105e2e3eebc2e20d5da8c/825b1615-539e-4281-9bbe-0df7eed0343c.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The time I get blocked or they delete?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;2. The project is shared as a zip file Instead of a clean private GitHub repository, the project is delivered as a Dropbox package.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/6a0105e2e3eebc2e20d5da8c/78916c10-9e40-4859-914f-e8b8ad8eacbb.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.dropbox.com/scl/fi/3bb255hoju2sxh9k7ml2h/datalake-token-app.zip?dl=0&amp;amp;e=2&amp;amp;file_subpath=%2Fdatalake-token-app&amp;amp;rlkey=jpk7vaw2n8kqrbzk8u3e8otcj&amp;amp;st=cmh3zbzg&quot;&gt;https://www.dropbox.com/scl/fi/3bb255hoju2sxh9k7ml2h/datalake-token-app.zip?dl=0&amp;amp;e=2&amp;amp;file_subpath=%2Fdatalake-token-app&amp;amp;rlkey=jpk7vaw2n8kqrbzk8u3e8otcj&amp;amp;st=cmh3zbzg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That matters because:&lt;/p&gt;
&lt;p&gt;it removes the normal protections and visibility of a repo,&lt;/p&gt;
&lt;p&gt;it makes the workflow feel “local and manual,”&lt;/p&gt;
&lt;p&gt;and it encourages the candidate to unpack and inspect the code on their own machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The codebase looks real The project itself is not empty or fake-looking. It contains a real frontend app, real Web3 integrations, real environment-driven configuration, and even sensitive-looking .env material.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is important because a believable app builds trust.&lt;/p&gt;
&lt;p&gt;1. The assignment asks for Git-based work The candidate is told to:&lt;/p&gt;
&lt;p&gt;create a new branch,add a review file,push the project, and return the GitHub URL.&lt;/p&gt;
&lt;p&gt;This is where the workflow becomes interesting.&lt;/p&gt;
&lt;p&gt;It makes the candidate behave like a normal developer:&lt;/p&gt;
&lt;p&gt;clone or unpack,inspect,branch,commit,push.&lt;/p&gt;
&lt;p&gt;That is exactly the behavior malicious hooks are waiting for.&lt;/p&gt;
&lt;p&gt;2. Malicious hook files sit in .git/hooks Hidden in the repo are hook files like:&lt;/p&gt;
&lt;p&gt;pre-commit,pre-push,post-checkout.&lt;/p&gt;
&lt;p&gt;Minimize image Edit image Delete image&lt;/p&gt;
&lt;p&gt;Add a caption (optional) These are not ordinary project files. They are execution points Git can trigger automatically.&lt;/p&gt;
&lt;p&gt;The scripts I found were not harmless automation. They used a pattern like:&lt;/p&gt;
&lt;p&gt;detect the OS, fetch a remote payload from an IP address,pipe it directly into sh or cmd,hide output, and run in the background.&lt;/p&gt;
&lt;p&gt;Minimize image Edit image Delete image&lt;/p&gt;
&lt;p&gt;Add a caption (optional) That is a classic downloader-style execution chain.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The payload is triggered by normal Git actions The dangerous part is not just that the scripts exist. It is that they can fire during ordinary Git operations:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;commit,push,checkout.&lt;/p&gt;
&lt;p&gt;So the candidate does not need to do anything unusual.&lt;/p&gt;
&lt;p&gt;A normal workflow action can become the trigger.&lt;/p&gt;
&lt;p&gt;Why this scheme works This workflow is effective because it mixes trust and routine.&lt;/p&gt;
&lt;p&gt;It succeeds when:&lt;/p&gt;
&lt;p&gt;the task looks professional,the app looks real, the instructions sound standard,&lt;/p&gt;
&lt;p&gt;and the candidate follows normal Git behavior without checking hook files first.&lt;/p&gt;
&lt;p&gt;The candidate thinks they are doing an interview task. In reality, they may be executing a hidden payload.&lt;/p&gt;
&lt;p&gt;What makes it dangerous The risk is not just “bad code.”&lt;/p&gt;
&lt;p&gt;The actual danger is the combination of:&lt;/p&gt;
&lt;p&gt;social engineering, believable delivery, hidden Git execution points, and remote code download.&lt;/p&gt;
&lt;p&gt;That means the workflow itself is the attack.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;They inspect src/, package.json, and the dependencies.&lt;/em&gt; &lt;strong&gt;They forget that Git metadata can execute code before the app ever starts.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I did not treat this lightly. I checked the package scripts first. I did not run install commands, startup commands, or any Git action that could trigger hooks. The application itself did not show obvious preinstall or postinstall trickery. The dangerous behavior lived in the Git layer. That distinction matters.&lt;/p&gt;
&lt;p&gt;What made this worse is that Git hooks are not decorative files. Git runs them automatically at specific events. A developer can trigger them just by committing, pushing, or checking out code. So if a malicious hook is sitting inside the repo, it does not matter that the app code looks clean. The attack path is already in place.&lt;/p&gt;
&lt;p&gt;This was the first time I had to think about a job task in two layers at once.&lt;/p&gt;
&lt;p&gt;The visible layer was the app.&lt;/p&gt;
&lt;p&gt;The hidden layer was the repository itself.&lt;/p&gt;
&lt;p&gt;And that changed how I now review any take-home assignment. I no longer start by asking, “Does the app work?” I start by asking, “What executes before the app even opens?”&lt;/p&gt;
&lt;p&gt;This repo contained hook-based remote code execution behavior. And that pattern is not hypothetical. Security writeups have documented Git-hook abuse in fake interview and recruiter-style campaigns, especially where the target is a developer working in crypto or web3. Detection content from security vendors also treats Git-hook execution and hook-origin network egress as suspicious.&lt;/p&gt;
&lt;p&gt;So the practical lesson is not &quot;never trust web3 projects.&quot;&lt;/p&gt;
&lt;p&gt;The lesson is &quot;never trust a repo until you inspect the parts outside the app.&quot;&lt;/p&gt;
&lt;p&gt;If you receive an interview task or a code sample from a stranger, check these before you run anything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;.git/hooks&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;.git/config&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;core.hooksPath&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;package.json scripts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;lockfile install scripts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;any shell, PowerShell, or Python files that run during setup&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If a hook reaches out to the network and executes what it gets back, stop there. That is not a hook anymore. It is a launcher.&lt;/p&gt;
&lt;p&gt;I now treat interview repos as hostile until proven otherwise.&lt;/p&gt;
&lt;p&gt;Not every repo with hooks is malicious. Normal hooks exist. But normal hooks do not phone home and execute remote payloads.&lt;/p&gt;
&lt;p&gt;That is the line.&lt;/p&gt;
&lt;p&gt;I am publishing this because the defensive habit matters more than the story itself. Read the metadata. Check the hooks. Do not assume the dangerous code will be in src/.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If you review take-home repos, have you ever looked past the app code and found something in Git metadata?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;Sources I checked included Git's hook documentation, recent threat-intel reporting on Git-hook abuse, and vendor detection guidance for hook-based persistence and egress.&lt;/p&gt;
&lt;p&gt;Core incident pattern SocPrime — Git hooks used to hide malware in fake interview workflows: &lt;a rel=&quot;nofollow&quot; href=&quot;https://socprime.com/active-threats/lazarus-group-uses-git-hooks-to-hide-malware-dprks-contagious-interview-and-taskjacker-campaigns/socprime&quot;&gt;https://socprime.com/active-threats/lazarus-group-uses-git-hooks-to-hide-malware-dprks-contagious-interview-and-taskjacker-campaigns/socprime&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;OpenSourceMalware — Lazarus / Contagious Interview using Git hooks: &lt;a rel=&quot;nofollow&quot; href=&quot;https://opensourcemalware.com/blog/lazarus-group-uses-git-hooks-to-hide-malwareopensourcemalware&quot;&gt;https://opensourcemalware.com/blog/lazarus-group-uses-git-hooks-to-hide-malwareopensourcemalware&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Andrii.ro — Investigating malware spreading through Git repositories: &lt;a rel=&quot;nofollow&quot; href=&quot;https://andrii.ro/blog/investigating-malwareandrii&quot;&gt;https://andrii.ro/blog/investigating-malwareandrii&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Microsoft Security Blog — Contagious Interview fake developer job malware: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.microsoft.com/en-us/security/blog/2026/03/11/contagious-interview-malware-delivered-through-fake-developer-job-interviews/microsoft&quot;&gt;https://www.microsoft.com/en-us/security/blog/2026/03/11/contagious-interview-malware-delivered-through-fake-developer-job-interviews/microsoft&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Detection and security references Elastic detection rule — Git Hook Command Execution: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.elastic.co/docs/reference/security/prebuilt-rules/rules/linux/persistence\_git\_hook\_executionelastic&quot;&gt;https://www.elastic.co/docs/reference/security/prebuilt-rules/rules/linux/persistence\_git\_hook\_executionelastic&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Elastic / Detection FYI — Git Hook Egress Network Connection: &lt;a rel=&quot;nofollow&quot; href=&quot;https://detection.fyi/elastic/detection-rules/linux/persistence\_git\_hook\_netcon/detection&quot;&gt;https://detection.fyi/elastic/detection-rules/linux/persistence\_git\_hook\_netcon/detection&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Elastic hunting query repo reference: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence\_via\_git\_hook\_pager.tomlgithub&quot;&gt;https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence\_via\_git\_hook\_pager.tomlgithub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SonarSource — Securing developer tools / Git integrations: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.sonarsource.com/blog/securing-developer-tools-git-integrations/sonarsource&quot;&gt;https://www.sonarsource.com/blog/securing-developer-tools-git-integrations/sonarsource&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fake recruiter / fake interview campaign coverage BleepingComputer — Fake job recruiters hide malware in developer coding challenges: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.bleepingcomputer.com/news/security/fake-job-recruiters-hide-malware-in-developer-coding-challenges/bleepingcomputer&quot;&gt;https://www.bleepingcomputer.com/news/security/fake-job-recruiters-hide-malware-in-developer-coding-challenges/bleepingcomputer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;BleepingComputer — New wave of fake interviews use npm packages to spread malware: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.bleepingcomputer.com/news/security/new-wave-of-fake-interviews-use-35-npm-packages-to-spread-malware/bleepingcomputer&quot;&gt;https://www.bleepingcomputer.com/news/security/new-wave-of-fake-interviews-use-35-npm-packages-to-spread-malware/bleepingcomputer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Hacker News — Fake Next.js job repos targeting developers: &lt;a rel=&quot;nofollow&quot; href=&quot;https://thehackernews.com/2026/02/fake-nextjs-repos-target-developers.htmlthehackernews&quot;&gt;https://thehackernews.com/2026/02/fake-nextjs-repos-target-developers.htmlthehackernews&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CSO Online — North Korean cyberspies trick developers via fake job interviews: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.csoonline.com/article/3479795/north-korean-cyberspies-trick-developers-into-installing-malware-with-fake-job-interviews.htmlcsoonline&quot;&gt;https://www.csoonline.com/article/3479795/north-korean-cyberspies-trick-developers-into-installing-malware-with-fake-job-interviews.htmlcsoonline&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CyberSecurityDive — State-linked hackers deploy malware in fake job interviews: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.cybersecuritydive.com/news/north-korean-hackers--fake-interview/739165/cybersecuritydive&quot;&gt;https://www.cybersecuritydive.com/news/north-korean-hackers--fake-interview/739165/cybersecuritydive&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CybersecurityNews — Beware of fake job interview challenges attacking developers: &lt;a rel=&quot;nofollow&quot; href=&quot;https://cybersecuritynews.com/beware-of-fake-job-interview-challenges/cybersecuritynews&quot;&gt;https://cybersecuritynews.com/beware-of-fake-job-interview-challenges/cybersecuritynews&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Economic Times Tech / Graphalgo coverage: &lt;a rel=&quot;nofollow&quot; href=&quot;https://techmonk.economictimes.indiatimes.com/news/cybersecurity/graphalgo-security-alert-fake-job-interviews-are-dropping-rat-malware-on-developer-machines/118188688techmonk.economictimes.indiatimes&quot;&gt;https://techmonk.economictimes.indiatimes.com/news/cybersecurity/graphalgo-security-alert-fake-job-interviews-are-dropping-rat-malware-on-developer-machines/118188688techmonk.economictimes.indiatimes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dev Popper / Bluegrid — Social engineering via fake job offers: &lt;a rel=&quot;nofollow&quot; href=&quot;https://bluegrid.io/blog/dev-popper-how-social-engineering-exploits-developers-with-fake-job-offers/bluegrid&quot;&gt;https://bluegrid.io/blog/dev-popper-how-social-engineering-exploits-developers-with-fake-job-offers/bluegrid&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Git hook hardening / disabling Stack Overflow — Disable Git hooks for security reasons: &lt;a rel=&quot;nofollow&quot; href=&quot;https://stackoverflow.com/questions/35997624/how-to-disable-git-hooks-for-security-reasonstackoverflow&quot;&gt;https://stackoverflow.com/questions/35997624/how-to-disable-git-hooks-for-security-reasonstackoverflow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Stack Overflow — How to globally disable Git hooks: &lt;a rel=&quot;nofollow&quot; href=&quot;https://stackoverflow.com/questions/48135161/how-to-globally-disable-git-hooks&quot;&gt;https://stackoverflow.com/questions/48135161/how-to-globally-disable-git-hooks&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21711/the-real-risk-was-not-the-code-i-could-see-it-was-the-code-git-could-run-for-me</guid>
<pubDate>Wed, 01 Jul 2026 17:06:54 +0000</pubDate>
</item>
<item>
<title>Confessions of a Git Beginner: Why the Terminal Stopped Scaring Me</title>
<link>https://coderlegion.com/21129/confessions-of-a-git-beginner-why-the-terminal-stopped-scaring-me</link>
<description>&lt;p&gt;When I started my coding journey, I thought writing code was the hard part. Then I met Git and the command line. At first, it felt like a series of incantations I had to type perfectly into the terminal, or my entire project would vanish. But after breaking a few things and pushing through the confusion, I realized Git isn't just a tool only, it's also a mindset shift.&lt;/p&gt;
&lt;p&gt;To be completely honest, the terminal terrified me. It felt like a cold, unforgiving void where typing a single wrong character could accidentally delete my entire project or break my machine. For the first few weeks, running commands felt like shouting magic incantations into the dark and praying everything didn't blow up. But a funny thing happens when you use Git every day: the fear fades, and the mindset changes.&lt;/p&gt;
&lt;p&gt;I’m still very much a beginner, but Git has completely changed how I think about building software. Here are the four biggest lessons version control has taught me so far.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Making Mistakes is Part of the Process (The Safety Net)&lt;/strong&gt;&lt;br&gt;
Before I started using Git, writing code felt incredibly high-stakes.&lt;/p&gt;
&lt;p&gt;Git completely flipped that script. It taught me that software development, it’s about having a safety net that allows you to fail safely.&lt;/p&gt;
&lt;p&gt;Realizing that I can ruthlessly experiment, completely mess up a file, and then simply run a command to discard those changes and reset back to safety was a massive breakthrough. Git didn't just save my code; it gave me the psychological freedom to break things on purpose just to see how they work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. The Art of Breaking Things Down&lt;/strong&gt;&lt;br&gt;
Git forced me to stop, breathe, and think modularly. It taught me to break big problems into micro-steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Isolate the feature: &lt;br&gt;
Instead of working on everything in the main branch, I learned to spin up a dedicated feature branch for a single specific task.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Work in tiny increments:&lt;br&gt;
Fix a small bug? Commit it. Format a file? Commit it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write meaningful histories:&lt;br&gt;
Wrapping my head around why &lt;em&gt;git commit -m &quot;fixed stuff&quot;&lt;/em&gt; is a bad idea made me a better critical thinker. Writing a clear, structured commit message forces you to explain what you changed and why.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It made me look at projects differently. I now view coding as a series of small, manageable stepping stones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Communication &amp;amp; Collaboration&lt;/strong&gt;&lt;br&gt;
Even if you are working on a solo project, you are never truly coding alone. At the very least, you are collaborating with Future You.&lt;/p&gt;
&lt;p&gt;There is nothing quite like looking back at a project you wrote three weeks ago and trying to figure out what your past self was thinking. Git taught me that code isn't just written for computers to execute; it's written for humans to read.&lt;/p&gt;
&lt;p&gt;Maintaining a clean Git history, naming branches logically, and writing a solid, detailed &lt;em&gt;README.md&lt;/em&gt; file aren't just administrative chores. They are essential communication tools. Git taught me that being a good developer means being a good teammate to whoever reads my repository next—even if that person is just me in a month.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Navigating the &quot;oops&quot; Moments&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Git will throw errors at you that look like complete gibberish at first. You'll run into an unexpected merge conflict, a permission issue, or a &lt;em&gt;403 Forbidden error&lt;/em&gt; when trying to push to a remote repository.&lt;/p&gt;
&lt;p&gt;In my first week, those errors made my stomach drop. I assumed I had broken everything beyond repair.&lt;/p&gt;
&lt;p&gt;But navigating those &quot;uh-oh&quot; moments taught me the most valuable lesson of all: senior developers don't have fewer errors; they just have more practice solving them.&lt;/p&gt;
&lt;p&gt;I learned to slow down, read the terminal logs closely, check my remote configurations, and realize that every single error is fixable. The terminal isn't an enemy looking to punish your mistakes; it’s just a tool waiting for clear instructions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dfl4f5yx4wcsn0sonmfw.png&quot; alt=&quot;Happy terminal&quot;&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Repetition Builds Muscle Memory&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
If you're a beginner reading this and you still have to look up how to update your branch from main, delete a local branch, or fix a detached HEAD state—that is completely normal.&lt;/p&gt;
&lt;p&gt;I still keep a cheatsheet open on my second monitor. The commands don't stick overnight, but the confidence does. Git stopped being a scary obstacle and became the ultimate developer tool. It keeps me organized, gives me permission to make mistakes, and reminds me that every big problem is just a collection of tiny, committable pieces.&lt;/p&gt;
&lt;p&gt;If you’re hiding from the terminal, take the leap. Type git init, embrace the errors, and let yourself break things. It's the only way to learn!&lt;/p&gt;
&lt;h3 id=&quot;-what-was-the-most-terrifying-git-error-you-ran-into-as-a-beginner-and-how-did-you-fix-it-let-s-swap-horror-stories-in-the-comments-&quot;&gt;️ What was the most terrifying Git error you ran into as a beginner, and how did you fix it? Let's swap horror stories in the comments!&lt;/h3&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21129/confessions-of-a-git-beginner-why-the-terminal-stopped-scaring-me</guid>
<pubDate>Mon, 22 Jun 2026 06:49:46 +0000</pubDate>
</item>
<item>
<title>I Added an AI Gate Before Every git push with no-mistakes ️</title>
<link>https://coderlegion.com/20563/i-added-an-ai-gate-before-every-git-push-with-no-mistakes</link>
<description>&lt;p&gt;We are all using AI to write code now. Whether it's Claude Code, Aider, or Copilot, the speed is incredible. But there is a glaring bottleneck we don't talk about enough: &lt;strong&gt;AI code is often just slightly broken.&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;It forgets an import, misses a type definition, or fails a test.&lt;/p&gt;
&lt;p&gt;Usually, you only find out &lt;em&gt;after&lt;/em&gt; you push to GitHub. Your CI/CD pipeline turns red , and you end up polluting your Git history with a dozen commits titled &lt;code&gt;fix: linting&lt;/code&gt; or &lt;code&gt;fix: missing test variable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I recently found a repository called &lt;a rel=&quot;nofollow&quot; href=&quot;https://kunchenguid.github.io/no-mistakes/&quot;&gt;no-mistakes&lt;/a&gt; that solves this brilliantly. It acts as a local proxy between your terminal and GitHub, forcing AI to test and fix its own code before anyone else sees it.&lt;/p&gt;
&lt;p&gt;Here is why it's worth a look. &lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-with-traditional-workflows&quot;&gt;The Problem With Traditional Workflows&lt;/h3&gt;
&lt;p&gt;Right now, developers handle broken code in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The CI/CD Walk of Shame :&lt;/strong&gt; You push code, wait 5 minutes for GitHub Actions to fail, pull the error locally, fix it, and push again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pre-commit Hooks (Husky) :&lt;/strong&gt; You set up local hooks. When you try to commit, it yells at you about formatting and blocks the commit until you manually fix it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both methods are &lt;strong&gt;passive&lt;/strong&gt;. They tell you something is broken, but they leave the cleanup to you. When you're using an AI coding agent to generate the code in the first place, manually babysitting its output defeats the purpose.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;enter-no-mistakes&quot;&gt;Enter &lt;code&gt;no-mistakes&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;no-mistakes&lt;/code&gt; is a CLI tool that intercepts your &lt;code&gt;git push&lt;/code&gt;. Instead of sending your code straight to origin, it routes it through a localized validation pipeline.&lt;/p&gt;
&lt;h4 id=&quot;-how-it-actually-works-&quot;&gt;⚙️ How it actually works:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Hidden Sandbox:&lt;/strong&gt; When you trigger it, it creates a temporary git worktree in the background. Your active editor stays completely undisturbed.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Validation:&lt;/strong&gt; It runs your tests, linter, and build steps inside that isolated sandbox.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The AI Feedback Loop:&lt;/strong&gt; If a test fails, it captures the error log and hands it back to an AI agent, essentially saying: &lt;em&gt;&quot;You broke this test. Fix it.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Clean Push:&lt;/strong&gt; Once the AI patches the code and all tests pass, it pushes the clean code to GitHub and auto-opens a Pull Request. It then deletes the temporary sandbox.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-makes-it-stand-out&quot;&gt;What Makes It Stand Out&lt;/h3&gt;
&lt;p&gt;The biggest differentiator is that &lt;strong&gt;it is active, not passive.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If a pre-commit hook fails, the process stops. If &lt;code&gt;no-mistakes&lt;/code&gt; fails, it initiates an &lt;strong&gt;agentic review loop&lt;/strong&gt;. It uses an interactive Terminal UI (TUI) to show you exactly what the AI is trying to fix. If the AI gets stuck, you can step in and provide guidance right in the terminal.&lt;/p&gt;
&lt;p&gt;It also integrates natively with tools like Claude Code. You can literally tell your agent, &lt;em&gt;&quot;Use the /no-mistakes skill to verify and gate these changes,&quot;&lt;/em&gt; and it will handle the entire lifecycle. &lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-key-features-at-a-glance&quot;&gt;✨ Key Features at a Glance&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Non-Blocking:&lt;/strong&gt; Because it uses hidden worktrees, you can immediately start working on your next feature while it tests and fixes the last one in the background.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-Rebase:&lt;/strong&gt; It automatically rebases your code on top of the latest &lt;code&gt;main&lt;/code&gt; branch before testing, preventing hidden merge conflicts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-Documentation:&lt;/strong&gt; It reviews the code changes and updates your &lt;code&gt;README.md&lt;/code&gt; to keep documentation from going stale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean PRs:&lt;/strong&gt; You never push failing builds upstream.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-to-get-started&quot;&gt;️ How to Get Started&lt;/h3&gt;
&lt;p&gt;Setup takes about 30 seconds. ⚡&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Install the CLI tool globally:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://raw.githubusercontent.com/kunchenguid/no-mistakes/main/docs/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. Initialize it in your project folder:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;no-mistakes init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This wires up a new git remote proxy called &lt;code&gt;no-mistakes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Change how you push:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The next time you (or your AI) finish a feature, skip &lt;code&gt;git push origin&lt;/code&gt;. Instead, run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push no-mistakes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tool will take over, spin up the worktree, run your suites, fix the AI's slop, and open the PR for you. &lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;let-s-discuss&quot;&gt;Let's Discuss&lt;/h3&gt;
&lt;p&gt;As AI agents write more of our code, our tooling needs to shift from &lt;strong&gt;writing&lt;/strong&gt; to &lt;strong&gt;verifying&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;How are you currently handling AI-generated bugs in your workflow? Are you still manually running tests and fixing the AI's mistakes, or have you started automating the review loop? Let me know in the comments! &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20563/i-added-an-ai-gate-before-every-git-push-with-no-mistakes</guid>
<pubDate>Sun, 14 Jun 2026 03:34:08 +0000</pubDate>
</item>
<item>
<title>Error: pulling is not possible because you have unmerged files.</title>
<link>https://coderlegion.com/20415/error-pulling-is-not-possible-because-you-have-unmerged-files</link>
<description>&lt;div&gt;&lt;p&gt;
When working with Git, it's not uncommon to encounter a variety of error messages. One of the most common error messages you might come across is &quot;&lt;span style=&quot;color: #FF04FC;&quot;&gt;Error: pulling is not possible because you have unmerged files&lt;/span&gt;.&quot; This error message can be frustrating, especially if you're not quite sure what it means or how to fix it. In this article, we'll take a deep dive into what this error message means, why it occurs, and most importantly, how to fix it. So, whether you're a seasoned Git veteran or a newcomer to the world of version control, keep reading to learn everything you need to know about resolving this error message and getting back to work on your project.
&lt;/p&gt;&lt;/div&gt;&lt;hr&gt;
&lt;div id=&quot;toc-div&quot;&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt; Table of Contents:&lt;a id=&quot;toc-toogle&quot; class=&quot;open&quot;&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div id=&quot;toc-list&quot;&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#why-the-error&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Why the error&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#some-basic-concepts-of-git&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Some basic concepts of git&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#useful-resources-to-master-git&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Useful resources to master git&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#conclusion&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Conclusion&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div&gt;&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Why the error&lt;/span&gt;&lt;a id=&quot;why-the-error&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;When working with version control systems like Git, it's not uncommon to encounter the error message &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;Error: pulling is not possible because you have unmerged files.&quot;&lt;/span&gt; This error occurs when you try to pull in changes from a remote repository, but your local repository has changes that have not yet been merged with the remote repository.&lt;/p&gt;&lt;p&gt;To understand why this error occurs, it's helpful to first understand how Git manages changes. When you make changes to a file in your local repository, Git considers those changes to be &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;uncommitted.&quot;&lt;/span&gt; In order to share those changes with others, you need to &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;commit&quot;&lt;/span&gt; them to your local repository. Once you've committed your changes, you can then &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;push&quot;&lt;/span&gt; them to a remote repository, where they can be shared with others.&lt;/p&gt;&lt;p&gt;The error message &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;Error: pulling is not possible because you have unmerged files&quot;&lt;/span&gt; is telling you that there are changes in your local repository that have not yet been merged with the remote repository. This could be because you haven't committed your changes yet, or it could be because someone else has pushed changes to the remote repository that conflict with your changes.&lt;/p&gt;&lt;p&gt;When you try to pull changes from a remote repository, Git compares the state of your local repository with the state of the remote repository. If there are no conflicts between the two, Git will automatically merge the changes and update your local repository. However, if there are conflicts, Git will stop the process and give you an error message.&lt;/p&gt;&lt;p&gt;To resolve this error, you'll need to first commit any changes you've made in your local repository, and then use Git's merge tools to resolve any conflicts. Once you've done this, you should be able to pull in changes from the remote repository without encountering any errors.&lt;/p&gt;&lt;p&gt;It's worth noting that this error is not an uncommon situation when working with a team on a project, as multiple people can work on the same files and push their changes to the remote repository. To avoid this error, it's a good practice to pull in changes from the remote repository before starting to work on a file, and also to commit your changes frequently and push them to the remote repository.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Some basic concepts of git&lt;/span&gt;&lt;a id=&quot;some-basic-concepts-of-git&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Git is a distributed version control system that allows developers to track changes in their code and collaborate with others on a project. With Git, developers can easily keep track of different versions of their code, roll back to previous versions, and merge changes made by multiple people.&lt;/p&gt;&lt;p&gt;One of the key concepts in Git is the &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;repository,&quot;&lt;/span&gt; which is a container for all the files and directories related to a project. When you create a new project, you can initialize a new repository using the command &quot;git init.&quot; Once you've initialized a repository, you can start adding files to it using the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git add&lt;/code&gt;&lt;/span&gt; For example, if you have a file called &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/span&gt; that you want to add to your repository, you would use the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git add main.py&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Once you've added your files to the repository, you can &lt;span style=&quot;color: #FF04FC;&quot;&gt;&quot;commit&quot;&lt;/span&gt; your changes. A commit is a snapshot of your repository at a specific point in time. When you commit your changes, you're essentially telling Git that you want to save the current state of your repository. You can create a new commit using the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git commit -m 'Commit message'&lt;/code&gt;&lt;/span&gt; where &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;Commit message&lt;/code&gt;&lt;/span&gt; is a description of the changes you made.&lt;/p&gt;&lt;p&gt;Another important concept in Git is the &quot;&lt;span style=&quot;color: #FF04FC;&quot;&gt;branch&lt;/span&gt;.&quot; A branch is a way to separate your work on a project into different threads. By default, when you create a new repository, it has a single branch called &quot;&lt;span style=&quot;color: #FF04FC;&quot;&gt;master&lt;/span&gt;.&quot; You can create a new branch using the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git branch new_branch&lt;/code&gt;&lt;/span&gt; and switch between branches using the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git checkout branch_name&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;One of the most powerful features of Git is its ability to collaborate with others. When you're working on a project with other people, you can use a &quot;&lt;span style=&quot;color: #FF04FC;&quot;&gt;remote repository&lt;/span&gt;&quot; to share your changes with others. A remote repository is a copy of your repository that's stored on a server. You can create a new remote repository using the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git remote add origin [URL]&lt;/code&gt;&lt;/span&gt; and then push your changes to it using the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git push -u origin master&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;To fetch and merge the changes made by others in the remote repository, you can use the command &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/span&gt;. This command will fetch the changes from remote repository and merge them with your local repository.&lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note &lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;
It is recommended a good practice to always pull before pushing to a remote repository. This is particularly useful when working collaboratively in a team.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;These are some of the basic commands that you can use to work with Git. However, Git is a complex tool with many features and options. To master Git, it's recommended to explore more advanced commands and concepts, such as merging, rebasing and resolving conflicts.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Useful resources to master git&lt;/span&gt;&lt;a id=&quot;useful-resources-to-master-git&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Pro Git by Scott Chacon and Ben Straub: This is considered as one of the best resources for learning Git. It's available for free online and covers all the basic and advanced concepts of Git in a clear and concise manner.&lt;/li&gt;&lt;li&gt;Learn Git Branching by Atlassian: This is an interactive tutorial that covers the basics of Git branching. It uses a visual approach to help you understand the concepts of Git.&lt;/li&gt;&lt;li&gt;Git documentation: The official documentation of Git is a great resource to learn about the different commands and features of Git.&lt;/li&gt;&lt;li&gt;Git Immersion by EdgeCase: This is a comprehensive tutorial that covers all the basic and advanced concepts of Git. It's available for free online and includes exercises and quizzes to help you test your understanding.&lt;/li&gt;&lt;li&gt;Version Control with Git by Jonathan McGlone: This is a comprehensive guide that covers all the basic and advanced concepts of Git. The book is available online for free and covers all the basic and advanced concepts of Git.&lt;/li&gt;&lt;li&gt;Git Hub Learning Lab: A series of interactive tutorials and exercises that can help you learn Git by doing.&lt;/li&gt;&lt;li&gt;Git Tutorial on Codecademy: An interactive tutorial that covers the basics of Git and how to use it to collaborate on a project.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These resources are considered as good starting point for those who want to learn Git and master it. They provide a comprehensive and in-depth understanding of the different features and concepts of Git, along with examples and interactive exercises to help you practice and improve your skills.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Conclusion&lt;/span&gt;&lt;a class=&quot;custom_anchor&quot;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Git is a powerful tool that allows developers to track changes in their code, collaborate with others and maintain different versions of the code. Basic commands like &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/span&gt;, &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git add&lt;/code&gt;&lt;/span&gt;, &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;/span&gt;, &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git branch&lt;/code&gt;&lt;/span&gt;, &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/span&gt; and &lt;span style=&quot;color: #FF04FC;&quot;&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/span&gt; are essential to work with git and keep track of the changes in your code.&lt;/p&gt;&lt;/div&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20415/error-pulling-is-not-possible-because-you-have-unmerged-files</guid>
<pubDate>Fri, 12 Jun 2026 13:39:35 +0000</pubDate>
</item>
<item>
<title>Why GitHub 2FA Won’t Save You From Infostealers</title>
<link>https://coderlegion.com/20110/why-github-2fa-wont-save-you-from-infostealers</link>
<description>&lt;p&gt;Imagine this: it is 3:00 AM. Your laptop is closed, your computer is off, and you are asleep. Suddenly, your phone buzzes with an email notification from GitHub: &lt;strong&gt;“Security Alert: Force-push detected on your repository.”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You jump out of bed, open GitHub, and start checking everything. Your active web sessions look normal. Your security log does not show a strange login. There is no obvious “new token generated” event. No one seems to have entered your account through the browser.&lt;/p&gt;
&lt;p&gt;But when you open your repositories, something is wrong. A strange script has been pushed into your codebase. Your package scripts have been modified. Your build files have been touched. The new code is designed to steal your production environment variables.&lt;/p&gt;
&lt;p&gt;The scary part is this: GitHub 2FA can be enabled, and this can still happen. Not because 2FA is useless, but because 2FA mainly protects the login screen. It does not automatically protect every credential that already exists on your machine. And that is exactly where modern infostealers attack.&lt;/p&gt;
&lt;h3 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;GitHub 2FA helps protect your account login, but it may not save you if malware steals an already-authorized Git credential from your local machine.&lt;/p&gt;
&lt;p&gt;If you use HTTPS Git remotes, your system may cache a Personal Access Token, OAuth token, GitHub CLI token, VS Code token, or another GitHub-related credential through your OS credential manager or Git credential helper. That token may be encrypted while stored, but once malware runs inside your active user session, it may be able to ask the same local tools Git uses to retrieve a usable credential.&lt;/p&gt;
&lt;p&gt;Once the attacker has a valid token, they may not need your password, browser session, or 2FA code. They can push code from somewhere else. And if your repository is connected to an auto-deploy platform, that push can quickly become a production incident.&lt;/p&gt;
&lt;p&gt;The fix is not just “enable 2FA.” You also need to revoke exposed tokens, clear cached Git credentials, move from HTTPS remotes to passphrase-protected SSH keys, disable direct force-pushes, protect your production branch, scan your codebase for malicious changes, and reduce the secrets available during CI/CD builds.&lt;/p&gt;
&lt;h3 id=&quot;the-false-sense-of-security-around-github-2fa&quot;&gt;The False Sense of Security Around GitHub 2FA&lt;/h3&gt;
&lt;p&gt;Most developers hear “2FA enabled” and feel safe. To be fair, 2FA is important. It protects your GitHub account from a very common attack: someone stealing or guessing your password and trying to log in through the website.&lt;/p&gt;
&lt;p&gt;But Git does not always work like a normal website login. When you push code from your terminal, VS Code, GitHub Desktop, or another development tool, you are often not typing your GitHub password every time. Instead, your machine uses an already-authorized credential.&lt;/p&gt;
&lt;p&gt;That credential might be a Personal Access Token, an OAuth token, a GitHub CLI token, a VS Code GitHub authentication token, or another credential stored through a Git credential helper. This is where the problem starts.&lt;/p&gt;
&lt;p&gt;2FA may stop a new login attempt, but if malware steals a token that has already been authorized, the attacker may be able to act as you without ever touching the 2FA flow. They are no longer trying to break into the front door. They found a copied key.&lt;/p&gt;
&lt;h3 id=&quot;how-infostealers-abuse-local-developer-environments&quot;&gt;How Infostealers Abuse Local Developer Environments&lt;/h3&gt;
&lt;p&gt;Modern infostealers do not always need to “hack GitHub.” Sometimes, they only need to compromise the developer’s machine.&lt;/p&gt;
&lt;p&gt;That compromise can happen through things developers interact with every day: a fake VS Code extension, a malicious npm package, a compromised dependency, a poisoned postinstall script, a fake CLI tool, a cracked app, a malicious browser extension, or a script copied from the internet without proper inspection.&lt;/p&gt;
&lt;p&gt;Once malware runs on your computer, it may inherit your normal user permissions. That part matters. Your operating system may trust processes running under your user account, and your Git tools may also trust that same environment.&lt;/p&gt;
&lt;p&gt;So even if your token is not lying around as plain text on disk, it can still become exposed when a trusted local tool retrieves it for Git operations. In simple terms: the credential may be protected while stored, but it can still be returned in usable form when your local Git workflow asks for it. That is the gap infostealers target.&lt;/p&gt;
&lt;h3 id=&quot;the-local-credential-problem&quot;&gt;The Local Credential Problem&lt;/h3&gt;
&lt;p&gt;Many developers clone repositories using HTTPS URLs like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;https://github.com/username/repository.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using HTTPS, Git usually needs a credential to authenticate pushes and private repository access. To avoid asking you for a password or token every single time, Git can use a credential helper.&lt;/p&gt;
&lt;p&gt;Depending on your OS and setup, this credential may be stored or retrieved through macOS Keychain, Windows Credential Manager, Linux libsecret, Git Credential Manager, GitHub CLI, VS Code authentication helpers, or other developer tools.&lt;/p&gt;
&lt;p&gt;This is convenient, but convenience creates a security tradeoff. If a malicious script can run inside your active user session, it may be able to interact with the same credential system your Git client uses. That means the attacker does not need your GitHub password. They do not need your 2FA code. They just need the already-authorized credential your machine uses to push code.&lt;/p&gt;
&lt;h3 id=&quot;the-3-00-am-attack&quot;&gt;The 3:00 AM Attack&lt;/h3&gt;
&lt;p&gt;Here is the attack flow in plain English.&lt;/p&gt;
&lt;p&gt;First, malware runs on your machine. Maybe it came from a malicious package, a fake extension, or a script you ran while trying to fix something. The malware may only need to run once.&lt;/p&gt;
&lt;p&gt;Next, it looks for developer credentials. It may search for Git credentials, GitHub CLI tokens, npm tokens, cloud provider keys, &lt;code&gt;.env&lt;/code&gt; files, SSH keys without passphrases, browser sessions, deployment tokens, and CI/CD-related secrets.&lt;/p&gt;
&lt;p&gt;Then, if your GitHub HTTPS credential can be retrieved in usable form, the malware sends it to the attacker. At that point, the attacker does not need to keep your laptop online. The credential can be used from another machine.&lt;/p&gt;
&lt;p&gt;Hours or days later, an automated server controlled by the attacker uses that credential to push malicious code to your repositories. It may force-push, modify build files, alter package scripts, or target multiple repositories at once. Because the push is authenticated with a valid credential, it may look like a legitimate Git operation.&lt;/p&gt;
&lt;p&gt;That is why you can wake up confused. You may not see a new login. You may not see a new browser session. You may not see a fresh “2FA passed” event. The attacker did not necessarily log in through the web UI. They reused a credential that already existed.&lt;/p&gt;
&lt;p&gt;This is why the attack feels invisible. Not because nothing happened, but because you are looking in the wrong place.&lt;/p&gt;
&lt;h3 id=&quot;why-this-becomes-worse-with-auto-deploy-platforms&quot;&gt;Why This Becomes Worse With Auto-Deploy Platforms&lt;/h3&gt;
&lt;p&gt;A repository breach is already bad, but for modern developers, a repository breach can quickly become a production breach.&lt;/p&gt;
&lt;p&gt;Many projects are connected to auto-deploy platforms like Vercel, Netlify, Render, Railway, Fly.io, Dokploy, Coolify, GitHub Actions, or self-hosted CI/CD pipelines. This creates a dangerous chain reaction.&lt;/p&gt;
&lt;p&gt;The attacker pushes malicious code. Your deployment platform sees a new commit. The platform pulls the code. The build starts automatically. The malicious code runs during the build. And build time is often when secrets are available.&lt;/p&gt;
&lt;p&gt;This is called poisoned pipeline execution. The attacker does not only want your source code. They want the secrets your deployment system can access: database URLs, API keys, AWS credentials, Firebase credentials, payment provider keys, SMTP credentials, private service tokens, and production environment variables.&lt;/p&gt;
&lt;p&gt;The payload can hide inside files developers rarely inspect carefully, such as &lt;code&gt;package.json&lt;/code&gt; scripts, &lt;code&gt;postinstall&lt;/code&gt; hooks, &lt;code&gt;vite.config.js&lt;/code&gt;, &lt;code&gt;next.config.js&lt;/code&gt;, &lt;code&gt;tailwind.config.js&lt;/code&gt;, build scripts, deployment scripts, or custom CLI files.&lt;/p&gt;
&lt;p&gt;That is why this attack is dangerous. It does not stop at GitHub. It can move from your machine, to your repository, to your CI/CD pipeline, to your production environment.&lt;/p&gt;
&lt;h3 id=&quot;the-real-lesson-do-not-treat-git-credentials-like-permanent-keys&quot;&gt;The Real Lesson: Do Not Treat Git Credentials Like Permanent Keys&lt;/h3&gt;
&lt;p&gt;The real problem is not just HTTPS. The real problem is permanent, reusable credentials that can be copied and used elsewhere.&lt;/p&gt;
&lt;p&gt;A cached HTTPS token is powerful. If an attacker gets the raw token, they may be able to use it from another machine, depending on its scope and validity. That is a risky security model for sensitive projects.&lt;/p&gt;
&lt;p&gt;A better model is explicit approval. Your Git push should require something that is not easily copied and reused remotely.&lt;/p&gt;
&lt;p&gt;This is where SSH keys with strong passphrases become useful. With SSH, authentication depends on a private key stored on your machine. If you protect that private key with a strong passphrase, an attacker cannot simply copy one token string and immediately use it from a cloud server.&lt;/p&gt;
&lt;p&gt;To be clear, this does not make your machine invincible. If malware is running on your computer, you still have a serious problem. Malware can tamper with files, watch your activity, steal other secrets, or wait for you to unlock something.&lt;/p&gt;
&lt;p&gt;But passphrase-protected SSH removes one major weakness: a silently stolen HTTPS token that works anywhere in the world.&lt;/p&gt;
&lt;h3 id=&quot;practical-lockdown-guide&quot;&gt;Practical Lockdown Guide&lt;/h3&gt;
&lt;p&gt;The goal is to move from this:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“My machine silently provides a reusable token whenever Git needs it.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“A push requires my local SSH key and my manual passphrase approval.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;phase-1-revoke-existing-github-tokens&quot;&gt;Phase 1: Revoke Existing GitHub Tokens&lt;/h3&gt;
&lt;p&gt;First, assume any old credential may already be exposed. Do not only delete it from your laptop. If the attacker already copied it, deleting it locally is not enough. You need to revoke it from GitHub.&lt;/p&gt;
&lt;p&gt;Go to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;GitHub → Settings → Applications
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check both:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Authorized OAuth Apps
Authorized GitHub Apps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Revoke anything you do not recognize. Pay close attention to tools with broad repository access, such as old IDE integrations, old Git clients, abandoned CLI tools, deployment services you no longer use, random apps you tested once, or anything with full repository permissions.&lt;/p&gt;
&lt;p&gt;Also check your tokens:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;GitHub → Settings → Developer settings → Personal access tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Review both classic and fine-grained tokens. Delete anything you do not actively use. For tokens you must keep, reduce the scope. A token should not have full account access when it only needs access to one repository.&lt;/p&gt;
&lt;h3 id=&quot;phase-2-clear-cached-github-https-credentials-locally&quot;&gt;Phase 2: Clear Cached GitHub HTTPS Credentials Locally&lt;/h3&gt;
&lt;p&gt;Next, clear GitHub credentials from your local credential helper. This helps prevent your machine from silently reusing old HTTPS authentication.&lt;/p&gt;
&lt;h4 id=&quot;macos&quot;&gt;macOS&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;security delete-internet-password -s github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on your setup, you may also need to inspect Keychain Access manually and remove GitHub-related internet passwords.&lt;/p&gt;
&lt;h4 id=&quot;windows&quot;&gt;Windows&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bat&quot;&gt;(echo protocol=https &amp;amp; echo host=github.com &amp;amp; echo.) | git credential reject
cmdkey /delete:LegacyGeneric:target=git:https://github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also open Windows Credential Manager and remove GitHub-related credentials manually.&lt;/p&gt;
&lt;h4 id=&quot;linux&quot;&gt;Linux&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;printf &quot;protocol=https\nhost=github.com\n\n&quot; | git credential reject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on your credential helper, you may also need to clear entries from libsecret, GNOME Keyring, KWallet, or a plaintext Git credential file if one was configured.&lt;/p&gt;
&lt;h4 id=&quot;verification-check&quot;&gt;Verification check&lt;/h4&gt;
&lt;p&gt;Run this locally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;printf &quot;protocol=https\nhost=github.com\n\n&quot; | git credential fill
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If nothing useful prints, your local Git credential cache is likely clean. If a username or password/token appears, do not share the output anywhere. Clear the credential again and inspect your credential helper configuration.&lt;/p&gt;
&lt;h3 id=&quot;phase-3-disable-automatic-ide-token-sharing&quot;&gt;Phase 3: Disable Automatic IDE Token Sharing&lt;/h3&gt;
&lt;p&gt;Some IDEs and developer tools try to make Git authentication easier by automatically sharing credentials with your terminal. That is convenient, but if your goal is strict manual approval, you should reduce this automation.&lt;/p&gt;
&lt;p&gt;In VS Code, open settings and search:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;git.terminalAuthentication
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Disable it. This helps prevent VS Code from automatically providing GitHub authentication to terminal Git commands. Also review the GitHub Pull Requests extension, GitHub authentication, and any third-party Git-related extensions you installed. Remove what you do not need.&lt;/p&gt;
&lt;h3 id=&quot;phase-4-generate-a-passphrase-protected-ssh-key&quot;&gt;Phase 4: Generate a Passphrase-Protected SSH Key&lt;/h3&gt;
&lt;p&gt;Now create a dedicated SSH key for GitHub:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C &quot;*Emails are not allowed*&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When asked where to save the key, you can accept the default path:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When asked for a passphrase, do not leave it empty. Use a strong passphrase you can remember. This passphrase protects your private key if someone tries to copy it.&lt;/p&gt;
&lt;h3 id=&quot;phase-5-add-the-ssh-public-key-to-github&quot;&gt;Phase 5: Add the SSH Public Key to GitHub&lt;/h3&gt;
&lt;p&gt;Copy your public key.&lt;/p&gt;
&lt;h4 id=&quot;macos-1&quot;&gt;macOS&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pbcopy &amp;lt; ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;windows-powershell&quot;&gt;Windows PowerShell&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-Content ~/.ssh/id_ed25519.pub | Set-Clipboard
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;linux-1&quot;&gt;Linux&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then go to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;GitHub → Settings → SSH and GPG keys → New SSH key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Give it a clear title, such as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Personal MacBook Terminal SSH Key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste the public key and save it.&lt;/p&gt;
&lt;p&gt;Never upload your private key. Your private key is the file without &lt;code&gt;.pub&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;phase-6-create-an-ssh-config&quot;&gt;Phase 6: Create an SSH Config&lt;/h3&gt;
&lt;p&gt;Create or edit your SSH config file.&lt;/p&gt;
&lt;h4 id=&quot;macos-linux&quot;&gt;macOS/Linux&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;windows-1&quot;&gt;Windows&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bat&quot;&gt;notepad %USERPROFILE%\.ssh\config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sshconfig&quot;&gt;Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avoid adding automatic keychain options if your goal is to require manual passphrase approval. On macOS, for example, avoid blindly adding settings that store the passphrase in Keychain unless you intentionally want that convenience.&lt;/p&gt;
&lt;p&gt;On macOS/Linux, secure the permissions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;phase-7-convert-your-repositories-from-https-to-ssh&quot;&gt;Phase 7: Convert Your Repositories From HTTPS to SSH&lt;/h3&gt;
&lt;p&gt;Your local projects may still point to HTTPS remotes. Check a project by running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;https://github.com/username/repository.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change it to SSH:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote set-url origin *Emails are not allowed*:username/repository.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now test SSH authentication:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -T *Emails are not allowed*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a passphrase prompt for your SSH key. After entering the passphrase, GitHub should respond with a message saying you authenticated successfully, but GitHub does not provide shell access. That means SSH authentication is working.&lt;/p&gt;
&lt;h3 id=&quot;optional-automate-the-cleanup-for-multiple-repositories&quot;&gt;Optional: Automate the Cleanup for Multiple Repositories&lt;/h3&gt;
&lt;p&gt;If you have many repositories on your machine, changing every remote manually can be stressful. That is why I built an open-source tool called &lt;code&gt;git-malware-remediator&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The tool was created to help developers recover faster after this kind of Git/GitHub compromise. Instead of manually opening dozens of folders and checking every &lt;code&gt;.git&lt;/code&gt; config one by one, it can help scan your local projects, detect HTTPS GitHub remotes, convert them to SSH format, and look for suspicious traces that may indicate malware activity in your codebase.&lt;/p&gt;
&lt;p&gt;It can also help with codebase cleanup by scanning for common malicious patterns, suspicious scripts, altered Git configurations, and possible payload traces that may have been introduced during the attack. This makes it useful not only for switching repositories from HTTPS to SSH, but also for reviewing and fixing compromised projects after a suspected malware incident.&lt;/p&gt;
&lt;p&gt;You can get it here:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/JC-Coder/git-malware-remediator.git
cd git-malware-remediator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then follow the README to run the scan, review the findings, and apply the recommended fixes.&lt;/p&gt;
&lt;h3 id=&quot;protect-the-repository-not-just-your-laptop&quot;&gt;Protect the Repository, Not Just Your Laptop&lt;/h3&gt;
&lt;p&gt;Local security is only one side of the problem. You also need to harden the repository itself.&lt;/p&gt;
&lt;p&gt;Even if your machine is compromised, the attacker should not be able to destroy your production branch with one push.&lt;/p&gt;
&lt;h3 id=&quot;1-disable-direct-force-pushes&quot;&gt;1. Disable Direct Force-Pushes&lt;/h3&gt;
&lt;p&gt;Go to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Repository → Settings → Branches → Branch protection rules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a rule for your production branch, usually &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt;. Make sure force-pushes are not allowed. If possible, enforce this rule for administrators too.&lt;/p&gt;
&lt;p&gt;A stolen credential should not be able to rewrite your production history without resistance.&lt;/p&gt;
&lt;h3 id=&quot;2-require-pull-requests-before-merging&quot;&gt;2. Require Pull Requests Before Merging&lt;/h3&gt;
&lt;p&gt;Do not allow direct pushes to production branches. Require pull requests, reviews, and status checks.&lt;/p&gt;
&lt;p&gt;This creates a human checkpoint between a compromised local machine and production deployment.&lt;/p&gt;
&lt;p&gt;A good rule is simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;No code reaches production without review.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even if you are a solo developer, use pull requests for important projects. Future you is also a reviewer.&lt;/p&gt;
&lt;h3 id=&quot;3-protect-ci-cd-secrets&quot;&gt;3. Protect CI/CD Secrets&lt;/h3&gt;
&lt;p&gt;Many developers accidentally expose too many secrets during build time. That is dangerous. If a malicious script runs during build, it may be able to read whatever secrets the build environment can access.&lt;/p&gt;
&lt;p&gt;Do not give build scripts access to production secrets unless absolutely necessary. Prefer runtime secrets where possible. Keep database credentials inside the server runtime, avoid exposing production &lt;code&gt;.env&lt;/code&gt; values during frontend builds, separate preview secrets from production secrets, rotate secrets after any suspected compromise, and review which environment variables your deployment platform exposes during build.&lt;/p&gt;
&lt;p&gt;The goal is simple: if a build script gets poisoned, it should not have everything it needs to drain your production environment.&lt;/p&gt;
&lt;h3 id=&quot;4-pin-and-review-dependencies&quot;&gt;4. Pin and Review Dependencies&lt;/h3&gt;
&lt;p&gt;Infostealers often reach developers through the package ecosystem, so do not treat dependencies as harmless.&lt;/p&gt;
&lt;p&gt;Be careful with packages that have very few downloads, recently changed maintainers, strange install scripts, obfuscated code, typosquatted names, or unnecessary access to your file system.&lt;/p&gt;
&lt;p&gt;Also watch your &lt;code&gt;package.json&lt;/code&gt; scripts. A malicious script can hide in places like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;scripts&quot;: {
    &quot;postinstall&quot;: &quot;node suspicious-file.js&quot;,
    &quot;build&quot;: &quot;node hidden-script.js &amp;amp;&amp;amp; vite build&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your scripts are part of your security boundary. Treat them that way.&lt;/p&gt;
&lt;h3 id=&quot;5-rotate-secrets-after-a-suspected-breach&quot;&gt;5. Rotate Secrets After a Suspected Breach&lt;/h3&gt;
&lt;p&gt;If you suspect your repository was modified by malware, do not only revert the commit. Assume secrets may have been exposed.&lt;/p&gt;
&lt;p&gt;Rotate GitHub tokens, npm tokens, database passwords, API keys, cloud credentials, deployment tokens, webhook secrets, SMTP credentials, payment provider keys, and any secret that was available on the machine or CI/CD environment.&lt;/p&gt;
&lt;p&gt;Reverting code fixes the repository. Rotating secrets fixes the damage that may have already happened.&lt;/p&gt;
&lt;h3 id=&quot;what-to-do-if-you-wake-up-to-a-suspicious-force-push&quot;&gt;What To Do If You Wake Up To a Suspicious Force-Push&lt;/h3&gt;
&lt;p&gt;If you receive a GitHub alert or notice strange repository changes, act quickly.&lt;/p&gt;
&lt;p&gt;First, disconnect the machine from the internet to stop possible ongoing exfiltration. Do not keep building or running the project, because the malicious code may be inside build scripts. Revoke GitHub tokens immediately, including OAuth apps, GitHub Apps, and suspicious integrations.&lt;/p&gt;
&lt;p&gt;Next, rotate deployment and production secrets. Assume anything available to the repository or build pipeline may be exposed. Check repository activity, branch history, force-push events, and recent commits. If the repository belongs to an organization, check organization audit logs if available.&lt;/p&gt;
&lt;p&gt;Also inspect CI/CD logs. Look for strange build commands, unexpected network requests, changed build scripts, or unknown environment variable access patterns. Restore from a known-good commit, and do not trust the latest branch state until you inspect it.&lt;/p&gt;
&lt;p&gt;If malware ran on your machine, consider the machine untrusted until you properly clean it. In serious cases, a full OS reinstall may be safer than trying to manually remove everything.&lt;/p&gt;
&lt;h3 id=&quot;the-point-is-not-2fa-is-bad-&quot;&gt;The Point Is Not “2FA Is Bad”&lt;/h3&gt;
&lt;p&gt;2FA is not bad. You should still use it. In fact, you should use strong 2FA, preferably with passkeys or hardware security keys where possible.&lt;/p&gt;
&lt;p&gt;The point is this: 2FA protects authentication events. It does not automatically protect every credential, token, key, secret, and build pipeline connected to your development workflow.&lt;/p&gt;
&lt;p&gt;Modern developer security is bigger than your GitHub password. You need to protect your local machine, Git credentials, SSH keys, IDE, dependencies, repository settings, CI/CD pipeline, and production secrets.&lt;/p&gt;
&lt;p&gt;Attackers know developers are the gateway to production. That is why they target our tools.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-3&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Convenience is useful, but too much invisible convenience can become dangerous.&lt;/p&gt;
&lt;p&gt;Cached credentials, automatic IDE authentication, broad tokens, direct pushes, and auto-deploy pipelines all make development faster. They also make attacks faster.&lt;/p&gt;
&lt;p&gt;GitHub 2FA is important, but it is not a magic shield. If an infostealer gets a valid local Git credential, it may bypass the login screen entirely and operate through the same trusted path your tools use every day.&lt;/p&gt;
&lt;p&gt;So draw a harder line. Revoke old tokens. Clear cached HTTPS credentials. Move sensitive repositories to passphrase-protected SSH. Disable force-pushes. Require pull requests. Limit build-time secrets. Scan your codebase for malicious changes. Fix suspicious Git configurations before trusting your projects again.&lt;/p&gt;
&lt;p&gt;And treat your local developer environment like part of your production security system.&lt;/p&gt;
&lt;p&gt;Because it is.&lt;/p&gt;
&lt;p&gt;Happy coding, and secure your tools before someone else uses them against you.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20110/why-github-2fa-wont-save-you-from-infostealers</guid>
<pubDate>Tue, 09 Jun 2026 22:26:57 +0000</pubDate>
</item>
<item>
<title>Git: Best Practices for Professionals</title>
<link>https://coderlegion.com/19645/git-best-practices-for-professionals</link>
<description>&lt;p&gt;Developers working in fast-moving teams often deal with overlapping work, long-running features, and reviews that depend on clear and reliable history.&lt;/p&gt;
&lt;p&gt;This guide focuses on common Git problems developers face when working in teams and highlights the techniques that keep workflows stable, predictable, and easy to maintain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/1*mx0EI8cn7OdmZ3smVJln8g.jpeg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;handling-merge-conflicts-the-right-way&quot;&gt;Handling Merge Conflicts the Right Way&lt;/h3&gt;
&lt;p&gt;You created a Pull Request (PR) and when it’s time to merge it to &lt;code&gt;main&lt;/code&gt; you are hit with a Merge Conflict on the UI. Here’s how to handle the conflict efficiently and seamlessly.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Resolving the actual conflict will always be a manual task at the code level.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;1-check-your-status-when-on-your-feature-branch&quot;&gt;1. Check your status when on your feature branch&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git status
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-checkout-to-the-main-branch-the-one-your-pr-needs-to-be-merged-to&quot;&gt;2. Checkout to the main branch — the one your PR needs to be merged to&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git checkout main
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-then-git-pull-to-get-latest-main-in-your-local&quot;&gt;3. Then git pull to get latest main in your local&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git pull
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;4-switch-back-to-the-feature-branch-the-pr-is-based-on&quot;&gt;4. Switch back to the feature branch the PR is based on&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git checkout feature-branch
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;5-execute-the-merge-command-&quot;&gt;5. Execute the Merge Command:&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git merge main    # (this tries to merge the main branch to the feature branch)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running the merge command you will get conflicts in certain files, these conflicts should replicate the original merge conflicts being faced in the Pull Request.&lt;/p&gt;
&lt;p&gt;Now the conflicts need to be merged manually and can incur code loss, which is fine if both the main and feature branches are pushed in origin.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git status               
git add resolve files    # add all the files with resolved code
git commit               # just git commit; without the -m flag
git push -u origin HEAD  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;git commit&lt;/code&gt; will auto create a Merge Commit so you don’t need to provide any custom message.&lt;/p&gt;
&lt;p&gt;After pushing to origin, the PR should not show any conflicts and the commit history in the feature branch should have a commit with an auto-merge message like this: &lt;code&gt;Merged PR 123: Updated the UI with new design&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;reverting-a-pushed-commit&quot;&gt;Reverting a Pushed Commit&lt;/h3&gt;
&lt;p&gt;Often at times we push and merge a commit to the main branch but then things go wrong and we end up introducing unexpected bugs into production code.&lt;/p&gt;
&lt;p&gt;This is where you can safely revert the bad commit until the issue is identified. Here is how to do it safely:&lt;/p&gt;
&lt;h4 id=&quot;1-update-your-local-repository&quot;&gt;1. Update your local repository&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git checkout main
git pull
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-view-the-recent-commits&quot;&gt;2. View the recent commits&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here find the bad commit and copy the commit hash, which should look something like this: c41975d1dae1cc69b16ad8892b8c77164e84ca39.&lt;/p&gt;
&lt;h4 id=&quot;3-revert-the-individual-commit&quot;&gt;3. Revert the individual commit&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git revert c41975d1dae1cc69b16ad8892b8c77164e84ca39
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git revert &amp;lt;commit-hash&amp;gt;&lt;/code&gt; automatically creates a commit message with a revert message:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Revert “Updated the UI with new design” This reverts commit c41975d1dae1cc69b16ad8892b8c77164e84ca39.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;4-verify-and-push-to-main&quot;&gt;4. Verify and Push to main&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git status
git push -u origin HEAD
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify using &lt;code&gt;git status&lt;/code&gt; that you are on the main branch and your local is a commit ahead of origin. &lt;code&gt;git push&lt;/code&gt; to push the reverted code changes to origin.&lt;/p&gt;
&lt;h3 id=&quot;using-stash-the-right-way&quot;&gt;Using stash the Right Way&lt;/h3&gt;
&lt;p&gt;Imagine you are working on a feature and have some useful changes on local but you have to pull the latest code from &lt;code&gt;main&lt;/code&gt; to work on top of, so you do a &lt;code&gt;git pull&lt;/code&gt; but end up getting the following message:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;error: Your local changes to the following files would be overwritten by merge:
    &amp;lt;file names&amp;gt;
Please commit your changes or stash them before you merge.
Aborting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here git already gives you a hint of what you need to do.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;git stash&lt;/code&gt; the right way:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git stash
git pull
git stash apply    # this reapplies the latest stashed changes in your local
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this might give you merge conflicts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This can be unsafe if you forget to reapply the changes immediately and lose track of the stashed code, so it is recommended for only small changes.&lt;/p&gt;
&lt;p&gt;A safer approach for larger changes would be to ensure you checkout to a new branch and push your changes to origin. Which allows you to freely pull the latest code and merge it to your working branch without losing your code.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this might also give you merge conflicts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;resetting-to-a-previous-commit&quot;&gt;Resetting to a Previous Commit&lt;/h3&gt;
&lt;p&gt;What if you are working locally and have a few bad commits which you want to get rid of.&lt;/p&gt;
&lt;p&gt;When can this happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You were resolving a merge conflict but end up with bad commits.&lt;/li&gt;
&lt;li&gt;You have some unwanted commits in your local which you do not want or even remember — can happen if you restart work on an old feature after a long time.&lt;/li&gt;
&lt;li&gt;Or you get the following message and git pull raises merge conflicts.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Your branch and 'origin/&amp;lt;branch&amp;gt;' have diverged,
and have 4 and 2 different commits each, respectively.
 (use &quot;git pull&quot; to merge the remote branch into yours)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are sure your local commits are unwanted or backed up, you can get rid of them by resetting your commit history by running the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git reset --hard HEAD~
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;CAUTION: This command destroys your local commits permanently!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This resets the local HEAD to one previous commit.&lt;/p&gt;
&lt;p&gt;If you have multiple local unwanted commits, you can run the command multiple times and sync cleanly using a &lt;code&gt;git pull&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git reset --hard HEAD~
git reset --hard HEAD~
git reset --hard HEAD~
...
git pull
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will reset the HEAD multiple times to a stable commit and then you can sync with the origin with no issues.&lt;/p&gt;
&lt;h3 id=&quot;cherry-picking&quot;&gt;Cherry Picking&lt;/h3&gt;
&lt;p&gt;Cherry-picking lets you take a specific commit from any branch and apply it onto another branch.&lt;/p&gt;
&lt;p&gt;It is a useful command which lets you pick up all changes in a commit from one branch to another, given that the 2 parent branches have similar history.&lt;/p&gt;
&lt;p&gt;Possible useful scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You fixed a bug on one branch e.g. “the dev branch”, and need the same fix to be applied on another branch e.g. “the release branch”.&lt;/li&gt;
&lt;li&gt;You accidentally committed changes to a wrong branch and want to move the commit to the correct branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is how to cherry-pick a commit safely from branch A to branch B:&lt;/p&gt;
&lt;h4 id=&quot;1-view-the-recent-commits-on-branch-a&quot;&gt;1. View the recent commits on branch A&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git checkout branch-a
git log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here find the commit to copy and copy its hash.&lt;/p&gt;
&lt;h4 id=&quot;2-switch-to-branch-b&quot;&gt;2. Switch to branch B&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git checkout branch-b
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-apply-the-individual-commit&quot;&gt;3. Apply the individual commit&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;git cherry-pick c41975d1dae1cc69b16ad8892b8c77164e84ca39
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a successful cherry pick you will get the following message:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[main abc123] Fix login bug
 Date: Tue Dec 1 12:00:00 2020 +0200
 2 files changed, 10 insertions(+), 3 deletions(-)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After which it should be safe to push to origin.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Hope this guide helps.&lt;/p&gt;
&lt;p&gt;Also, check out the guide &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/guroosh/git-best-practices-for-beginners-1h3c&quot;&gt;Best Git Practices for Beginners&lt;/a&gt; if you haven’t already.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19645/git-best-practices-for-professionals</guid>
<pubDate>Thu, 04 Jun 2026 07:29:58 +0000</pubDate>
</item>
<item>
<title>Git and GitHub concepts</title>
<link>https://coderlegion.com/19437/git-and-github-concepts</link>
<description>&lt;h3 id=&quot;introduction-2&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;I will be breaking down git and GitHub from beginner basics to a more advanced level in this article.I hope this structured approach will help you build a solid foundation moving forward.&lt;/p&gt;
&lt;h3 id=&quot;what-is-git&quot;&gt;what is Git&lt;/h3&gt;
&lt;p&gt;Git is a distributed version control system created by Linus Torvalds in 2005.It is designed to handle everything from small to very large projects with speed and efficiency.Unlike centralized version control systems,Git allows every user to have a complete copy of the repository,including its history on their local machine.This means you can work offline and still have access to the full project history.&lt;/p&gt;
&lt;p&gt;version control is a system that records changes to files over time so that you can recall specific versions later.&lt;br&gt;
It's essential for collaborative work,allowing multiple people to work on the same project without conflicts.&lt;/p&gt;
&lt;h3 id=&quot;instillation&quot;&gt;Instillation&lt;/h3&gt;
&lt;p&gt;To start using Git,you need to install it on your computer.You can download Git from the official website git-scm.com. Installation is straightforward and available for various operating systems,including Windows,macOS and Linux.&lt;/p&gt;
&lt;h3 id=&quot;basic-configuration-letting-git-know-you-&quot;&gt;Basic configuration(letting git know you)&lt;/h3&gt;
&lt;p&gt;Once Git is installed,you should configure it with your username and email.This information will be associated with your commits and help others identify who made specific changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hrbioysr4425kaeh9gi8.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
You can fill in you name and gmail using a syntax:&lt;br&gt;
git config --global user.name &quot;Your Name&quot;&lt;br&gt;
git config --global user.email &quot;your_email@example.com&quot;&lt;/p&gt;
&lt;h3 id=&quot;creating-your-first-repository&quot;&gt;Creating your first repository&lt;/h3&gt;
&lt;p&gt;To create a new Git repository,navigate to your project directory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8822x3w3vd5o9wftz9nv.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
This command initializes a new Git repository,creating a hidden directory that stores all the version control information.&lt;/p&gt;
&lt;h3 id=&quot;basic-git-commands&quot;&gt;Basic git commands&lt;/h3&gt;
&lt;h4 id=&quot;1-checking-the-status&quot;&gt;1.Checking the status&lt;/h4&gt;
&lt;p&gt;To see the current status of your repository including staged and unstaged changes,use:&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/md42m77fm9xjew59wsuo.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
This command provides a snapshot of your working directory and staging area helping you understand what changes are ready to be committed.&lt;/p&gt;
&lt;h4 id=&quot;2-adding-changes&quot;&gt;2.Adding changes&lt;/h4&gt;
&lt;p&gt;Before you can commit changes,you need to stage them.You can stage individual files or all changes at once.Below is the basic syntax you can follow.You can add any file you want from your file explorer&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gw64mbwrnhc0mkjgfjoe.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;3-committing-changes&quot;&gt;3.committing changes&lt;/h4&gt;
&lt;p&gt;Once your changes are staged,you can commit them to the repository.Each commit should have a descriptive message explaining what changes were made&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bd636s3uehm8am6kkwk.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;4-branching-and-merging&quot;&gt;4.Branching and Merging&lt;/h4&gt;
&lt;p&gt;Git encourages the use of branches,allowing you to work on new features or fixes in isolation without affecting the main codebase.Once your work is complete,you can merge it back into the main branch.&lt;/p&gt;
&lt;h5 id=&quot;a-creating-a-branch&quot;&gt;a.Creating a branch&lt;/h5&gt;
&lt;p&gt;Branches allow you to work on different features or fixes without affecting the main codebase.To create a new branch,use the git branch and write the branch name&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vwcx7kqms8adfm8t2cfy.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h5 id=&quot;b-switching-branches&quot;&gt;b.Switching branches&lt;/h5&gt;
&lt;p&gt;Switching to a new branch you use the git checkout.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/99l7j254d5z4eopdcocl.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;5-merging-branches&quot;&gt;5.Merging branches&lt;/h4&gt;
&lt;p&gt;Once you’ve completed work on a branch,you can merge it back into the main branch (often called main or master)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sncyk1o8j3vaevg0lmec.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
If there are conflicts (changes that cannot be automatically merged),Git will prompt you to resolve them manually.&lt;/p&gt;
&lt;h3 id=&quot;remote-repositories-with-github&quot;&gt;Remote Repositories with GitHub&lt;/h3&gt;
&lt;h4 id=&quot;what-is-github-&quot;&gt;what is GitHub?&lt;/h4&gt;
&lt;p&gt;GitHub is a web-based platform that uses Git for version control. It provides a user-friendly interface for managing Git repositories and facilitates collaboration among developers. GitHub allows you to host your code online,making it accessible to others and enabling collaborative work.&lt;/p&gt;
&lt;h4 id=&quot;creating-a-github-account&quot;&gt;Creating a GitHub account&lt;/h4&gt;
&lt;p&gt;To use GitHub,you need to create an account at gitHub.com.Once registered,you can create repositories,contribute to others' projects and manage your own code.&lt;/p&gt;
&lt;h4 id=&quot;connecting-your-local-repository-to-github&quot;&gt;Connecting Your Local Repository to GitHub&lt;/h4&gt;
&lt;p&gt;To push your local repository to GitHub,you first need to create a new repository on GitHub.After that,link your local repository to the remote one&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mk3iy2pb0mlss9pap8yt.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
This command sets up a connection to the remote repository,allowing you to push and pull changes.&lt;/p&gt;
&lt;h3 id=&quot;pushing-and-pulling-changes&quot;&gt;Pushing and Pulling changes&lt;/h3&gt;
&lt;h4 id=&quot;1-pushing-changes&quot;&gt;1.pushing changes&lt;/h4&gt;
&lt;p&gt;To upload your local commits to GitHub,use the git push and writing the branch name as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b0y2na94i3m4zuhtry5b.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
This command sends your changes to the remote repository and making them available to others.&lt;/p&gt;
&lt;h4 id=&quot;2-pulling-changes&quot;&gt;2.Pulling changes&lt;/h4&gt;
&lt;p&gt;To fetch and merge changes from the remote repository into your local branch,use the git pull as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/81iw133fnidmfobfpquz.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
This command ensures your local repository is up to date with the latest changes made by others.&lt;/p&gt;
&lt;h3 id=&quot;advanced-git-concepts&quot;&gt;Advanced Git concepts&lt;/h3&gt;
&lt;h4 id=&quot;1-rebasing&quot;&gt;1.Rebasing&lt;/h4&gt;
&lt;p&gt;Rebasing is a powerful feature that allows you to integrate changes from one branch into another.It rewrites the commit history and making it linear and cleaner.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wefwnzlg57b14yq7yatr.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;2-stashing&quot;&gt;2.Stashing&lt;/h4&gt;
&lt;p&gt;If you need to switch branches but aren’t ready to commit your changes,you can stash them temporarily.&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ni7ugkhtj8kx6uakfcq.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
You can also apply the stashed changes back to your working directory by adding apply command:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmv8meqxw0tmzx2dtkg8.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;resolving-merge-conflicts&quot;&gt;Resolving merge conflicts&lt;/h3&gt;
&lt;p&gt;When merging branches,conflicts may arise if changes overlap.Git will mark these conflicts in the affected files.You’ll need to manually resolve them by editing the files then staging and committing the resolved changes.&lt;/p&gt;
&lt;h3 id=&quot;collaborating-on-github&quot;&gt;Collaborating on GitHub&lt;/h3&gt;
&lt;h4 id=&quot;1-forking-repositories&quot;&gt;1.Forking Repositories&lt;/h4&gt;
&lt;p&gt;Forking allows you to create a personal copy of someone else's repository.This is useful for making changes without affecting the original project.You can fork a repository directly from GitHub.&lt;/p&gt;
&lt;h4 id=&quot;2-pull-request&quot;&gt;2.Pull request&lt;/h4&gt;
&lt;p&gt;Once you’ve made changes in your forked repository,you can propose these changes to the original repository by creating a pull request.This allows the original repository owner to review your changes and merge them if they approve.&lt;/p&gt;
&lt;h3 id=&quot;best-practices-for-using-git-and-github&quot;&gt;Best practices for using Git and GitHub&lt;/h3&gt;
&lt;p&gt;1.Commit Often:Make small,frequent commits with clear messages.This makes it easier to track changes and understand the project history.&lt;/p&gt;
&lt;p&gt;2.Use Branches:Keep your main branch clean by using branches for new features or bug fixes.This helps isolate changes and reduces the risk of introducing bugs.&lt;/p&gt;
&lt;p&gt;3.Write Good Commit Messages:Clearly describe what changes were made and why.Good commit messages help others (and your future self) understand the project’s history.&lt;/p&gt;
&lt;h3 id=&quot;online-courses&quot;&gt;Online courses&lt;/h3&gt;
&lt;p&gt;Platforms like Coursera,Udemy and freeCodeCamp offer comprehensive courses on Git and GitHub.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-4&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;By following this detailed guide,you can build a strong foundation in Git and GitHub,progressing from a beginner to an expert level.Start with the basics,practice regularly and explore advanced features as you become more comfortable.This knowledge will not only enhance your coding skills but also improve your ability to collaborate effectively in team environments.I hope you found it useful.Ciao.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19437/git-and-github-concepts</guid>
<pubDate>Mon, 01 Jun 2026 17:08:20 +0000</pubDate>
</item>
<item>
<title>How Diff Algorithms Work: Myers Algorithm Explained Simply</title>
<link>https://coderlegion.com/18153/how-diff-algorithms-work-myers-algorithm-explained-simply</link>
<description>&lt;h3 id=&quot;introduction-the-hidden-engine-behind-code-collaboration&quot;&gt;Introduction: The Hidden Engine Behind Code Collaboration&lt;/h3&gt;
&lt;p&gt;Every time you execute &lt;code&gt;git diff&lt;/code&gt; or review a pull request on GitHub, a clever algorithm runs silently in the background. It calculates the exact, minimum sequence of additions and deletions needed to change your old file into your new file.&lt;/p&gt;
&lt;p&gt;A bad diff algorithm would tell you that you deleted an entire function and rewrote it, just because you added a single space. A good algorithm, however, understands the logical way humans write code.&lt;/p&gt;
&lt;p&gt;In this guide, we will explore the &lt;strong&gt;Myers Diff Algorithm&lt;/strong&gt;—the standard tool behind Git—in plain English.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;section-1-the-core-problem---edit-distance-&quot;&gt;Section 1: The Core Problem - &quot;Edit Distance&quot;&lt;/h3&gt;
&lt;p&gt;At its heart, comparing text is about finding the &quot;edit distance.&quot; Imagine you have two versions of a document. Your goal is to find the absolute shortest list of edits (inserting or deleting lines) that turns Version A into Version B.&lt;/p&gt;
&lt;p&gt;If a computer simply guessed every possible combination of edits, it would take centuries to compare two large code files. We need a smarter shortcut.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;section-2-the-magic-shortcut-longest-common-subsequence-&quot;&gt;Section 2: The Magic Shortcut (Longest Common Subsequence)&lt;/h3&gt;
&lt;p&gt;The best shortcut is finding what &lt;em&gt;hasn't&lt;/em&gt; changed. This is called the &lt;strong&gt;Longest Common Subsequence&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If we can find the longest list of lines that exist in &lt;em&gt;both&lt;/em&gt; files in the exact same order, then everything else is just a simple addition or deletion.&lt;/p&gt;
&lt;h4 id=&quot;a-simple-example&quot;&gt;A Simple Example&lt;/h4&gt;
&lt;p&gt;Imagine two short grocery lists:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;List A (Old):&lt;/strong&gt; &lt;code&gt;[&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;, &quot;date&quot;]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;List B (New):&lt;/strong&gt; &lt;code&gt;[&quot;apple&quot;, &quot;blueberry&quot;, &quot;cherry&quot;, &quot;elderberry&quot;]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The longest sequence they share is: &lt;code&gt;[&quot;apple&quot;, &quot;cherry&quot;]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because we know these lines match perfectly, we easily know what happened to the rest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&quot;banana&quot;&lt;/code&gt; is only in List A -&amp;gt; &lt;strong&gt;Deleted&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;blueberry&quot;&lt;/code&gt; is only in List B -&amp;gt; &lt;strong&gt;Inserted&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;date&quot;&lt;/code&gt; is only in List A -&amp;gt; &lt;strong&gt;Deleted&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;elderberry&quot;&lt;/code&gt; is only in List B -&amp;gt; &lt;strong&gt;Inserted&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;section-3-the-genius-of-the-myers-algorithm&quot;&gt;Section 3: The Genius of the Myers Algorithm&lt;/h3&gt;
&lt;p&gt;Finding that shared sequence can still be slow. In 1986, computer scientist Eugene Myers invented a brilliant way to speed this up. Instead of comparing text line-by-line in a massive, slow table, he turned the problem into a game of finding the shortest path on a map.&lt;/p&gt;
&lt;h4 id=&quot;the-grid-map-system&quot;&gt;The Grid Map System&lt;/h4&gt;
&lt;p&gt;Imagine a grid where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moving &lt;strong&gt;Right&lt;/strong&gt; means adding a line from the new file.&lt;/li&gt;
&lt;li&gt;Moving &lt;strong&gt;Down&lt;/strong&gt; means deleting a line from the old file.&lt;/li&gt;
&lt;li&gt;Moving &lt;strong&gt;Diagonally&lt;/strong&gt; means keeping a matching line.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &quot;cost&quot; of moving diagonally is zero—it's a free move because the lines match perfectly! The Myers algorithm simply looks for the fastest path from the top-left of the map to the bottom-right, taking as many &quot;free&quot; diagonal moves as possible.&lt;/p&gt;
&lt;p&gt;Because it prioritizes these free moves, it skips doing unnecessary calculations and runs almost instantly, even on massive project files.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;section-4-why-git-uses-myers-and-when-it-struggles-&quot;&gt;Section 4: Why Git Uses Myers (and When It Struggles)&lt;/h3&gt;
&lt;p&gt;The Myers algorithm is incredibly fast, which is why Git uses it by default. However, because it is purely mathematical, it doesn't understand programming languages. &lt;/p&gt;
&lt;p&gt;Sometimes, it gets confused by closing brackets &lt;code&gt;}&lt;/code&gt; and matches them incorrectly, creating a messy view. If you ever see a weird diff full of brackets, you can tell Git to use a slightly different approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;git diff --diff-algorithm=patience&lt;/code&gt;&lt;/strong&gt; : This forces Git to focus on unique lines (like function names) rather than common symbols (like brackets), making the output much easier for humans to read when code has been heavily moved around.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion-5&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The efficiency of our daily code reviews relies on the elegant logic of the Myers Diff Algorithm. By turning a massive text comparison problem into a simple path-finding game, it allows developers to collaborate quickly and seamlessly without ever noticing the complex math happening behind the scenes.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18153/how-diff-algorithms-work-myers-algorithm-explained-simply</guid>
<pubDate>Sun, 17 May 2026 19:23:18 +0000</pubDate>
</item>
<item>
<title>Forking CoverLetterGPT: What I Learned Rebuilding an AI SaaS from an Open-Source Starter</title>
<link>https://coderlegion.com/18152/forking-coverlettergpt-what-i-learned-rebuilding-an-ai-saas-from-an-open-source-starter</link>
<description>&lt;p&gt;One of the better ways to learn a stack is to take a working app and rebuild it your way. I forked CoverLetterGPT — an open-source AI cover letter generator originally built by Vince Loewe as a flagship example app for the Wasp framework — and used it as a launchpad to learn full-stack AI app development. This post is about what I changed, what surprised me, and what I'd do differently.&lt;br&gt;
Credit where it's due: original project at &lt;a rel=&quot;nofollow&quot; href=&quot;https://coverlettergpt.xyz&quot;&gt;https://coverlettergpt.xyz&lt;/a&gt;, source at github.com/vincanger/coverlettergpt. Everything below is about my fork and what I learned from the exercise.&lt;br&gt;
Why fork instead of building from scratch?&lt;br&gt;
Greenfield projects teach you architecture decisions. Forks teach you about integration. CoverLetterGPT has:&lt;/p&gt;
&lt;p&gt;OpenAI integration for the actual letter generation&lt;br&gt;
Stripe for paid tiers&lt;br&gt;
Google OAuth for sign-in&lt;br&gt;
Prisma + Postgres for persistence&lt;br&gt;
React + Chakra UI on the frontend&lt;br&gt;
Wasp wiring all of it together&lt;/p&gt;
&lt;p&gt;Forking gave me a working baseline for every one of those pieces. The learning came from changing one thing and watching what broke.&lt;br&gt;
What Wasp actually does (and why it surprised me)&lt;br&gt;
Wasp is a config-as-code framework for full-stack apps. You declare your entities, routes, auth methods, and jobs in a .wasp file, and the framework generates the boilerplate around your React + Node code. Coming from Next.js, it felt strange — until I realized how much code it removed.&lt;br&gt;
A rough comparison:&lt;br&gt;
ConcernNext.jsWaspAuthWire up NextAuth/Auth.js, configure providers, write callbacksDeclare auth: { ... googleAuth } in the wasp fileDB queriesWrite Prisma queries + tRPC/route handlersDeclare a query or action, write the functionBackground jobsWire up BullMQ / InngestDeclare a job, write the functionEmailNodemailer + your own abstractionemailSender: { provider: SendGrid }&lt;br&gt;
It's not magic — it's just opinionated code generation. But it makes the integration work fast, which is exactly what you want when you're learning.&lt;br&gt;
What I changed in my fork&lt;br&gt;
Things I actually built on top of the original:&lt;/p&gt;
&lt;p&gt;Prompt rework. The original prompt is good but generic. I rebuilt the prompt to extract structured fields from the job posting (role, seniority, top 3 required skills, tone hints) before generating the letter, so the output references specifics instead of platitudes.&lt;br&gt;
Streaming responses. The original waits for the full completion. I added server-sent events so the letter streams token-by-token. Better UX, and it taught me how to plumb a stream through Wasp's API actions.&lt;br&gt;
Versioning. Each generation saves as a versioned draft, so users can compare &quot;tone: confident&quot; vs &quot;tone: humble&quot; side by side instead of regenerating blindly.&lt;br&gt;
Cost guardrails. Per-user daily token cap, falling back to a cheaper model after the cap. Critical if you ever plan to actually expose an OpenAI app to the internet.&lt;br&gt;
Auth providers. Added GitHub OAuth alongside Google — useful for the developer-leaning audience I had in mind.&lt;/p&gt;
&lt;p&gt;Things that broke (and what they taught me)&lt;/p&gt;
&lt;p&gt;Stripe webhooks in local dev — I'd never wired Stripe end-to-end before. stripe listen --forward-to and idempotent webhook handlers are the lessons.&lt;br&gt;
Prisma migrations on a forked schema — changing the schema after the upstream had already migrated meant I had to be careful with migration history. Lesson: branch the migration history early.&lt;br&gt;
OpenAI rate limits under streaming — streaming responses count differently against limits; I had to add backoff that handles partial completions.&lt;br&gt;
Wasp's escape hatches — Wasp is opinionated, but there are places (custom middleware, raw routes) where you drop into Express. Knowing when to fight the framework vs use the escape hatch is the actual skill.&lt;/p&gt;
&lt;p&gt;Would I use Wasp for a production project?&lt;br&gt;
Honestly, for a small-to-medium SaaS with standard requirements (auth, DB, payments, jobs, email) — yes. The boilerplate it removes is exactly the boilerplate I find tedious in Next.js + tRPC + NextAuth + BullMQ + Resend stacks. For anything with unusual architecture (heavy real-time, custom infra, microservices) — probably not.&lt;br&gt;
The honest answer is: forking CoverLetterGPT taught me Wasp faster than any tutorial would have, because I had skin in the game. If you're looking for a way to learn a new stack, find a working open-source app in it and break things.&lt;/p&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://coverlettergpt.xyz&quot;&gt;https://coverlettergpt.xyz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Happy to answer questions about the fork, the framework, or any of the integrations in the comments.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18152/forking-coverlettergpt-what-i-learned-rebuilding-an-ai-saas-from-an-open-source-starter</guid>
<pubDate>Sun, 17 May 2026 11:53:32 +0000</pubDate>
</item>
<item>
<title>git-sfs: Large File Storage Without the LFS Server</title>
<link>https://coderlegion.com/17734/git-sfs-large-file-storage-without-the-lfs-server</link>
<description>&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;You added a 2 GB dataset to a repo. Now &lt;code&gt;git clone&lt;/code&gt; takes 10 minutes, CI downloads the full history on every run, and GitHub is billing you per GB transferred. You switch to Git LFS - and now you need a server, a token, and a storage plan. You try DVC - and now you need Python, a pipeline config, and lock files that conflict on every PR.&lt;/p&gt;
&lt;p&gt;None of this is the actual problem. The actual problem is: large files don't belong in Git objects. Everything else is overhead.&lt;/p&gt;
&lt;h3 id=&quot;meet-git-sfs&quot;&gt;Meet git-sfs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;SFS&lt;/strong&gt; stands for &lt;strong&gt;Symbolic File Storage&lt;/strong&gt;. The name is deliberate - it's Git LFS with the &lt;code&gt;L&lt;/code&gt; swapped for &lt;code&gt;S&lt;/code&gt;. Git LFS replaces large files with opaque pointer files and routes bytes through a proprietary server protocol. &lt;code&gt;git-sfs&lt;/code&gt; replaces large files with plain symlinks that Git already understands natively, and routes bytes through &lt;code&gt;rclone&lt;/code&gt; to any remote you already have.&lt;/p&gt;
&lt;p&gt;No new protocol. No server. No pointer file format to decode. Just symlinks.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h3&gt;
&lt;p&gt;The model is three sentences:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Git tracks symlinks.
git-sfs stores file bytes.
rclone moves files.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you run &lt;code&gt;git-sfs add data/train-000.tar.zst&lt;/code&gt;, here's what happens:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The file is hashed with SHA-256&lt;/li&gt;
&lt;li&gt;Bytes are stored in &lt;code&gt;&amp;lt;cache&amp;gt;/files/sha256/ab/&amp;lt;hash&amp;gt;&lt;/code&gt; - write-once, read-only&lt;/li&gt;
&lt;li&gt;The original path becomes a relative Git symlink pointing into the cache&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;data/train-000.tar.zst -&amp;gt; ../.git-sfs/cache/files/sha256/ab/&amp;lt;hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;data/train-000.tar.zst&lt;/code&gt; opens normally. Git commits a 70-byte symlink. Your repo stays fast.&lt;/p&gt;
&lt;h3 id=&quot;why-not-git-lfs-&quot;&gt;Why Not Git LFS?&lt;/h3&gt;
&lt;p&gt;Git LFS solves the storage problem but adds a &lt;em&gt;server&lt;/em&gt; problem. You need an LFS endpoint, pay per-GB transfer fees on GitHub, and the pointer-file format is an opaque internal detail.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git-sfs&lt;/code&gt; remotes are plain &lt;code&gt;rclone&lt;/code&gt; destinations - S3, GCS, Azure Blob, Backblaze B2, SFTP, a local path, anything &lt;code&gt;rclone&lt;/code&gt; supports. You can &lt;code&gt;rclone ls&lt;/code&gt; your remote and see exactly what's there. No magic.&lt;/p&gt;
&lt;h3 id=&quot;one-binary-no-runtime&quot;&gt;One Binary, No Runtime&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git-sfs&lt;/code&gt; is written in Go. It ships as a single static binary - no Python environment, no runtime, no version conflicts. Drop it on any machine and it runs.&lt;/p&gt;
&lt;h3 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;# Install (macOS/Linux, arm64 and x86_64)
curl -LsSf https://github.com/Red-Eyed/git-sfs/releases/latest/download/install.sh | sh

# Init and configure
git-sfs init        # creates .git-sfs/config.toml
# edit config.toml: set remote backend, path, rclone config
git-sfs setup       # bind local cache

# Add files
git-sfs add data/
git add .git-sfs/config.toml data/
git commit -m &quot;track datasets&quot;

# Sync to remote
git-sfs push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On another machine:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git clone &amp;lt;repo&amp;gt; &amp;amp;&amp;amp; cd &amp;lt;repo&amp;gt;
git-sfs setup
git-sfs pull        # download only what you need
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also pull a subset - useful when one machine only needs validation data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git-sfs pull data/validation/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;safety-by-design&quot;&gt;Safety by Design&lt;/h3&gt;
&lt;p&gt;Data loss in a dataset management tool is unacceptable, so &lt;code&gt;git-sfs&lt;/code&gt; has a few hard rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hash-verify at every boundary&lt;/strong&gt; - after hashing, after download, after copy. A corrupted file is rejected, not silently accepted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atomic writes&lt;/strong&gt; - temp file + rename everywhere. An interrupted push or pull never leaves a partial file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache files are immutable&lt;/strong&gt; - write-once, then write-protected. Accidental overwrites are impossible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;verify&lt;/code&gt; command is designed for CI:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git-sfs verify              # presence check (fast)
git-sfs verify --with-integrity data/  # rehash cached + remote files (thorough)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;doctor&lt;/code&gt; checks your entire setup - config, cache, rclone binary, remote connectivity - in one shot:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git-sfs doctor
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;what-s-different-from-dvc-git-annex-etc-&quot;&gt;What's Different from dvc, git-annex, etc.?&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Tool &lt;/th&gt;&lt;th&gt;Requires &lt;/th&gt;&lt;th&gt;Remote &lt;/th&gt;&lt;th&gt;PR-friendly?&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Git LFS &lt;/td&gt;&lt;td&gt;LFS server &lt;/td&gt;&lt;td&gt;proprietary protocol &lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;DVC &lt;/td&gt;&lt;td&gt;Python, pipelines &lt;/td&gt;&lt;td&gt;S3/GCS/etc via SDK &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;git-annex &lt;/td&gt;&lt;td&gt;Haskell runtime &lt;/td&gt;&lt;td&gt;many backends &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;git-sfs&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;rclone &lt;/td&gt;&lt;td&gt;anything rclone supports &lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;DVC&lt;/strong&gt; stores large file metadata in &lt;code&gt;.dvc&lt;/code&gt; sidecar files and a &lt;code&gt;dvc.lock&lt;/code&gt; that encode pipeline state. When two branches touch the same dataset, merging those lock files creates conflicts that have no meaningful resolution in a pull request - reviewers see &lt;code&gt;.dvc&lt;/code&gt; diffs, not data diffs, and the merge problem is fundamentally DVC's, not Git's.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;git-annex&lt;/strong&gt; stores its metadata in a separate orphan branch (&lt;code&gt;git-annex&lt;/code&gt;). That branch never appears in a normal &lt;code&gt;git log&lt;/code&gt; or PR diff, so reviewers have no visibility into what large files changed or why. Merging the annex branch is a separate out-of-band operation that GitHub PRs don't surface at all.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git-sfs&lt;/code&gt; tracks only plain relative symlinks. A PR diff shows exactly which files were added, removed, or renamed - the same way any other file change looks in Git. Reviewers can approve or reject dataset changes with full context, and there are no sidecar files or hidden branches to reconcile.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git-sfs&lt;/code&gt; has no pipelines, no Python, no manifests, no committed lock files. The Git tree is the file list. The cache is a plain directory. The remote is whatever &lt;code&gt;rclone&lt;/code&gt; can reach.&lt;/p&gt;
&lt;h3 id=&quot;concurrency-and-partial-pulls&quot;&gt;Concurrency and Partial Pulls&lt;/h3&gt;
&lt;p&gt;Large dataset workflows often mean hundreds of files if not millions. &lt;code&gt;git-sfs&lt;/code&gt; runs add, push, and pull with a configurable worker pool:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;# .git-sfs/config.toml
[settings]
n_jobs = 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And partial pulls let teams share a repo where different machines only materialize what they actually use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;git-sfs pull data/train/         # only training split
git-sfs pull data/checkpoints/   # only model weights
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;source&quot;&gt;Source&lt;/h3&gt;
&lt;p&gt;The project is open source under MIT: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Red-Eyed/git-sfs&quot;&gt;github.com/Red-Eyed/git-sfs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feedback and issues welcome. If you're managing large files in Git and the LFS server tax is getting old, give it a try.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17734/git-sfs-large-file-storage-without-the-lfs-server</guid>
<pubDate>Wed, 13 May 2026 16:37:11 +0000</pubDate>
</item>
<item>
<title>git-lrc and the Rise of AI Code Review Anxiety git-lrc and the Rise of AI Code Review Anxiety</title>
<link>https://coderlegion.com/17674/git-lrc-and-the-rise-of-ai-code-review-anxiety-git-lrc-and-the-rise-of-ai-code-review-anxiety</link>
<description>&lt;h2 id=&quot;git-lrc-and-the-rise-of-ai-code-review-anxiety&quot;&gt;git-lrc and the Rise of AI Code Review Anxiety&lt;/h2&gt;
&lt;p&gt;AI coding tools have fundamentally changed software development.&lt;/p&gt;
&lt;p&gt;Developers can now generate entire functions, APIs, and architectures in minutes using tools like Copilot, Cursor, Claude Code, and GPT-powered agents. Software velocity has accelerated dramatically. But alongside this acceleration, a quieter problem has emerged:&lt;/p&gt;
&lt;p&gt;Developers are increasingly shipping code they did not fully read, verify, or understand.&lt;/p&gt;
&lt;p&gt;That is the exact engineering tension that git-lrc attempts to solve.&lt;/p&gt;
&lt;h3 id=&quot;what-is-git-lrc-&quot;&gt;What is git-lrc?&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://hexmos.com/livereview/git-lrc/?utm_source=chatgpt.com&quot;&gt;git-lrc&lt;/a&gt; is an AI-powered pre-commit review system built by &lt;a rel=&quot;nofollow&quot; href=&quot;https://hexmos.com?utm_source=chatgpt.com&quot;&gt;Hexmos&lt;/a&gt;. Instead of reviewing code only after a pull request is opened, git-lrc inserts an AI review layer directly into the Git workflow before commits are finalized.&lt;/p&gt;
&lt;p&gt;In simple terms, it acts like a verification checkpoint between AI-generated code and permanent repository history.&lt;/p&gt;
&lt;p&gt;The workflow is intentionally minimal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git add .
git lrc review
git commit -m &quot;message&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than building another large dashboard-based review platform, git-lrc stays close to existing developer behavior. It integrates directly into Git, which makes adoption feel lightweight instead of disruptive.&lt;/p&gt;
&lt;p&gt;That design decision is important.&lt;/p&gt;
&lt;p&gt;Most developers do not want another complex AI platform. They want small tools that fit naturally into workflows they already trust.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-real-problem-is-not-ai-code-generation&quot;&gt;The Real Problem Is Not AI Code Generation&lt;/h2&gt;
&lt;p&gt;The industry often frames AI-assisted development around productivity.&lt;/p&gt;
&lt;p&gt;“How much faster can developers write code?”&lt;/p&gt;
&lt;p&gt;But that framing misses the deeper issue.&lt;/p&gt;
&lt;p&gt;The bottleneck is no longer code generation.&lt;/p&gt;
&lt;p&gt;The bottleneck is comprehension.&lt;/p&gt;
&lt;p&gt;Modern AI tools can generate code faster than humans can carefully inspect it. This creates a dangerous asymmetry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;code output increases,&lt;/li&gt;
&lt;li&gt;understanding decreases,&lt;/li&gt;
&lt;li&gt;and review quality slowly collapses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Developers are beginning to experience what could be called &lt;em&gt;review debt&lt;/em&gt; — a growing accumulation of code that nobody deeply examined.&lt;/p&gt;
&lt;p&gt;This is where git-lrc becomes interesting.&lt;/p&gt;
&lt;p&gt;The tool is not primarily about generating more software. It is about slowing developers down at the right moment.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-pre-commit-review-matters&quot;&gt;Why Pre-Commit Review Matters&lt;/h2&gt;
&lt;p&gt;Traditional review systems usually operate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;during pull requests,&lt;/li&gt;
&lt;li&gt;inside CI pipelines,&lt;/li&gt;
&lt;li&gt;or after code has already entered repository history.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;git-lrc changes the timing.&lt;/p&gt;
&lt;p&gt;It reviews staged diffs before commit.&lt;/p&gt;
&lt;p&gt;That small workflow shift has large implications.&lt;/p&gt;
&lt;p&gt;When reviews happen before commit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;context is still fresh,&lt;/li&gt;
&lt;li&gt;developers still remember intent,&lt;/li&gt;
&lt;li&gt;mistakes are easier to correct,&lt;/li&gt;
&lt;li&gt;and risky code is prevented from spreading downstream.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This “review-before-history” model feels increasingly relevant in the age of AI-assisted engineering.&lt;/p&gt;
&lt;p&gt;Because once AI-generated code enters a repository, it rapidly becomes normalized. Future developers inherit it, build on top of it, and often trust it simply because it already exists.&lt;/p&gt;
&lt;p&gt;Preventing low-confidence code from entering history may become one of the defining engineering disciplines of the AI era.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;features-that-stand-out&quot;&gt;Features That Stand Out&lt;/h2&gt;
&lt;h3 id=&quot;1-ai-powered-diff-reviews&quot;&gt;1. AI-Powered Diff Reviews&lt;/h3&gt;
&lt;p&gt;git-lrc reviews staged changes before commit and attempts to identify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;logic regressions,&lt;/li&gt;
&lt;li&gt;suspicious behavior,&lt;/li&gt;
&lt;li&gt;risky cloud operations,&lt;/li&gt;
&lt;li&gt;accidental credential leaks,&lt;/li&gt;
&lt;li&gt;and other potentially dangerous modifications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not perfect detection.&lt;/p&gt;
&lt;p&gt;The goal is introducing friction before risky code becomes permanent.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-git-native-design&quot;&gt;2. Git-Native Design&lt;/h3&gt;
&lt;p&gt;One of the strongest aspects of the tool is its simplicity.&lt;/p&gt;
&lt;p&gt;The workflow remains extremely close to standard Git usage. Developers are not forced into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;external dashboards,&lt;/li&gt;
&lt;li&gt;complicated configuration layers,&lt;/li&gt;
&lt;li&gt;or enterprise-heavy review systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That minimalism matters because developer tools succeed when they reduce cognitive overhead instead of increasing it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-accountability-over-blind-automation&quot;&gt;3. Accountability Over Blind Automation&lt;/h3&gt;
&lt;p&gt;An interesting aspect of git-lrc’s philosophy is that developers can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;approve,&lt;/li&gt;
&lt;li&gt;skip,&lt;/li&gt;
&lt;li&gt;or manually vouch for code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But those actions remain visible.&lt;/p&gt;
&lt;p&gt;This creates an engineering culture centered around accountability rather than passive trust in AI systems.&lt;/p&gt;
&lt;p&gt;In a world increasingly obsessed with autonomous agents, this design philosophy feels unusually grounded.&lt;/p&gt;
&lt;p&gt;git-lrc is essentially arguing that:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;humans still need to understand what they deploy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That sounds obvious, but modern AI tooling trends often move in the opposite direction.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-emerging-fear-beneath-ai-development&quot;&gt;The Emerging Fear Beneath AI Development&lt;/h2&gt;
&lt;p&gt;There is a growing anxiety across software engineering that few people openly discuss.&lt;/p&gt;
&lt;p&gt;Developers increasingly wonder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How much of modern codebases are truly understood?&lt;/li&gt;
&lt;li&gt;How many AI-generated abstractions are entering production unchecked?&lt;/li&gt;
&lt;li&gt;What happens when teams optimize entirely for velocity?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The danger is not necessarily malicious AI.&lt;/p&gt;
&lt;p&gt;The danger is gradual detachment between developers and the systems they maintain.&lt;/p&gt;
&lt;p&gt;Software engineering historically depended on comprehension:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reading code,&lt;/li&gt;
&lt;li&gt;understanding architecture,&lt;/li&gt;
&lt;li&gt;tracing logic,&lt;/li&gt;
&lt;li&gt;reasoning about systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI-assisted workflows risk weakening those habits if organizations prioritize output alone.&lt;/p&gt;
&lt;p&gt;git-lrc appears to emerge directly from this tension.&lt;/p&gt;
&lt;p&gt;It is less a productivity tool and more a defensive mechanism against cognitive drift inside modern engineering teams.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tradeoffs-and-limitations&quot;&gt;Tradeoffs and Limitations&lt;/h2&gt;
&lt;p&gt;Like any pre-commit system, git-lrc introduces tradeoffs.&lt;/p&gt;
&lt;p&gt;Potential downsides include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;additional review friction before commits,&lt;/li&gt;
&lt;li&gt;possible false positives,&lt;/li&gt;
&lt;li&gt;reliance on external AI APIs,&lt;/li&gt;
&lt;li&gt;and the temptation for developers to bypass the review process entirely.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The success of tools like this will depend on balancing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;review quality,&lt;/li&gt;
&lt;li&gt;developer trust,&lt;/li&gt;
&lt;li&gt;and workflow speed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Too much friction and developers disable the hook.&lt;br&gt;
Too little scrutiny and the review loses meaning.&lt;/p&gt;
&lt;p&gt;That balance will define the future of AI review tooling.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;a-shift-in-engineering-philosophy&quot;&gt;A Shift in Engineering Philosophy&lt;/h2&gt;
&lt;p&gt;The most interesting part of git-lrc may not be the software itself.&lt;/p&gt;
&lt;p&gt;It may be what the tool represents.&lt;/p&gt;
&lt;p&gt;For years, engineering culture optimized for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;automation,&lt;/li&gt;
&lt;li&gt;acceleration,&lt;/li&gt;
&lt;li&gt;and abstraction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now a countertrend is beginning to appear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;verification,&lt;/li&gt;
&lt;li&gt;traceability,&lt;/li&gt;
&lt;li&gt;and comprehension.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The industry is slowly realizing that generating software faster is not enough if developers no longer understand the systems they are deploying.&lt;/p&gt;
&lt;p&gt;git-lrc sits directly inside that transition.&lt;/p&gt;
&lt;p&gt;And that is why the tool feels important beyond its feature set.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-thoughts-1&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;The AI coding revolution is not just changing how software is written.&lt;/p&gt;
&lt;p&gt;It is changing the relationship developers have with understanding itself.&lt;/p&gt;
&lt;p&gt;Tools like git-lrc suggest that the next stage of AI-assisted engineering may not focus purely on generation.&lt;/p&gt;
&lt;p&gt;Instead, the future may revolve around a harder question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;How do we preserve human comprehension in an era of machine-accelerated software development?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That question extends far beyond a single Git tool.&lt;/p&gt;
&lt;p&gt;But git-lrc is one of the first products attempting to confront it directly.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17674/git-lrc-and-the-rise-of-ai-code-review-anxiety-git-lrc-and-the-rise-of-ai-code-review-anxiety</guid>
<pubDate>Wed, 13 May 2026 06:17:09 +0000</pubDate>
</item>
<item>
<title>Please commit your changes or stash them before you merge. aborting</title>
<link>https://coderlegion.com/17408/please-commit-your-changes-or-stash-them-before-you-merge-aborting</link>
<description>&lt;p&gt;&lt;strong&gt;&quot;&lt;span style=&quot;color: rgb(185, 106, 217);&quot;&gt;Please commit your changes or stash them before you merge. Aborting&lt;/span&gt;&quot; error occurs when trying to merge branches in Git and the current branch has uncommitted changes. To resolve this issue, either &lt;span style=&quot;color: rgb(185, 106, 217);&quot;&gt;commit the changes&lt;/span&gt; or &lt;span style=&quot;color: rgb(185, 106, 217);&quot;&gt;stash them using the Git stash command&lt;/span&gt;. This article will discuss the cause of the error and provide instructions on how to resolve it.&lt;/strong&gt;&lt;/p&gt;
&lt;div id=&quot;tc-div&quot;&gt;
&lt;h4&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;Table of Contents &lt;a id=&quot;tc-toogle&quot; class=&quot;open&quot;&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;div id=&quot;tc-list&quot;&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#what&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;What is the problem?&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#how&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;How to solve this problem?&lt;/span&gt;&lt;/a&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#how1&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Commit the changes&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#how2&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Stash the changes&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#con&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Conclusion&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#ref&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Reference&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;
&lt;section&gt;
&lt;a id=&quot;what&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;1. What is the problem?&lt;/strong&gt;&lt;/span&gt; &lt;a id=&quot;what_problem&quot; rel=&quot;nofollow&quot; href=&quot;#what&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt; The &quot;Please commit your changes or stash them before you merge. Aborting&quot; error's message appears when attempting to merge branches in Git and the current branch has uncommitted changes. &lt;/p&gt;
&lt;p&gt;In the below example, I've made a change in a file in feature-branch branch and trying to merge it into master branch without committing the change.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ git checkout feature-branch
$ echo &quot;new changes&quot; &amp;gt;&amp;gt; file.txt
$ git checkout master
$ git merge feature-branch
error: Your local changes to the following files would be overwritten by merge:
file.txt
Please, commit your changes or stash them before you merge.
Aborting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, you could reproduce the same error by following these steps:
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Checkout a branch where you have made changes to a file.&lt;/li&gt;
&lt;li&gt;Switch to another branch without committing or stashing the changes.&lt;/li&gt;
&lt;li&gt;Attempt to merge the first branch into the second branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This error can occur in various scenarios and may be encountered by developers while working with Git.&lt;/p&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;a id=&quot;how&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;2. How to solve this problem?&lt;/strong&gt;&lt;/span&gt; &lt;a id=&quot;how_solve&quot; rel=&quot;nofollow&quot; href=&quot;#why&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You must have realized how to do it by now. The message error already tell you what to do, committing the changes or stashing the changes. Let's me guide you to each solution step-by-step.&lt;/p&gt;
&lt;a id=&quot;how1&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;1. Commit the changes&lt;/strong&gt;&lt;/span&gt; &lt;/h3&gt;
&lt;p&gt;The simplest solution to this error is to commit the changes in the current branch before attempting to merge with another branch. This will ensure that the changes are saved and can be easily retrieved if needed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the status of the current branch to see the changes that have been made:&lt;/li&gt;
$ git status
&lt;li&gt;Add the changes to the stage:&lt;/li&gt;
$ git add [file_name]
&lt;li&gt;Commit the changes:&lt;/li&gt;
$ git commit -m &quot;Committing changes before merge&quot;
&lt;li&gt;Switch to the branch you want to merge into:&lt;/li&gt;
$ git checkout [branch_name]
&lt;li&gt;Merge the branch with the changes:&lt;/li&gt;
$ git merge [branch_with_changes]
&lt;/ul&gt;
&lt;a id=&quot;how2&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;2. Stash the changes&lt;/strong&gt;&lt;/span&gt; &lt;/h3&gt;
&lt;p&gt;Another solution to this error is to stash the changes in the current branch before attempting to merge with another branch. Stashing allows you to save the changes without committing them, so they can be retrieved later if needed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the status of the current branch to see the changes that have been made:&lt;/li&gt;
$ git status
&lt;li&gt;Stash the changes:&lt;/li&gt;
$ git stash
&lt;li&gt;Switch to the branch you want to merge into:&lt;/li&gt;
$ git checkout [branch_name]
&lt;li&gt;Merge the branch with the changes:&lt;/li&gt;
$ git merge [branch_with_changes]
&lt;li&gt;Retrieve the stashed changes:&lt;/li&gt;
$ git stash apply
&lt;/ul&gt;
&lt;div class=&quot;div-pink&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Caution&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;When stashing changes, it is important to remember that the stash is not permanent and may be lost if not reapplied or saved to a branch. It is recommended to regularly save stashes to a branch or reapply them to ensure the changes are not lost.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Both solutions ensure that the changes are saved and can be retrieved if needed, while also allowing the merge to proceed without interruption.&lt;/p&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;a id=&quot;con&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;3. Conclusion&lt;/strong&gt;&lt;/span&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#con&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this article, we've discussed the error &quot;&lt;span style=&quot;color: rgb(185, 106, 217);&quot;&gt;Please commit your changes or stash them before you merge. Aborting&lt;/span&gt;&quot; and its solutions of &lt;span style=&quot;color: rgb(185, 106, 217);&quot;&gt;committing&lt;/span&gt; or &lt;span style=&quot;color: rgb(185, 106, 217);&quot;&gt;stashing&lt;/span&gt; changes in the current branch before merging. Both solutions ensure changes are saved and retrievable while allowing the merge to proceed. If you have any questions or comments, feel free to leave them below. Have a nice day!&lt;/p&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;a id=&quot;ref&quot; class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;4. Reference&lt;/strong&gt;&lt;/span&gt;&lt;a id=&quot;reference&quot; rel=&quot;nofollow&quot; href=&quot;#ref&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For further information, please visit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Git stash:&lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-stash&quot;&gt; https://git-scm.com/docs/git-stash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Git commit: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.atlassian.com/git/tutorials/saving-changes/git-commit&quot;&gt; https://www.atlassian.com/git/tutorials/saving-changes/git-commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Git merge: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.atlassian.com/git/tutorials/using-branches/git-merge&quot;&gt; https://www.atlassian.com/git/tutorials/using-branches/git-merge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Understanding the Git Workflow:&lt;a rel=&quot;nofollow&quot; href=&quot;https://guides.github.com/introduction/flow/&quot;&gt; https://guides.github.com/introduction/flow/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17408/please-commit-your-changes-or-stash-them-before-you-merge-aborting</guid>
<pubDate>Mon, 11 May 2026 08:08:35 +0000</pubDate>
</item>
<item>
<title>Nowadays, LLM is based and major for me</title>
<link>https://coderlegion.com/17360/nowadays-llm-is-based-and-major-for-me</link>
<description>&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/topstar-ai/OpenLLM&quot;&gt;https://github.com/topstar-ai/OpenLLM&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://bentoml.com&quot;&gt;https://bentoml.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Run any open-source LLMs, such as DeepSeek and Llama, as OpenAI compatible API endpoint in the cloud.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8591791232814553939&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17360/nowadays-llm-is-based-and-major-for-me</guid>
<pubDate>Mon, 11 May 2026 04:56:55 +0000</pubDate>
</item>
<item>
<title>[Fixed] Fatal: could not read from remote repository.</title>
<link>https://coderlegion.com/16943/fixed-fatal-could-not-read-from-remote-repository</link>
<description>&lt;p&gt;&lt;strong&gt;The &quot;&lt;span style=&quot;color: rgb(185, 106, 217)&quot;&gt;Fatal: could not read from remote repository&lt;/span&gt;&quot; error occurs when a Git client is unable to access a remote repository. This error can happen for several reasons, such as a network connection issue, incorrect repository URL, or insufficient permissions. To solve this problem, you can try a number of methods such as checking your network connection, verifying the repository URL, and ensuring you have the proper permissions to access the repository. In this article, we'll provide detailed solutions to help you resolve it.&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;
&lt;h4&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;Table of Contents &lt;a class=&quot;open&quot;&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#what&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;What is the problem?&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#how&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;How to solve this problem?&lt;/span&gt;&lt;/a&gt;
&lt;/li&gt;&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#con&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Conclusion&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#ref&quot;&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;Reference&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;
&lt;section&gt;
&lt;a class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;1. What is the problem?&lt;/strong&gt;&lt;/span&gt; &lt;a id=&quot;what_the_problem&quot; rel=&quot;nofollow&quot; href=&quot;#what&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;a. Define the problem&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt; The &quot;&lt;strong&gt;Fatal: could not read from remote repository&lt;/strong&gt;&quot; error is a common issue faced by Git users when they try to access a remote repository. This error message usually appears in the terminal or command line interface when the Git client is unable to establish a connection with the remote repository. The error message typically appears as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git clone https://github.com/user/repo.git
Cloning into 'repo'...
fatal: unable to access 'https://github.com/user/repo.git/': Could not read from remote repository.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;b. Why is it occur?&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;This error can occur for several reasons, some of which are listed below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Incorrect repository URL: If the repository URL is incorrect or invalid, the Git client will not be able to connect to the remote repository and the error message will appear.&lt;/li&gt;
&lt;li&gt;Network connection issue: If there is a problem with your network connection, the Git client will not be able to establish a connection with the remote repository and the error message will appear.&lt;/li&gt;
&lt;li&gt;Insufficient permissions: If the Git client does not have the necessary permissions to access the remote repository, the error message will appear.&lt;/li&gt;
&lt;li&gt;Firewall or proxy restrictions: If there is a firewall or proxy restriction in place, the Git client may not be able to establish a connection with the remote repository and the error message will appear.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;div-pink&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Caution&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;Some of these reasons can be easily resolved, others may require a more technical understanding of Git and network configurations. If you are unable to resolve the error on your own, it may be necessary to consult with a more experienced Git user or your system administrator. &lt;/span&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;c. Reproduce the error&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;To reproduce the error, try cloning a remote repository using the Git client. If the error message appears, then it indicates that there is a problem with accessing the remote repository. For example, if you try to clone a repository using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git clone https://github.com/user/repo.git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the error message appears, which means that there is a problem with accessing the remote repository.&lt;/p&gt;
&lt;p&gt;Another way to reproduce the error is by trying to push changes to a remote repository using the Git client. If the error message appears, it means that there is a problem with accessing the remote repository. For example, the error message will appear if you try to push changes to a remote repository using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git push origin master&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For general cases, understanding the reasons behind this error message is crucial in order to troubleshoot and solve the problem.
&lt;/p&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;a class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;2. How to solve this problem?&lt;/strong&gt;&lt;/span&gt; &lt;a id=&quot;how_to_solve&quot; rel=&quot;nofollow&quot; href=&quot;#why&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;div-pink&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Caution&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;Before making any changes to your Git configuration, it is important to backup your existing settings and to thoroughly understand the implications of each solution.&lt;/span&gt;
&lt;/div&gt;
&lt;br&gt;

&lt;p&gt;Here are some methods that you can try to resolve the problem:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Checking the URL&lt;/strong&gt;: Ensure that the URL of the remote repository is correct. If you are using an HTTPS URL, make sure that it is typed correctly and is accessible from your network. You can also try using an SSH URL instead. To do this, use the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git clone *Emails are not allowed*:user/repo.git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Configuring proxy&lt;/strong&gt;: If your network requires a proxy to access the Internet, configure Git to use the proxy by setting the '&lt;strong&gt;http_proxy&lt;/strong&gt;' and '&lt;strong&gt;https_proxy&lt;/strong&gt;' environment variables. You can do this by adding the following lines to your Git configuration file located at '&lt;strong&gt;~/.gitconfig&lt;/strong&gt;':&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[http]
  proxy = http://proxy.example.com:8080
[https]
  proxy = https://proxy.example.com:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Checking SSH keys&lt;/strong&gt;: If you are using an SSH URL to clone the repository, make sure that the SSH key being used is correctly set up and associated with your GitHub account. You can check this by running the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -T *Emails are not allowed&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Using Git protocol version 2&lt;/strong&gt;: If you are using a version of Git prior to 2.0, you may encounter the &quot;Fatal: could not read from remote repository&quot; error. To resolve this, upgrade to the latest version of Git or use the '&lt;strong&gt;-v&lt;/strong&gt;' option to specify version 2 of the Git protocol.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git clone -v --branch  [branch] [repo]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Disabling firewalls&lt;/strong&gt;: Firewall restrictions may prevent Git from accessing the remote repository. To resolve this, you can either temporarily disable the firewall or allow Git through the firewall.&lt;/p&gt;

&lt;div class=&quot;div-purple&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;It is recommended to try each solution in the order presented, as some solutions may not be applicable or necessary in every case. Additionally, some solutions may require administrative privileges and may need to be performed on a server or by your system administrator.&lt;/span&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;a class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;3. Conclusion&lt;/strong&gt;&lt;/span&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#con&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this article, we discussed the &quot;&lt;strong&gt;Fatal: could not read from remote repository&lt;/strong&gt;&quot; error and its causes. The error message occurs when a Git client is unable to access a remote repository and can be caused by several factors such as an incorrect repository URL, network connection issues, insufficient permissions, or firewall or proxy restrictions. We also provided several methods for resolving the error, including verifying the repository URL, checking the network connection, ensuring proper permissions, and checking for firewall or proxy restrictions.&lt;/p&gt; 
&lt;p&gt;If you have any questions or comments, feel free to leave them below. Have a nice day!&lt;/p&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;a class=&quot;custom_anchor&quot;&gt;&lt;/a&gt; &lt;h2&gt;&lt;span style=&quot;font-family:arial,helvetica,sans-serif;&quot;&gt;&lt;strong&gt;4. Reference&lt;/strong&gt;&lt;/span&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#ref&quot; style=&quot;color: blue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For further information, please visit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&quot;Git clone: fatal: Could not read from remote repository&quot; - Git Documentation:&lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-clone#git-clone---mirror&quot;&gt; https://git-scm.com/docs/git-clone#git-clone---mirror&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&quot;Git - Cloning a Repository&quot;:&lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-clone&quot;&gt; https://git-scm.com/docs/git-clone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&quot;GitLab - Importing a Repository&quot;:&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.gitlab.com/ee/user/project/repository/importing.html&quot;&gt; https://docs.gitlab.com/ee/user/project/repository/importing.html&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16943/fixed-fatal-could-not-read-from-remote-repository</guid>
<pubDate>Thu, 07 May 2026 07:14:16 +0000</pubDate>
</item>
<item>
<title>Git Command</title>
<link>https://coderlegion.com/16479/git-command</link>
<description>&lt;p&gt;&lt;strong&gt;The Clean All (discard all changes)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clean -fd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;How do you move a commit to the staging area in git?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --soft HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pushing empty commits to remote&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --allow-empty -m &quot;Trigger Build&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;History&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git log --graph --color --decorate --oneline --all
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;gitk --all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Git to discard uncommitted changes in your working directory&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Moves the last commit back to the staging area (index), keeping all changes.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --soft HEAD~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Moves the last commit back to the staging area (index), Discard all changes.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard HEAD~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Deletes a local branch named branch_name.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch -d branch_name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Force Delete a Branch (If Not Merged):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch -D branch_name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Delete a Remote Branch:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin --delete branch_name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;List both local &amp;amp; remote branches:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Prunes (removes) local references to remote branches that no longer exist on the remote.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git fetch --prune
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Quickly Switch to the Previous Branch&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Show a Visual Branch Tree&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git log --oneline --graph --all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Search Commit Messages&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git log --grep=&quot;bugfix&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Configures Git to use Windows' built-in SSL/TLS (Schannel) instead of OpenSSL&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global https.sslBackend schannel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Trigger the CI/CD pipeline with a blank commit&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --allow-empty -m 'Empty commit'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;visualizing Git history&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gitk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;How do you move a commit to the staging area in git?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --soft HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Git to discard uncommitted changes in your working directory&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;source:- &lt;a rel=&quot;nofollow&quot; href=&quot;https://mdmirajansari.vercel.app/blog/17&quot;&gt;link&lt;/a&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/16479/git-command</guid>
<pubDate>Sun, 03 May 2026 07:05:31 +0000</pubDate>
</item>
<item>
<title>The Renaissance of Git Worktrees</title>
<link>https://coderlegion.com/16284/the-renaissance-of-git-worktrees</link>
<description>&lt;p&gt;Git worktrees are not new. They were introduced back in 2015 as part of Git version 2.5. For years, they stayed mostly under the radar, used by a small group of developers who understood their value.&lt;/p&gt;
&lt;p&gt;Now they are suddenly everywhere.&lt;/p&gt;
&lt;p&gt;In 2025 and 2026, worktrees have seen a strong resurgence. The reason is the way we write code is changing. With the rise of agent driven development and parallel workflows, worktrees have become a practical necessity.&lt;/p&gt;
&lt;h3 id=&quot;what-are-git-worktrees&quot;&gt;What are Git worktrees&lt;/h3&gt;
&lt;p&gt;Normally, if you are inside one cloned repository and you switch branches, your whole working directory changes with it. That becomes annoying in situations like these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are working on a feature, and suddenly need to fix a production bug on another branch&lt;/li&gt;
&lt;li&gt;You want to review a pull request locally without disturbing your current work&lt;/li&gt;
&lt;li&gt;You want to compare two branches side by side&lt;/li&gt;
&lt;li&gt;You need one directory for development and another for running tests or a local build on a different branch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without worktrees, many developers solve this by cloning the same repository multiple times. That works, but it wastes disk space, duplicates setup, and creates extra maintenance. Worktrees give you multiple folders while still using the same underlying repository.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works-1&quot;&gt;How it works&lt;/h3&gt;
&lt;p&gt;You keep your normal repository folder, then create extra folders attached to it. Each folder can be on a different branch.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my-app/                 # main worktree
my-app-feature-login/   # linked worktree
my-app-hotfix/          # linked worktree
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each one is a real working directory, so you can open it in your editor, run builds there, and commit from there. Git keeps metadata so it knows all these directories belong to the same repository.&lt;/p&gt;
&lt;p&gt;The main command is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git worktree add &amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, start in your main project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch

* main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new worktree for a feature:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git worktree add ../app-feature-login -b feature/login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a new folder ../app-feature-login is created&lt;/li&gt;
&lt;li&gt;a new branch feature/login is created&lt;/li&gt;
&lt;li&gt;that branch is checked out in the new folder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now you have:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app/                  # main branch
app-feature-login/    # feature/login branch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can open both in your editor and have your coding agents work simultaneously without interrupting each other.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16284/the-renaissance-of-git-worktrees</guid>
<pubDate>Thu, 30 Apr 2026 18:08:50 +0000</pubDate>
</item>
<item>
<title>How to Make Conventional Commits a Lasting Habit?</title>
<link>https://coderlegion.com/16273/how-to-make-conventional-commits-a-lasting-habit</link>
<description>&lt;p&gt;Conventional commits sound great in theory: structured, readable commit messages that make history tracking easier. But let's be honest—most of us try them for a few days and then fall back into our old habits. Why? Because it’s tedious to remember the exact syntax, and we often don’t have the patience to write proper commit messages when we’re in the zone.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-conventional-commits-fade-away-quickly&quot;&gt;The Problem: Conventional Commits Fade Away Quickly&lt;/h3&gt;
&lt;p&gt;If you've ever attempted to enforce conventional commits in your projects, you might have noticed this pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Day 1:&lt;/strong&gt; You're excited and commit with messages like &lt;code&gt;feat: add authentication module&lt;/code&gt; or &lt;code&gt;fix: resolve login bug&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Day 3:&lt;/strong&gt; You start forgetting the exact format and write something like &lt;code&gt;feat(auth): added stuff&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Day 7:&lt;/strong&gt; You're back to &lt;code&gt;fix bug&lt;/code&gt; or &lt;code&gt;wip&lt;/code&gt;, and conventional commits are a thing of the past.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s not that you don’t see the value—it’s just hard to stay consistent without frictionless tooling. That’s where &lt;code&gt;convcommit&lt;/code&gt; comes in.&lt;/p&gt;
&lt;h3 id=&quot;meet-convcommit-your-memory-booster-for-commit-messages&quot;&gt;Meet Convcommit: Your Memory Booster for Commit Messages&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;convcommit&lt;/code&gt; is a simple but powerful CLI tool that ensures you never fall back into bad commit habits. It does a few things:&lt;/p&gt;
&lt;p&gt;✅ &lt;strong&gt;Guides you through commit message selection&lt;/strong&gt; – No need to memorize commit types or scopes.&lt;/p&gt;
&lt;p&gt;✅ &lt;strong&gt;Stores and suggests your previous messages&lt;/strong&gt; – If you’ve used a message before, you can quickly reuse it.&lt;/p&gt;
&lt;p&gt;✅ &lt;strong&gt;Works with &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt; seamlessly&lt;/strong&gt; – Keeping your workflow fast and efficient.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works-2&quot;&gt;How It Works&lt;/h3&gt;
&lt;p&gt;Using &lt;code&gt;convcommit&lt;/code&gt; is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install it (if you haven’t already):&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mush install convcommit
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Run it whenever you commit:&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;convcommit -a -p
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Select the commit type, scope (if any), and message from suggestions or enter a new one.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it! No more second-guessing your commit messages or breaking your commit history structure.&lt;/p&gt;
&lt;h3 id=&quot;why-you-should-start-using-it-today&quot;&gt;Why You Should Start Using It Today&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Develop muscle memory&lt;/strong&gt;: The more you use it, the more natural conventional commits become.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintain a clean commit history&lt;/strong&gt;: Future-you (and your teammates) will thank you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Boost your personal branding&lt;/strong&gt;: Writing clean commits is a sign of professionalism in open-source projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;try-it-now-and-make-your-commits-count-&quot;&gt;Try It Now and Make Your Commits Count!&lt;/h3&gt;
&lt;p&gt;You can check out &lt;code&gt;convcommit&lt;/code&gt; on GitHub, give it a star ⭐, and start using it today:&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/francescobianco/convcommit&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let me know what you think, and if you have ideas for improvements, PRs are always welcome! Happy committing! &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16273/how-to-make-conventional-commits-a-lasting-habit</guid>
<pubDate>Thu, 30 Apr 2026 15:47:59 +0000</pubDate>
</item>
<item>
<title>GitHub Suspended Me. Turns Out, That Was Fine.</title>
<link>https://coderlegion.com/16004/github-suspended-me-turns-out-that-was-fine</link>
<description>&lt;p&gt;Okay so I've been putting off writing this post for weeks because honestly I wasn't sure how much I wanted to share. But enough people have asked what happened with Ultimate Media Downloader that I figure I owe everyone an explanation. Fair warning: this is long. Make a coffee.&lt;/p&gt;
&lt;p&gt;Late March 2026. Everything was normal. I was pushing updates, answering issues, doing the usual open-source maintainer thing. Nothing felt off. I had notifications from GitHub, I was working on a fix for a bug that had been annoying me for days, and life was... fine, you know? Boring good. The kind of good you don't appreciate until it's gone.&lt;/p&gt;
&lt;p&gt;Then GitHub suspended my account.&lt;/p&gt;
&lt;h3 id=&quot;day-one-the-screen-that-broke-my-brain&quot;&gt;Day One: The Screen That Broke My Brain&lt;/h3&gt;
&lt;p&gt;I'm not going to pretend I handled it gracefully. I did not. &lt;/p&gt;
&lt;p&gt;I opened GitHub to push a commit and got the suspension screen. My brain kind of just... stopped for a second. I reread it three times thinking I was somehow misreading it. I wasn't.&lt;/p&gt;
&lt;p&gt;The reason they gave was vague. Something about policy violations and suspicious activity. I genuinely had no idea what I'd done. I mean — I host a media downloader. I've always known that sits in a gray area. But I wasn't the first person to do this, and there are way bigger projects doing the exact same thing still sitting on GitHub right now, so I figured I'd just... appeal it and it'd get sorted.&lt;/p&gt;
&lt;p&gt;Reader, it did not get sorted quickly.&lt;/p&gt;
&lt;h3 id=&quot;shouting-into-the-github-support-void&quot;&gt;Shouting Into the GitHub Support Void&lt;/h3&gt;
&lt;p&gt;Have you ever submitted an appeal to a large tech platform? It's a specific kind of miserable. You write this really thoughtful, detailed explanation. You cite examples. You reference other projects. You basically build a legal case for why your account should be reinstated. And then you get back an email that reads like it was written by someone who definitely did not read your email.&lt;/p&gt;
&lt;p&gt;That was my week two.&lt;/p&gt;
&lt;p&gt;I sent multiple follow-ups. I explained that the software is a tool and that tools don't have intent. I pointed out YouTube-DL, which does functionally the same thing and had its own GitHub drama a few years back. Nothing landed. Or if it did, nobody told me.&lt;/p&gt;
&lt;p&gt;What made it worse — and this is the part that kept me up at night — is that I had users. Real people relying on auto-updates that pulled from GitHub releases. My version checker hit the GitHub API. Everything was tangled up with my account. And I was locked out.&lt;/p&gt;
&lt;p&gt;By about day ten I stopped waiting and started planning.&lt;/p&gt;
&lt;h3 id=&quot;the-backwards-move-that-actually-worked&quot;&gt;The Backwards Move That Actually Worked&lt;/h3&gt;
&lt;p&gt;I'm skipping over some of the specifics here because honestly they're boring and a bit embarrassing. But the short version is: I managed to get temporary access to the repo through some recovery options I'd forgotten I'd set up years ago. &lt;/p&gt;
&lt;p&gt;My first instinct when I got in was to back everything up. And then I made what felt like a weird call at the time: I made the repository private.&lt;/p&gt;
&lt;p&gt;I know that sounds backwards. &quot;Your project is already invisible because your account is suspended, why would you make it more invisible?&quot; But my thinking was — if GitHub sees a popular public repo suddenly go private, maybe they interpret that as me taking security concerns seriously rather than being a bad actor. It was a gamble.&lt;/p&gt;
&lt;p&gt;Turns out it kind of worked? I started getting slightly less robotic responses from their support team. Something shifted. I don't know exactly what, and I'm not going to pretend I do.&lt;/p&gt;
&lt;h3 id=&quot;back-in-but-not-really-welcome&quot;&gt;Back In, But Not Really Welcome&lt;/h3&gt;
&lt;p&gt;They reinstated the account. With conditions attached. Basically: the main repo for Ultimate Media Downloader couldn't stay as-is. Too &quot;potentially problematic.&quot; That was their phrase, not mine.&lt;/p&gt;
&lt;p&gt;So I was back. But it didn't feel like a win. It felt like being let back into a party where the host is clearly not happy you're there. You're standing by the snack table and every few minutes someone glances at you and you're like — yeah I know, I know.&lt;/p&gt;
&lt;p&gt;That was the moment I actually started taking Codeberg seriously.&lt;/p&gt;
&lt;h3 id=&quot;fine-let-s-try-this-codeberg-thing-&quot;&gt;Fine. Let's Try This Codeberg Thing.&lt;/h3&gt;
&lt;p&gt;I'd known Codeberg existed for a while. I'd seen it mentioned in open-source forums and had vaguely filed it away as &quot;GitHub but smaller and European.&quot; Never felt urgent enough to actually explore.&lt;/p&gt;
&lt;p&gt;Now it felt urgent.&lt;/p&gt;
&lt;p&gt;I spent a day on the platform just clicking around. And the first thing I noticed was how — I don't know how to say this without sounding cheesy — how genuine it felt? There's no AI assistant trying to summarize your commits. No featured marketplace pushing you to buy things. No weird enterprise upsell banners. It's just a git hosting platform run by a non-profit that actually believes in what it's doing.&lt;/p&gt;
&lt;p&gt;That shouldn't be a revelation. But after the GitHub experience it kind of was.&lt;/p&gt;
&lt;p&gt;The other thing I noticed: nobody there was going to suspend my account for hosting a media tool. Because they actually care about software freedom. Not performatively — actually.&lt;/p&gt;
&lt;p&gt;By end of week four I'd made up my mind.&lt;/p&gt;
&lt;h3 id=&quot;the-actual-migration&quot;&gt;The Actual Migration&lt;/h3&gt;
&lt;p&gt;This part is less dramatic than the rest of the story. Migrations are mostly just tedious, not traumatic.&lt;/p&gt;
&lt;p&gt;I updated the README. Changed the clone URLs. Updated the version checker endpoint from GitHub's API to Codeberg's. Went through the contributing docs, the code of conduct, the issue templates — every file that had a github.com link in it. Found more than I expected. One of them was buried in a comment inside a Python file that I'd written like two years ago and completely forgot about. That took a minute.&lt;/p&gt;
&lt;p&gt;Total time: maybe a day and a half, spread over two days because I kept getting distracted.&lt;/p&gt;
&lt;p&gt;The harder part was more psychological. I'd been on GitHub for years. All my institutional memory of &quot;where things live&quot; pointed there. Switching platforms meant rewriting some mental muscle memory, which sounds small but isn't.&lt;/p&gt;
&lt;p&gt;But once it was done? I felt weirdly relieved.&lt;/p&gt;
&lt;h3 id=&quot;where-we-re-at-now&quot;&gt;Where We're At Now&lt;/h3&gt;
&lt;p&gt;It's late April. The project lives on Codeberg now. Everything works. Updates go out, the version checker works, people are downloading things just fine.&lt;/p&gt;
&lt;p&gt;The Codeberg community is smaller. That's real. Fewer people stumble across your project because there's no algorithm surfacing it. But the people who do find it seem more... intentional? Like they were looking for something specific rather than just scrolling. I've had better issue reports in the last few weeks than I used to get in a month.&lt;/p&gt;
&lt;p&gt;I still have my GitHub account. I'm using it mostly to mirror things over there, just so people who bookmarked old URLs don't hit dead ends. But I'm not building anything like that there anymore.&lt;/p&gt;
&lt;p&gt;But still using the githb to showcase my previous successful project and more. because i don't want to sart over all of it again.&lt;/p&gt;
&lt;h3 id=&quot;the-actual-takeaway&quot;&gt;The Actual Takeaway&lt;/h3&gt;
&lt;p&gt;I don't want to wrap this up with some neat lesson because the experience wasn't neat.&lt;/p&gt;
&lt;p&gt;It was stressful. I was anxious for basically a month straight. I sent emails to GitHub support that I now find slightly embarrassing in retrospect. I lost sleep. My project went dark for a stretch of time and I couldn't even tell users why because I didn't fully understand why myself.&lt;/p&gt;
&lt;p&gt;But — and I mean this — I wouldn't have moved to Codeberg if it hadn't happened. I was too comfortable. Sometimes that's what it takes.&lt;/p&gt;
&lt;p&gt;If you're one of the users who noticed things were broken and didn't get an explanation at the time: I'm sorry. That sucked and you deserved better communication from me. I was in reactive mode and honestly just trying to keep the thing alive.&lt;/p&gt;
&lt;p&gt;It's alive. It's on Codeberg. Come find it there.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;One more thing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I still have like four documentation files somewhere that probably have a github.com URL hiding in them. I'll find them eventually. If you spot one before I do, open an issue. On Codeberg, obviously.&lt;/p&gt;
&lt;p&gt;— NK2552003 &lt;/p&gt;
&lt;p&gt;Writing from my home office which is definitely a real office and not just my bed with a laptop&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://codeberg.org/nk2552003/umd&quot;&gt;Codeberg &amp;gt; UMD&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://codeberg.org/nk2552003&quot;&gt;Github&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16004/github-suspended-me-turns-out-that-was-fine</guid>
<pubDate>Mon, 27 Apr 2026 16:54:04 +0000</pubDate>
</item>
<item>
<title>ArgoCD Guide: GitOps Continuous Delivery for Kubernetes</title>
<link>https://coderlegion.com/15641/argocd-guide-gitops-continuous-delivery-for-kubernetes</link>
<description>
&lt;a href=&quot;https://alexandre-vazquez.com/argocd-guide/&quot; title=&quot;ArgoCD Guide: GitOps Continuous Delivery for Kubernetes&quot; rel=&quot;nofollow&quot;&gt;&lt;img width=&quot;1672&quot; height=&quot;941&quot; src=&quot;https://mlk5tkvgfeoa.i.optimole.com/cb:s8WW.68/w:1672/h:941/q:mauto/f:best/https://alexandre-vazquez.com/wp-content/uploads/2026/04/ArgoCD.avif&quot; class=&quot;webfeedsFeaturedVisual wp-post-image&quot; alt=&quot;ArgoCD Guide: GitOps Continuous Delivery for Kubernetes&quot; style=&quot;display: block; margin: auto; margin-bottom: 5px;max-width: 100%;&quot; title=&quot;ArgoCD Guide: GitOps Continuous Delivery for Kubernetes 4&quot;&gt;&lt;/a&gt;
&lt;p class=&quot;lead&quot;&gt;ArgoCD has become the de facto standard for GitOps-based continuous delivery in Kubernetes. If you are running production workloads on Kubernetes and still deploying with raw &lt;code&gt;kubectl apply&lt;/code&gt; or untracked Helm releases, ArgoCD solves a class of problems you may not even know you have yet. This guide covers everything from core concepts to production-grade configuration.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Problem ArgoCD Solves&lt;/h2&gt;



&lt;p&gt;Traditional CI/CD pushes deployments into a cluster. A CI system runs tests, builds an image, and then executes &lt;code&gt;kubectl apply&lt;/code&gt; or &lt;code&gt;helm upgrade&lt;/code&gt; against the cluster. This model has several structural problems:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Drift goes undetected.&lt;/strong&gt; Someone applies a hotfix directly to the cluster. Now your Git repository no longer reflects reality, and nobody knows it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;No single source of truth.&lt;/strong&gt; The cluster state is authoritative, not Git. Your desired state and actual state can diverge silently.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rollback is painful.&lt;/strong&gt; Rolling back a bad deployment means re-running old CI pipelines or manually reversing changes, neither of which is fast.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Multi-cluster management compounds the problem.&lt;/strong&gt; Each cluster becomes a snowflake with its own history of undocumented changes.&lt;/li&gt;&lt;/ul&gt;



&lt;p&gt;GitOps inverts this model. Git is the source of truth. The cluster pulls its desired state from Git and continuously reconciles toward it. ArgoCD is the most mature GitOps operator for Kubernetes, implementing this pull-based model with a production-ready feature set.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;How ArgoCD Works: Core Architecture&lt;/h2&gt;



&lt;p&gt;ArgoCD runs as a set of controllers inside your Kubernetes cluster. The core components are:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Application Controller&lt;/strong&gt; &amp;mdash; Watches both the Git repository and the live cluster state. Computes the diff and drives reconciliation.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;API Server&lt;/strong&gt; &amp;mdash; Exposes the gRPC/REST API consumed by the CLI, UI, and external systems.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Repository Server&lt;/strong&gt; &amp;mdash; Generates Kubernetes manifests from source (Helm, Kustomize, plain YAML, Jsonnet).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt; &amp;mdash; Caches cluster state and repository data to reduce API server load.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Dex&lt;/strong&gt; (optional) &amp;mdash; Provides OIDC authentication for SSO integration.&lt;/li&gt;&lt;/ul&gt;



&lt;p&gt;The fundamental unit in ArgoCD is an &lt;strong&gt;Application&lt;/strong&gt; &amp;mdash; a CRD that maps a source (a path in a Git repo at a specific revision) to a destination (a namespace in a cluster). ArgoCD continuously compares the desired state from Git with the live state in the cluster and reports on the sync status.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Sync Status vs Health Status&lt;/h3&gt;



&lt;p&gt;Two orthogonal concepts you need to understand from day one:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Sync Status&lt;/strong&gt; &amp;mdash; Does the live state match what Git says it should be? Values: Synced, OutOfSync, Unknown.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Health Status&lt;/strong&gt; &amp;mdash; Is the application actually working? Values: Healthy, Progressing, Degraded, Suspended, Missing, Unknown.&lt;/li&gt;&lt;/ul&gt;



&lt;p&gt;An application can be &lt;em&gt;Synced&lt;/em&gt; but &lt;em&gt;Degraded&lt;/em&gt; &amp;mdash; the manifests were applied correctly, but a pod is crash-looping. Conversely, it can be &lt;em&gt;OutOfSync&lt;/em&gt; but &lt;em&gt;Healthy&lt;/em&gt; &amp;mdash; someone applied a change directly to the cluster outside of Git.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Installing ArgoCD&lt;/h2&gt;



&lt;p&gt;The official installation method uses a single manifest. For production, always pin to a specific version:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;kubectl create namespace argocd
kubectl apply -n argocd -f &lt;a href=&quot;https://raw.githubusercontent.com/argoproj/argo-cd/v2.11.0/manifests/install.yaml&quot; rel=&quot;nofollow&quot;&gt;https://raw.githubusercontent.com/argoproj/argo-cd/v2.11.0/manifests/install.yaml&lt;/a&gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This deploys ArgoCD in the &lt;code&gt;argocd&lt;/code&gt; namespace with full cluster-admin access. For a production HA setup, use the &lt;code&gt;manifests/ha/install.yaml&lt;/code&gt; variant, which deploys multiple replicas of the API server and application controller.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Accessing the UI and CLI&lt;/h3&gt;



&lt;p&gt;The initial admin password is auto-generated and stored in a secret:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;argocd admin initial-password -n argocd&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;For local access, port-forward the API server:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;kubectl port-forward svc/argocd-server -n argocd 8080:443&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Then log in via the CLI:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;argocd login localhost:8080 --username admin --password &amp;lt;password&amp;gt; --insecure&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;For production, expose the ArgoCD server via an Ingress or LoadBalancer with a proper TLS certificate. If you&amp;rsquo;re using NGINX Ingress Controller:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    nginx.ingress.kubernetes.io/ssl-passthrough: &quot;true&quot;
    nginx.ingress.kubernetes.io/backend-protocol: &quot;HTTPS&quot;
spec:
  ingressClassName: nginx
  rules:
  - host: argocd.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              number: 443&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Defining Your First Application&lt;/h2&gt;



&lt;p&gt;Applications can be created via the UI, the CLI, or declaratively with a YAML manifest. The declarative approach is the recommended one &amp;mdash; it means your ArgoCD configuration itself is in Git:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: &lt;a href=&quot;https://github.com/your-org/your-app&quot; rel=&quot;nofollow&quot;&gt;https://github.com/your-org/your-app&lt;/a&gt;
    targetRevision: HEAD
    path: k8s/overlays/production
  destination:
    server: &lt;a href=&quot;https://kubernetes.default.svc&quot; rel=&quot;nofollow&quot;&gt;https://kubernetes.default.svc&lt;/a&gt;
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Key fields to understand:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;targetRevision&lt;/strong&gt; &amp;mdash; Can be a branch name, tag, or commit SHA. For production, pin to a tag rather than &lt;code&gt;HEAD&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;path&lt;/strong&gt; &amp;mdash; The directory within the repo containing your Kubernetes manifests.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;automated.prune&lt;/strong&gt; &amp;mdash; Automatically delete resources that are no longer in Git. Required for true GitOps but use carefully &amp;mdash; it will delete things.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;automated.selfHeal&lt;/strong&gt; &amp;mdash; Automatically revert manual changes made directly to the cluster. This is what enforces Git as the single source of truth.&lt;/li&gt;&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Helm Integration&lt;/h2&gt;



&lt;p&gt;ArgoCD has native Helm support. It can deploy Helm charts directly from chart repositories or from your Git repository. You can override values per environment:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prometheus-stack
  namespace: argocd
spec:
  project: default
  source:
    repoURL: &lt;a href=&quot;https://prometheus-community.github.io/helm-charts&quot; rel=&quot;nofollow&quot;&gt;https://prometheus-community.github.io/helm-charts&lt;/a&gt;
    chart: kube-prometheus-stack
    targetRevision: 58.4.0
    helm:
      releaseName: prometheus-stack
      valuesObject:
        grafana:
          adminPassword: &quot;${GRAFANA_PASSWORD}&quot;
        prometheus:
          prometheusSpec:
            retention: 30d
            storageSpec:
              volumeClaimTemplate:
                spec:
                  storageClassName: fast-ssd
                  resources:
                    requests:
                      storage: 50Gi
  destination:
    server: &lt;a href=&quot;https://kubernetes.default.svc&quot; rel=&quot;nofollow&quot;&gt;https://kubernetes.default.svc&lt;/a&gt;
    namespace: observability&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;One important nuance: ArgoCD renders Helm charts server-side using its own templating engine, not &lt;code&gt;helm install&lt;/code&gt;. This means Helm hooks (&lt;code&gt;pre-install&lt;/code&gt;, &lt;code&gt;post-upgrade&lt;/code&gt;, etc.) &lt;em&gt;are&lt;/em&gt; supported, but the release is not tracked in Helm&amp;rsquo;s release history. Running &lt;code&gt;helm list&lt;/code&gt; will not show ArgoCD-managed releases unless you configure ArgoCD to use the Helm secrets backend.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Projects: Multi-Tenancy and Access Control&lt;/h2&gt;



&lt;p&gt;ArgoCD Projects provide multi-tenancy within a single ArgoCD instance. They let you restrict which source repositories, destination clusters, and namespaces a team can deploy to. Every Application belongs to a Project.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: platform-team
  namespace: argocd
spec:
  description: Platform team applications
  sourceRepos:
  - '&lt;a href=&quot;https://github.com/your-org/*&quot; rel=&quot;nofollow&quot;&gt;https://github.com/your-org/*&lt;/a&gt;'
  destinations:
  - namespace: 'platform-*'
    server: &lt;a href=&quot;https://kubernetes.default.svc&quot; rel=&quot;nofollow&quot;&gt;https://kubernetes.default.svc&lt;/a&gt;
  clusterResourceWhitelist:
  - group: ''
    kind: Namespace
  namespaceResourceBlacklist:
  - group: ''
    kind: ResourceQuota&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Projects are where you define the boundaries of what each team can do. The &lt;code&gt;default&lt;/code&gt; project has no restrictions &amp;mdash; never use it for production workloads. Create dedicated projects per team or per environment.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;RBAC Configuration&lt;/h2&gt;



&lt;p&gt;ArgoCD has its own RBAC system layered on top of Kubernetes RBAC. It is configured via the &lt;code&gt;argocd-rbac-cm&lt;/code&gt; ConfigMap. Roles are defined per project or globally:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.default: role:readonly
  policy.csv: |
    # Platform team has full access to platform-team project
    p, role:platform-admin, applications, *, platform-team/*, allow
    p, role:platform-admin, projects, get, platform-team, allow
    p, role:platform-admin, repositories, *, *, allow

    # Dev team can sync but not delete
    p, role:developer, applications, get, */*, allow
    p, role:developer, applications, sync, */*, allow
    p, role:developer, applications, action/*, */*, allow

    # Bind SSO groups to roles
    g, your-org:platform-team, role:platform-admin
    g, your-org:developers, role:developer&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The &lt;code&gt;policy.default: role:readonly&lt;/code&gt; ensures that any authenticated user who has no explicit role assignment gets read-only access &amp;mdash; a safe default for production.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Multi-Cluster Management&lt;/h2&gt;



&lt;p&gt;ArgoCD can manage multiple Kubernetes clusters from a single control plane. Register external clusters with the CLI:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# First, ensure the target cluster context is in your kubeconfig
argocd cluster add production-eu-west --name production-eu-west

# Verify registration
argocd cluster list&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;ArgoCD will create a ServiceAccount in the target cluster and store its credentials as a Kubernetes secret in the ArgoCD namespace. Applications can then target this cluster by name in their &lt;code&gt;destination.server&lt;/code&gt; field.&lt;/p&gt;



&lt;p&gt;For large-scale multi-cluster setups, consider the &lt;strong&gt;App of Apps&lt;/strong&gt; pattern or &lt;strong&gt;ApplicationSets&lt;/strong&gt;. ApplicationSets are a controller that generates Applications dynamically based on generators &amp;mdash; cluster lists, Git directory structures, or matrix combinations:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-addons
  namespace: argocd
spec:
  generators:
  - clusters:
      selector:
        matchLabels:
          environment: production
  template:
    metadata:
      name: '{{name}}-addons'
    spec:
      project: platform
      source:
        repoURL: &lt;a href=&quot;https://github.com/your-org/cluster-addons&quot; rel=&quot;nofollow&quot;&gt;https://github.com/your-org/cluster-addons&lt;/a&gt;
        targetRevision: HEAD
        path: 'addons/{{metadata.labels.region}}'
      destination:
        server: '{{server}}'
        namespace: kube-system&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This single ApplicationSet deploys the appropriate addons to every cluster labeled &lt;code&gt;environment: production&lt;/code&gt;, using each cluster&amp;rsquo;s region label to select the correct path in the repository.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Sync Strategies and Waves&lt;/h2&gt;



&lt;p&gt;When deploying complex applications with dependencies between resources, you need to control the order of deployment. ArgoCD provides two mechanisms:&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Sync Phases&lt;/h3&gt;



&lt;p&gt;Resources are deployed in three phases: PreSync, Sync, and PostSync. Use Sync Hooks for resources that must complete before the main sync proceeds (database migrations, certificate issuance, etc.):&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: migrate
        image: your-app:v1.2.3
        command: [&quot;./migrate.sh&quot;]
      restartPolicy: Never&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Sync Waves&lt;/h3&gt;



&lt;p&gt;Within the Sync phase, waves control ordering. Resources with a lower wave number are applied and must become healthy before resources with higher wave numbers are applied:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# Applied first
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: &quot;1&quot;

# Applied after wave 1 is healthy
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: &quot;2&quot;&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Notifications and Alerting&lt;/h2&gt;



&lt;p&gt;ArgoCD Notifications is a standalone controller that sends alerts when Application state changes. It supports Slack, PagerDuty, GitHub commit status, email, and a dozen other providers. Configure it via the &lt;code&gt;argocd-notifications-cm&lt;/code&gt; ConfigMap:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  template.app-sync-failed: |
    slack:
      attachments: |
        [{
          &quot;title&quot;: &quot;{{.app.metadata.name}}&quot;,
          &quot;color&quot;: &quot;#E96D76&quot;,
          &quot;fields&quot;: [{
            &quot;title&quot;: &quot;Sync Status&quot;,
            &quot;value&quot;: &quot;{{.app.status.sync.status}}&quot;,
            &quot;short&quot;: true
          },{
            &quot;title&quot;: &quot;Message&quot;,
            &quot;value&quot;: &quot;{{range .app.status.conditions}}{{.message}}{{end}}&quot;,
            &quot;short&quot;: false
          }]
        }]
  trigger.on-sync-failed: |
    - when: app.status.sync.status == 'Unknown'
      send: [app-sync-failed]
    - when: app.status.operationState.phase in ['Error', 'Failed']
      send: [app-sync-failed]&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Secret Management with ArgoCD&lt;/h2&gt;



&lt;p&gt;ArgoCD intentionally has no secret management built in &amp;mdash; storing secrets in Git as plain text is never acceptable. The common patterns are:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Sealed Secrets&lt;/strong&gt; (Bitnami) &amp;mdash; Encrypts secrets with a cluster-specific key. The encrypted secret can be committed to Git; only the cluster can decrypt it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;External Secrets Operator&lt;/strong&gt; &amp;mdash; Syncs secrets from Vault, AWS Secrets Manager, GCP Secret Manager, etc. into Kubernetes secrets. The ArgoCD Application manages the ExternalSecret CRD, not the actual secret value.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;argocd-vault-plugin&lt;/strong&gt; &amp;mdash; A plugin that replaces placeholder values in manifests with secrets retrieved from Vault at sync time.&lt;/li&gt;&lt;/ul&gt;



&lt;p&gt;The External Secrets Operator approach is the most flexible for teams already using a centralized secrets backend. The Application in ArgoCD deploys &lt;code&gt;ExternalSecret&lt;/code&gt; objects, which the ESO controller resolves at runtime without ever touching Git.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Production Best Practices&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Run ArgoCD in HA mode.&lt;/strong&gt; Use &lt;code&gt;manifests/ha/install.yaml&lt;/code&gt; with 3 replicas of the API server and multiple application controller shards for large clusters (100+ applications).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pin image versions.&lt;/strong&gt; Never use &lt;code&gt;latest&lt;/code&gt; for the ArgoCD image itself. Pin to a specific version and upgrade deliberately.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Use the App of Apps pattern for bootstrapping.&lt;/strong&gt; A single root Application deploys all other Applications. This makes cluster bootstrapping idempotent and reproducible.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Separate ArgoCD config from application config.&lt;/strong&gt; Store ArgoCD Application manifests in a dedicated &lt;code&gt;gitops&lt;/code&gt; repository, separate from application source code.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Enable resource tracking via annotations.&lt;/strong&gt; Use &lt;code&gt;application.resourceTrackingMethod: annotation&lt;/code&gt; in &lt;code&gt;argocd-cm&lt;/code&gt; instead of the default label-based tracking, which can conflict with Helm&amp;rsquo;s own labels.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Set resource limits on ArgoCD controllers.&lt;/strong&gt; Application controller CPU and memory scale with the number of resources tracked. Monitor and tune accordingly.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Restrict auto-sync in production.&lt;/strong&gt; Consider requiring manual sync approval for production environments even when using GitOps &amp;mdash; or at minimum require a PR approval gate before changes reach the target branch.&lt;/li&gt;&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ArgoCD vs Flux&lt;/h2&gt;



&lt;p&gt;Flux v2 is the other major GitOps operator. Both are CNCF projects. The main differences in practice:&lt;/p&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Feature&lt;/th&gt;&lt;th&gt;ArgoCD&lt;/th&gt;&lt;th&gt;Flux v2&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;UI&lt;/td&gt;&lt;td&gt;Built-in web UI&lt;/td&gt;&lt;td&gt;No official UI (use Weave GitOps)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Multi-cluster&lt;/td&gt;&lt;td&gt;Single control plane manages many clusters&lt;/td&gt;&lt;td&gt;Agent per cluster, pull model&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ApplicationSets&lt;/td&gt;&lt;td&gt;Native&lt;/td&gt;&lt;td&gt;Kustomization + HelmRelease&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Secret management&lt;/td&gt;&lt;td&gt;Plugin-based&lt;/td&gt;&lt;td&gt;SOPS native integration&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Learning curve&lt;/td&gt;&lt;td&gt;Steeper (more concepts)&lt;/td&gt;&lt;td&gt;Lower (Kubernetes-native CRDs)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CNCF status&lt;/td&gt;&lt;td&gt;Graduated&lt;/td&gt;&lt;td&gt;Graduated&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;



&lt;p&gt;ArgoCD wins when you need the UI, multi-cluster management from a central plane, or have a large operations team that benefits from the visual application topology view. Flux wins when you want a simpler, purely Kubernetes-native approa&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/15641/argocd-guide-gitops-continuous-delivery-for-kubernetes</guid>
<pubDate>Thu, 23 Apr 2026 12:47:43 +0000</pubDate>
</item>
<item>
<title>GitWand - A complete workflow for GIT</title>
<link>https://coderlegion.com/15413/gitwand-a-complete-workflow-for-git</link>
<description>&lt;p&gt;Every Git client I've ever used treats merge conflicts the same way: show the markers, let the human figure it out. Even for the obvious ones — a lockfile where both branches bumped a version number, a config file where only one side changed a value, two branches that independently made the exact same edit. The tool knows it's a conflict. It just doesn't try to resolve it.&lt;/p&gt;
&lt;p&gt;PhpStorm has a &quot;magic wand&quot; button that resolves trivial conflicts automatically. It's one of those features that, once you've used it, makes every other editor feel like it's missing a limb. I wanted that everywhere — not just in PhpStorm, not just with a specific language server. So I built the engine myself, as a standalone TypeScript library at the core of GitWand.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/15413/gitwand-a-complete-workflow-for-git</guid>
<pubDate>Tue, 21 Apr 2026 21:13:19 +0000</pubDate>
</item>
<item>
<title>Why I built another Git client</title>
<link>https://coderlegion.com/15412/why-i-built-another-git-client</link>
<description>&lt;p&gt;There are already a dozen Git GUIs. GitKraken, Fork, Tower, Sourcetree, GitHub Desktop, Sublime Merge, GitButler. Most of them are good. Some of them are excellent. So when people ask why I spent the better part of a year building yet another one, I owe them a real answer.&lt;/p&gt;
&lt;p&gt;The short version: every Git client I've used shows you merge conflicts and stops there. Even for the obvious ones.&lt;/p&gt;
&lt;h3 id=&quot;the-thing-that-kept-bothering-me&quot;&gt;The thing that kept bothering me&lt;/h3&gt;
&lt;p&gt;PhpStorm has a &quot;magic wand&quot; button in its merge editor. When you open a conflicted file, it scans every hunk and resolves the trivial ones automatically — the ones where only one side changed something, where both sides made the same edit, where the differences are just indentation. You're left with only the conflicts that genuinely need human judgment.&lt;/p&gt;
&lt;p&gt;It's one of those features that, once you've used it, makes every other tool feel like it's missing a limb.&lt;/p&gt;
&lt;p&gt;I've been using Git for a long time. I've resolved a lot of merge conflicts. And a huge portion of them — I'd estimate somewhere between 60% and 80% in a typical week — are completely trivial. A lockfile where two branches bumped the same dependency to different patch versions. A config file where only one branch touched a value. Two branches that independently made the exact same edit. None of these require a human decision. They're just noise.&lt;/p&gt;
&lt;p&gt;Every Git client I know makes you resolve them manually anyway.&lt;/p&gt;
&lt;h3 id=&quot;what-i-actually-wanted&quot;&gt;What I actually wanted&lt;/h3&gt;
&lt;p&gt;I wanted a Git client that understood the structure of a conflict, not just its existence.&lt;/p&gt;
&lt;p&gt;Not an AI that guesses at resolution and might hallucinate the wrong answer. Something deterministic — a classifier that applies explicit rules, explains its reasoning, and never touches anything ambiguous. If it's not certain, it asks. If it is certain, it just fixes it.&lt;/p&gt;
&lt;p&gt;I also wanted this to be available everywhere, not just inside a specific IDE. In the terminal (&lt;code&gt;npx @gitwand/cli resolve&lt;/code&gt;). In CI (&lt;code&gt;--ci&lt;/code&gt; flag, structured JSON output). As an MCP tool that AI agents like Claude can call. And in a native desktop app that doesn't cost $150MB of RAM just to show a diff.&lt;/p&gt;
&lt;p&gt;That's GitWand.&lt;/p&gt;
&lt;h3 id=&quot;the-conflict-resolution-engine&quot;&gt;The conflict resolution engine&lt;/h3&gt;
&lt;p&gt;The core of GitWand is a pattern registry — nine conflict types, evaluated in priority order, each with an explicit detection rule and a confidence score.&lt;/p&gt;
&lt;p&gt;The patterns, from simplest to most complex:&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=333803734182358893&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The engine requires &lt;code&gt;git merge --diff3&lt;/code&gt; (or &lt;code&gt;merge.conflictstyle diff3&lt;/code&gt; in your config) to unlock the most powerful patterns. With the three-way base available, &lt;code&gt;one_side_change&lt;/code&gt; is trivially detectable: if ours matches the ancestor but theirs doesn't, take theirs. Without the base, you can't tell who changed what.&lt;/p&gt;
&lt;p&gt;Every resolution carries a composite confidence score — not a simple label, but a weighted formula across five dimensions: classification certainty, data risk, block size, file complexity, and base availability. Anything below &quot;high&quot; is left for you, with a full decision trace explaining what was detected and why.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;✓ src/config.ts — 3/3 resolved
  L12 [one_side_change] certain — Only the incoming branch modified this block.
  L25 [same_change] certain — Both branches made the exact same edit.
  L41 [value_only_change] high — Scalar value updated on one side (version field).
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-merge-preview&quot;&gt;The merge preview&lt;/h3&gt;
&lt;p&gt;One of my favorite features — and the one that gets the most surprised reactions from people seeing it for the first time — is the merge preview.&lt;/p&gt;
&lt;p&gt;Before you merge any branch, GitWand simulates the outcome without touching your working tree. It uses &lt;code&gt;git merge-base&lt;/code&gt;, &lt;code&gt;git show&lt;/code&gt;, and &lt;code&gt;git merge-file -p --diff3&lt;/code&gt; to compute exactly what would happen, then shows you a per-file breakdown:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto-resolvable — the engine can handle it &lt;/li&gt;
&lt;li&gt;Partial — some hunks need your attention &lt;/li&gt;
&lt;li&gt;Manual — complex conflicts requiring human judgment&lt;/li&gt;
&lt;li&gt;Add/delete — a file was added on one side, deleted on the other&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The summary badge tells you at a glance: &lt;code&gt;Clean merge&lt;/code&gt;, &lt;code&gt;100% auto-resolvable&lt;/code&gt;, or &lt;code&gt;3 conflicts to review&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This matters because the worst time to discover a gnarly conflict is after you've already run git merge and your working tree is in a half-merged state. The preview gives you that information before you commit to anything.&lt;/p&gt;
&lt;h3 id=&quot;format-aware-resolvers&quot;&gt;Format-aware resolvers&lt;/h3&gt;
&lt;p&gt;Plain-text conflict detection misses a lot. Two branches that both added a dependency to &lt;code&gt;package.json&lt;/code&gt; will appear as an overlapping edit on the &lt;code&gt;dependencies&lt;/code&gt; block — a false conflict, because the changes are semantically independent.&lt;/p&gt;
&lt;p&gt;GitWand ships semantic resolvers for the file formats where this happens most often: JSON and JSONC (recursive key-by-key merge), Markdown (section-by-section merge by ATX heading), YAML, Vue single-file components, CSS/SCSS, TypeScript/JavaScript imports, and the major lockfile formats (npm, yarn, pnpm). For Cargo.toml. For Dockerfile. For &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The JSON resolver, as a quick example: it parses all three versions (ours, theirs, base), computes per-key diffs, and merges key by key. A key added on one side only → accept it. A key modified identically on both sides → keep it. A key modified differently on both sides → flag it as unresolvable and fall back to text. Nested objects are handled recursively.&lt;/p&gt;
&lt;p&gt;The result is that a &lt;code&gt;package.json&lt;/code&gt; where one branch added &lt;code&gt;lodash&lt;/code&gt; and another added &lt;code&gt;axios&lt;/code&gt; gets merged cleanly, even though the &lt;code&gt;dependencies&lt;/code&gt; block was &quot;touched&quot; on both sides.&lt;/p&gt;
&lt;h3 id=&quot;the-cli-and-the-mcp-server&quot;&gt;The CLI and the MCP server&lt;/h3&gt;
&lt;p&gt;The desktop app is the main interface, but the engine doesn't live only there.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npx @gitwand/cli resolve&lt;/code&gt; runs the same engine from the terminal. &lt;code&gt;--dry-run&lt;/code&gt; previews without writing. &lt;code&gt;--verbose&lt;/code&gt; shows the full decision trace per hunk. &lt;code&gt;--ci&lt;/code&gt; returns structured JSON with confidence scores, decision traces, and a &lt;code&gt;pendingHunks&lt;/code&gt; array for the conflicts the engine couldn't resolve.&lt;/p&gt;
&lt;p&gt;That &lt;code&gt;pendingHunks&lt;/code&gt; array is the bridge to the MCP server (&lt;code&gt;@gitwand/mcp&lt;/code&gt;). An AI agent — Claude, Cursor, Windsurf — can call &lt;code&gt;gitwand_resolve_conflicts&lt;/code&gt;, get back the trivially-resolved hunks plus the unresolved ones with full context, and then apply its own reasoning to the remainder via &lt;code&gt;gitwand_apply_resolution&lt;/code&gt;. GitWand handles the deterministic part. The LLM handles the semantic reasoning. Neither tries to do the other's job.&lt;/p&gt;
&lt;h3 id=&quot;what-was-harder-than-expected&quot;&gt;What was harder than expected&lt;/h3&gt;
&lt;p&gt;A few things humbled me during this build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edge cases in JSON merging.&lt;/strong&gt; The recursive key-by-key approach works well for most configs, but there are pathological cases — arrays of objects where identity is ambiguous, deeply nested structures where one side restructured the hierarchy. The resolver has to know when to give up and fall back to text, and getting that boundary right took more iteration than I expected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;whitespace_only&lt;/code&gt; false positive problem.&lt;/strong&gt; Whitespace differences that are semantically neutral in most languages (trailing spaces, mixed tabs/spaces) are semantically significant in Python. The resolver normalizes aggressively but has to be careful not to flatten indentation that carries meaning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS Gatekeeper.&lt;/strong&gt; GitWand isn't notarized yet (it's on the roadmap). First launch on macOS requires a right-click → Open workaround because Gatekeeper blocks unsigned apps. This creates unnecessary friction for new users and is near the top of the priority list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Auto-update.&lt;/strong&gt; The mechanism exists but had four silent bugs that were preventing it from ever triggering in practice. Fixing them went into v1.6.3 alongside the worktree and submodule work.&lt;/p&gt;
&lt;h3 id=&quot;where-things-stand&quot;&gt;Where things stand&lt;/h3&gt;
&lt;p&gt;GitWand is at v1.6.2. It covers the full daily Git workflow — changes, history, branches, push/pull, cherry-pick, stash, interactive rebase, amend, PR review with inline comments and CI checks — plus the conflict resolution engine, the merge preview, the CLI, the MCP server, and a VS Code extension.&lt;/p&gt;
&lt;p&gt;322 tests passing. MIT licensed. Available for macOS, Linux, and Windows.&lt;/p&gt;
&lt;p&gt;The repo is at &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/devlint/GitWand&quot;&gt;github.com/devlint/GitWand&lt;/a&gt;. If you want just the engine without the desktop app, &lt;code&gt;npx @gitwand/cli resolve&lt;/code&gt; is the fastest way in.&lt;/p&gt;
&lt;p&gt;And if you use &lt;code&gt;git merge --diff3&lt;/code&gt; (you should: &lt;code&gt;git config --global merge.conflictstyle diff3&lt;/code&gt;), the engine will automatically use the three-way base for better classification.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15412/why-i-built-another-git-client</guid>
<pubDate>Tue, 21 Apr 2026 21:13:15 +0000</pubDate>
</item>
<item>
<title>Understanding Closures in Go</title>
<link>https://coderlegion.com/13380/understanding-closures-in-go</link>
<description>
&lt;p&gt;&lt;em&gt;Hello, I'm Ganesh Kumar. I'm working on &lt;a href=&quot;https://hexmos.com/livereview/git-lrc/&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;git-lrc&lt;/a&gt;: a Git hook for Checking AI generated code.AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my previous post, I explained how anonymous functions created and understand how it works.&lt;/p&gt;

&lt;p&gt;In this article we will understand how closure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is closure
&lt;/h2&gt;

&lt;p&gt;A closure is a function value that references variables from outside its body. &lt;/p&gt;

&lt;p&gt;The function may access and assign to the referenced variables; in this sense the function is &quot;bound&quot; to the variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  How closure is used?
&lt;/h2&gt;

&lt;p&gt;Each time new value updates the sum variable.&lt;/p&gt;

&lt;p&gt;Next time when we call the function, it will use the updated value of sum.&lt;/p&gt;

&lt;p&gt;So, It remembers the value of sum.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight go&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;
&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;gk@jarvis:~/exp/code/rd/go-exmaple&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go run main.go 
0 0
1 &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;
3 &lt;span class=&quot;nt&quot;&gt;-3&lt;/span&gt;
6 &lt;span class=&quot;nt&quot;&gt;-6&lt;/span&gt;
10 &lt;span class=&quot;nt&quot;&gt;-10&lt;/span&gt;
15 &lt;span class=&quot;nt&quot;&gt;-15&lt;/span&gt;
21 &lt;span class=&quot;nt&quot;&gt;-21&lt;/span&gt;
28 &lt;span class=&quot;nt&quot;&gt;-28&lt;/span&gt;
36 &lt;span class=&quot;nt&quot;&gt;-36&lt;/span&gt;
45 &lt;span class=&quot;nt&quot;&gt;-45&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Fibonacci series
&lt;/h2&gt;

&lt;p&gt;Here is the example of fibonacci series.&lt;br&gt;
initially we are initializing a and b with 0 and 1.&lt;/p&gt;

&lt;p&gt;then with each time calling the function it will return the return value of a.&lt;/p&gt;

&lt;p&gt;and update the value of a and b.&lt;/p&gt;

&lt;p&gt;So, hence creating a fibonacci series.&lt;br&gt;
&lt;/p&gt;
&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight go&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// fibonacci is a function that returns&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// a function that returns an int.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fibonacci&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fibonacci&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;
&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;gk@jarvis:~/exp/code/rd/go-exmaple&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go run main.go 
0
1
1
2
3
5
8
13
21
34
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article we understood how closure is used. closure is a function value that references variables from outside its body.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://hexmos.com/livereview/git-lrc/&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifqf83t1qjl4u0g6aj4l.png&quot; alt=&quot;git-lrc&quot; width=&quot;800&quot; height=&quot;381&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; Check out: &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;git-lrc&lt;/a&gt;&lt;br&gt;
Any feedback or contributors are welcome! It&amp;rsquo;s online, open-source, and ready for anyone to use.&lt;br&gt;
&amp;#11088; Star it on GitHub:&lt;br&gt;
&lt;/p&gt;
&lt;div class=&quot;ltag-github-readme-tag&quot;&gt;
  &lt;div class=&quot;readme-overview&quot;&gt;
    &lt;h2&gt;
      &lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg&quot; alt=&quot;GitHub logo&quot;&gt;
      &lt;a href=&quot;https://github.com/HexmosTech&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;
        HexmosTech
      &lt;/a&gt; / &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;
        git-lrc
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Free, Unlimited AI Code Reviews That Run on Commit
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class=&quot;ltag-github-body&quot;&gt;
    
&lt;div id=&quot;readme&quot; class=&quot;md&quot;&gt;
&lt;div&gt;
&lt;a rel=&quot;noopener noreferrer nofollow&quot; href=&quot;https://camo.githubusercontent.com/948c8f2d5cf41b48985cd364d48c3a2dc9bfbfd42eab3e0a9a1b3e61f5f17ce3/68747470733a2f2f6865786d6f732e636f6d2f66726565646576746f6f6c732f7075626c69632f6c725f6c6f676f2e737667&quot;&gt;&lt;img width=&quot;60&quot; alt=&quot;git-lrc logo&quot; src=&quot;https://camo.githubusercontent.com/948c8f2d5cf41b48985cd364d48c3a2dc9bfbfd42eab3e0a9a1b3e61f5f17ce3/68747470733a2f2f6865786d6f732e636f6d2f66726565646576746f6f6c732f7075626c69632f6c725f6c6f676f2e737667&quot;&gt;&lt;/a&gt;
&lt;p&gt;&lt;strong&gt;git-lrc&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free, Unlimited AI Code Reviews That Run on Commit&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;/div&gt;



&lt;div&gt;
&lt;a href=&quot;https://www.producthunt.com/products/git-lrc?embed=true&amp;amp;utm_source=badge-top-post-badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=badge-git-lrc&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;img alt=&quot;git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt&quot; width=&quot;250&quot; height=&quot;54&quot; src=&quot;https://camo.githubusercontent.com/87bf2d4283c1e0aa99e254bd17fefb1c67c0c0d39300043a243a4aa633b6cecc/68747470733a2f2f6170692e70726f6475637468756e742e636f6d2f776964676574732f656d6265642d696d6167652f76312f746f702d706f73742d62616467652e7376673f706f73745f69643d31303739323632267468656d653d6c6967687426706572696f643d6461696c7926743d31373731373439313730383638&quot;&gt;&lt;/a&gt;
&lt;/div&gt;




&lt;p&gt;AI agents write code fast. They also &lt;em&gt;silently remove logic&lt;/em&gt;, change behavior, and introduce bugs -- without telling you. You often find out in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git-lrc&lt;/code&gt; fixes this.&lt;/strong&gt; It hooks into &lt;code&gt;git commit&lt;/code&gt; and reviews every diff &lt;em&gt;before&lt;/em&gt; it lands. 60-second setup. Completely free.&lt;/p&gt;

&lt;div class=&quot;markdown-heading&quot;&gt;
&lt;h2 class=&quot;heading-element&quot;&gt;See It In Action&lt;/h2&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;See git-lrc catch serious security issues such as leaked credentials, expensive cloud
operations, and sensitive material in log statements&lt;/p&gt;
&lt;/blockquote&gt;


  
    

    &lt;span class=&quot;m-1&quot;&gt;git-lrc-intro-60s.mp4&lt;/span&gt;
  

  

  


&lt;div class=&quot;markdown-heading&quot;&gt;
&lt;h2 class=&quot;heading-element&quot;&gt;Why&lt;/h2&gt;

&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt; &lt;strong&gt;AI agents silently break things.&lt;/strong&gt; Code removed. Logic changed. Edge cases gone. You won't notice until production.&lt;/li&gt;

&lt;li&gt; &lt;strong&gt;Catch it before it ships.&lt;/strong&gt; AI-powered inline comments show you &lt;em&gt;exactly&lt;/em&gt; what changed and what looks wrong.&lt;/li&gt;

&lt;li&gt; &lt;strong&gt;Build a habit, ship better code.&lt;/strong&gt; Regular review &amp;rarr; fewer bugs &amp;rarr; more robust code &amp;rarr; better results in your team.&lt;/li&gt;

&lt;li&gt; &lt;strong&gt;Why git?&lt;/strong&gt; Git is universal. Every editor, every IDE, every AI&amp;hellip;&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class=&quot;gh-btn-container&quot;&gt;&lt;a class=&quot;gh-btn&quot; href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;



</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/13380/understanding-closures-in-go</guid>
<pubDate>Mon, 23 Mar 2026 10:11:14 +0000</pubDate>
</item>
<item>
<title>The New Minimum: What You Must Know About Code You Didn't Write?</title>
<link>https://coderlegion.com/13230/the-new-minimum-what-you-must-know-about-code-you-didnt-write</link>
<description>&lt;p&gt;Let's be honest. We don't read code anymore.&lt;/p&gt;
&lt;p&gt;AI writes it. We skim it. We ship it. Maybe we glance at the diff in a PR review, maybe we don't. The era of reading every line of code in your codebase is over, and pretending otherwise is just performative engineering.&lt;/p&gt;
&lt;p&gt;And honestly? That's fine. You don't need to read every line. You never really did.&lt;/p&gt;
&lt;p&gt;But here's what's not fine — most of us have dropped below the minimum threshold of understanding. We've stopped reading the code, and along with it, we've lost something far more important than the &quot;what&quot; or the &quot;how.&quot;&lt;/p&gt;
&lt;p&gt;We've lost the &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-why-is-the-only-thing-that-matters&quot;&gt;The &quot;Why&quot; Is the Only Thing That Matters&lt;/h3&gt;
&lt;p&gt;Think about your codebase right now. You probably know &lt;em&gt;what&lt;/em&gt; it does — it's an API, a dashboard, a pipeline. You might even know &lt;em&gt;how&lt;/em&gt; certain parts work — this service calls that service, data flows from here to there.&lt;/p&gt;
&lt;p&gt;But can you answer these?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why is the auth service separate from the main API?&lt;/li&gt;
&lt;li&gt;Why did we pick Postgres over Mongo for this particular data model?&lt;/li&gt;
&lt;li&gt;Why does this request go through three middleware layers before hitting the handler?&lt;/li&gt;
&lt;li&gt;Why is this config hardcoded instead of environment-driven?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the questions that matter when things break at 2 AM. When a new hire asks &quot;why is it like this?&quot; and you realize you have no idea. When a security auditor asks why data flows through a particular path and you're guessing.&lt;/p&gt;
&lt;p&gt;The &quot;what&quot; and &quot;how&quot; — those are recoverable. You can read the code, trace the flow, figure it out. But the &quot;why&quot; is almost never in the code itself.&lt;/p&gt;
&lt;h3 id=&quot;where-the-why-actually-lives&quot;&gt;Where the &quot;Why&quot; Actually Lives&lt;/h3&gt;
&lt;p&gt;Here's the uncomfortable truth: the reasoning behind most architectural decisions in your codebase exists in one of these places:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A Slack thread&lt;/strong&gt; from 18 months ago that nobody can find&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A meeting&lt;/strong&gt; that was never recorded&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The head of an engineer&lt;/strong&gt; who left the company&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A compliance requirement&lt;/strong&gt; that was communicated verbally&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A PR description&lt;/strong&gt; that says &quot;refactored auth module&quot; with no further context&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code &lt;em&gt;implies&lt;/em&gt; the why, but you need context to read between the lines. Without that context, you're just looking at the outcome of a decision with no way to trace it back.&lt;/p&gt;
&lt;p&gt;And here's what makes this worse in 2026: &lt;strong&gt;AI-generated code doesn't even have a &quot;why.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When Claude or Copilot writes a function, there was no design decision. There was no tradeoff discussion. There was a prompt, and there was output. The code works, but the reasoning layer — the intent — was never there to begin with. It was in the developer's head when they wrote the prompt, and it evaporated the moment they hit Enter.&lt;/p&gt;
&lt;h3 id=&quot;-just-write-better-commit-messages-&quot;&gt;&quot;Just Write Better Commit Messages&quot;&lt;/h3&gt;
&lt;p&gt;I know what you're thinking. I thought it too.&lt;/p&gt;
&lt;p&gt;The obvious solution: capture the &quot;why&quot; in commit messages. You can even put it in your &lt;code&gt;CLAUDE.md&lt;/code&gt; or agent rules — &quot;always include the reason behind the change in commit messages.&quot; Discipline your team to do the same.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feat: split auth into separate service

Why: Compliance team requires auth logs to be isolated from
application logs for SOC2 audit trail. Shared DB made this
impossible without query-level filtering.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's beautiful. That's exactly what a commit message should look like.&lt;/p&gt;
&lt;p&gt;But here's the problem — &lt;strong&gt;we don't read commit messages either.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Be honest. When was the last time you ran &lt;code&gt;git log&lt;/code&gt; and actually read through commit messages to understand why something is the way it is? We barely read code anymore. We're definitely not reading commit history.&lt;/p&gt;
&lt;p&gt;Commit messages, PR descriptions, ADRs, design docs — they're all &lt;strong&gt;write-only mediums&lt;/strong&gt;. People write them (sometimes), but nobody goes back to read them when it matters. The &quot;why&quot; gets captured but never retrieved.&lt;/p&gt;
&lt;p&gt;The problem isn't capture. It's retrieval.&lt;/p&gt;
&lt;h3 id=&quot;the-knowledge-tiers&quot;&gt;The Knowledge Tiers&lt;/h3&gt;
&lt;p&gt;So if we've accepted that we don't read code, and we've accepted that the &quot;why&quot; is the hardest thing to recover — what's the minimum you actually need to know?&lt;/p&gt;
&lt;p&gt;Here's how I think about it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Must Know (you're dangerous without this):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System boundaries — what are the services, what owns what&lt;/li&gt;
&lt;li&gt;Data flow — how does a request travel through the system&lt;/li&gt;
&lt;li&gt;Auth model — who can access what and how&lt;/li&gt;
&lt;li&gt;Integration points — what external systems do we depend on&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Should Know (you're effective with this):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Key design decisions and the reasoning behind them&lt;/li&gt;
&lt;li&gt;Database schema and why it's structured that way&lt;/li&gt;
&lt;li&gt;Error handling strategy — what happens when things fail&lt;/li&gt;
&lt;li&gt;The constraints that shaped the architecture (compliance, scale, team size)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Can Look Up (don't waste brain space):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implementation details of individual functions&lt;/li&gt;
&lt;li&gt;Utility code and helpers&lt;/li&gt;
&lt;li&gt;Specific algorithm choices&lt;/li&gt;
&lt;li&gt;Configuration specifics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Don't Need:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Line-by-line understanding of every file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The critical insight: the &quot;Must Know&quot; and &quot;Should Know&quot; tiers are almost entirely about the &lt;strong&gt;why&lt;/strong&gt;, not the what. You need to know &lt;em&gt;why&lt;/em&gt; the system boundaries are where they are. &lt;em&gt;Why&lt;/em&gt; data flows this way and not that way. &lt;em&gt;Why&lt;/em&gt; the database schema looks like this.&lt;/p&gt;
&lt;h3 id=&quot;the-real-test&quot;&gt;The Real Test&lt;/h3&gt;
&lt;p&gt;Here's a simple test for whether you understand your codebase at the level that matters:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can you explain why it's built this way — without opening the code?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Not what it does. Not how it works. &lt;em&gt;Why&lt;/em&gt; this architecture. &lt;em&gt;Why&lt;/em&gt; these tradeoffs. &lt;em&gt;Why&lt;/em&gt; this database, this queue, this separation of concerns.&lt;/p&gt;
&lt;p&gt;If you can't, you're below the line. And that's where most of us are right now, if we're honest about it.&lt;/p&gt;
&lt;h3 id=&quot;the-why-needs-to-be-surfaced-not-just-stored&quot;&gt;The &quot;Why&quot; Needs to Be Surfaced, Not Just Stored&lt;/h3&gt;
&lt;p&gt;The real solution isn't writing better docs or more disciplined commit messages — though those help. The real solution is making the &quot;why&quot; available &lt;strong&gt;at the point where you need it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Not buried in git history. Not scattered across Slack. Not locked in someone's head. But surfaced, structured, and accessible when you're actually making decisions about the code.&lt;/p&gt;
&lt;p&gt;This is what I've been building with &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.revibe.codes&quot;&gt;Revibe&lt;/a&gt;. It analyzes your codebase and reconstructs the architectural reasoning — the system boundaries, the design decisions, the &quot;why&quot; behind the structure. Not by reading your Slack (it can't), but by reading the code deeply enough to surface what's implicit.&lt;/p&gt;
&lt;p&gt;It's not perfect. No tool can fully recover intent that was never documented. But it gets you above that minimum threshold — the point where you can answer &quot;why is it built this way?&quot; with something better than &quot;I don't know, it was like this when I got here.&quot;&lt;/p&gt;
&lt;h3 id=&quot;the-engineer-s-new-job&quot;&gt;The Engineer's New Job&lt;/h3&gt;
&lt;p&gt;We've accepted that AI writes code. Fine.&lt;/p&gt;
&lt;p&gt;But someone still needs to own the &quot;why.&quot; Someone needs to know why the system is shaped the way it is, why these tradeoffs were made, why this architecture and not another.&lt;/p&gt;
&lt;p&gt;That someone is you. And the uncomfortable truth is that as AI takes over more of the writing, the &quot;why&quot; layer is disappearing even faster — because AI doesn't have intent. It has instructions.&lt;/p&gt;
&lt;p&gt;The engineer's job is shifting. It's no longer &quot;write the code.&quot; It's &lt;strong&gt;&quot;maintain the why.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And if you can't do that, it doesn't matter how good your AI agent is at writing code. You don't understand your codebase at the level that matters.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.revibe.codes&quot;&gt;Revibe&lt;/a&gt; helps you understand any codebase — architecture, design decisions, and the reasoning behind the code. Try it free.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13230/the-new-minimum-what-you-must-know-about-code-you-didnt-write</guid>
<pubDate>Fri, 20 Mar 2026 02:22:13 +0000</pubDate>
</item>
</channel>
</rss>