Skip to content

Commit 11bf3de

Browse files
aymuos15claude
andcommitted
feat: add worktree vs branches microblog with interactive diagram
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c4fab19 commit 11bf3de

2 files changed

Lines changed: 165 additions & 0 deletions

File tree

script.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,16 @@ document.getElementById('back-btn').addEventListener('click', () => {
416416
// ── Microblog ────────────────────────────────────────────────────────────────
417417
/* eslint-disable quotes */
418418
const microblogEntries = [
419+
{
420+
id: "(Git) worktrees-vs-branches",
421+
date: "Mar. '26",
422+
title: "(Git) Worktrees vs Branches",
423+
tags: ["git", "workflow"],
424+
diagram: "worktree",
425+
content: `<p>A <strong>branch</strong> is just a pointer to a commit. Switching branches swaps files in your single working directory. Which forces one on stashing uncommitted work. A <strong>worktree</strong> (<code>git worktree add</code>) gives each branch its own directory, all backed by the same <code>.git</code> object store. Thus, no stashing required, a personal ick only tbh.</p>
426+
<p>The agentic advantage: worktrees let you run coding agents in parallel. Each agent gets its own isolated checkout while sharing the same repo. <a href="https://github.com/jj-vcs/jj" target="_blank">Jujutsu (jj)</a> is an open-source alternative that bakes this kind of parallel workflow into the VCS itself. Find a better deep dive into other alternatives <a href="https://blog.ezyang.com/2026/03/parallel-agents-heart-sapling/" target="_blank">here</a>.</p>`,
427+
links: []
428+
},
419429
{
420430
id: "deformable-convolutions",
421431
date: "Mar. '26",
@@ -434,8 +444,146 @@ const microblogList = document.getElementById('microblog-list');
434444
const microblogDetail = document.getElementById('microblog-detail');
435445
let microblogTagSwitching = false;
436446

447+
// ── Worktree vs Branches Diagram ─────────────────────────────────────────────
448+
function buildWorktreeDiagram() {
449+
const container = document.getElementById('microblog-diagram-worktree');
450+
if (!container) return;
451+
452+
let mode = 'branch';
453+
454+
// Shared geometry
455+
const W = 300, H = 150;
456+
const gitX = W / 2, gitY = 22, gitRx = 30, gitRy = 12;
457+
458+
// Animated node positions: [gitEllipse, ...boxes, ...branch-labels]
459+
// We'll track box centres and animate between layouts
460+
const branchLayout = {
461+
boxes: [{ x: W / 2, y: 90 }],
462+
labels: [
463+
{ x: 46, y: 90, text: 'main', dim: true },
464+
{ x: W / 2, y: 130, text: 'feat-A', dim: false },
465+
{ x: 254, y: 90, text: 'fix-B', dim: true }
466+
],
467+
connectors: [[gitX, gitY + gitRy, W / 2, 90 - 15]],
468+
footnote: 'stash \u2192 checkout \u2192 switch'
469+
};
470+
471+
const worktreeLayout = {
472+
boxes: [
473+
{ x: W / 2 - 65, y: 90 },
474+
{ x: W / 2 + 65, y: 90 }
475+
],
476+
labels: [],
477+
connectors: [
478+
[gitX - 10, gitY + gitRy, W / 2 - 65, 90 - 15],
479+
[gitX + 10, gitY + gitRy, W / 2 + 65, 90 - 15]
480+
],
481+
footnote: 'parallel \u2014 no stashing needed'
482+
};
483+
484+
function buildSvg(layout) {
485+
const bw = 90, bh = 30;
486+
let s = `<svg viewBox="0 0 ${W} ${H}" class="wt-svg" xmlns="http://www.w3.org/2000/svg">`;
487+
488+
// .git ellipse
489+
s += `<ellipse cx="${gitX}" cy="${gitY}" rx="${gitRx}" ry="${gitRy}"
490+
fill="none" stroke="var(--text-secondary)" stroke-width="1" stroke-dasharray="3 2" opacity="0.55"/>`;
491+
s += `<text x="${gitX}" y="${gitY + 3.5}" text-anchor="middle"
492+
font-family="var(--font-mono)" font-size="8" fill="var(--text-secondary)">.git</text>`;
493+
494+
// Connectors (dashed lines from .git to boxes)
495+
layout.connectors.forEach(([x1, y1, x2, y2]) => {
496+
s += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}"
497+
stroke="var(--text-secondary)" stroke-width="0.7" stroke-dasharray="3 2" opacity="0.45"/>`;
498+
});
499+
500+
// Boxes
501+
layout.boxes.forEach((b, i) => {
502+
const col = layout.boxes.length > 1
503+
? (i === 0 ? 'var(--link)' : 'var(--text)')
504+
: 'var(--text)';
505+
s += `<rect x="${b.x - bw/2}" y="${b.y - bh/2}" width="${bw}" height="${bh}"
506+
rx="3" fill="none" stroke="${col}" stroke-width="1"/>`;
507+
// Inner label for worktree mode, "working dir" for branch mode
508+
const inner = layout.boxes.length === 1 ? 'working dir' : (i === 0 ? 'feat-A/' : 'fix-B/');
509+
s += `<text x="${b.x}" y="${b.y + 3}" text-anchor="middle"
510+
font-family="var(--font-mono)" font-size="7.5" fill="${col}">${inner}</text>`;
511+
});
512+
513+
// Branch labels (only in branch mode)
514+
layout.labels.forEach(l => {
515+
const col = l.dim ? 'var(--text-secondary)' : 'var(--link)';
516+
const op = l.dim ? '0.4' : '1';
517+
s += `<text x="${l.x}" y="${l.y + 3}" text-anchor="middle"
518+
font-family="var(--font-mono)" font-size="7" fill="${col}" opacity="${op}">${l.text}</text>`;
519+
// Active indicator arrow for the checked-out branch
520+
if (!l.dim && layout.boxes.length === 1) {
521+
const bx = layout.boxes[0].x;
522+
s += `<line x1="${l.x}" y1="${l.y - 4}" x2="${bx}" y2="${layout.boxes[0].y + bh/2}"
523+
stroke="var(--link)" stroke-width="0.6" stroke-dasharray="2 2" opacity="0.45"/>`;
524+
}
525+
});
526+
527+
// Footnote
528+
s += `<text x="${W / 2}" y="${H - 4}" text-anchor="middle"
529+
font-family="var(--font-mono)" font-size="6.5" fill="var(--text-secondary)" opacity="0.5">${layout.footnote}</text>`;
530+
531+
s += '</svg>';
532+
return s;
533+
}
534+
535+
function render() {
536+
const layout = mode === 'branch' ? branchLayout : worktreeLayout;
537+
container.querySelector('.wt-diagram-area').innerHTML = buildSvg(layout);
538+
}
539+
540+
// Toggle buttons (reuse dcn-toggle / dcn-btn styles)
541+
const toggle = document.createElement('div');
542+
toggle.className = 'dcn-toggle';
543+
544+
const btnBr = document.createElement('button');
545+
btnBr.className = 'dcn-btn active';
546+
btnBr.textContent = 'Branches';
547+
548+
const btnWt = document.createElement('button');
549+
btnWt.className = 'dcn-btn';
550+
btnWt.textContent = 'Worktrees';
551+
552+
function setMode(m) {
553+
if (m === mode) return;
554+
mode = m;
555+
btnBr.classList.toggle('active', mode === 'branch');
556+
btnWt.classList.toggle('active', mode === 'worktree');
557+
const area = container.querySelector('.wt-diagram-area');
558+
area.classList.add('wt-fading');
559+
setTimeout(() => {
560+
render();
561+
area.classList.remove('wt-fading');
562+
}, 200);
563+
}
564+
565+
btnBr.addEventListener('click', () => setMode('branch'));
566+
btnWt.addEventListener('click', () => setMode('worktree'));
567+
568+
toggle.appendChild(btnBr);
569+
toggle.appendChild(btnWt);
570+
571+
const area = document.createElement('div');
572+
area.className = 'wt-diagram-area';
573+
574+
const caption = document.createElement('p');
575+
caption.className = 'dcn-caption';
576+
caption.textContent = 'Click to compare single-directory branching with parallel worktrees.';
577+
578+
container.appendChild(toggle);
579+
container.appendChild(area);
580+
container.appendChild(caption);
581+
render();
582+
}
583+
437584
// ── Deformable Convolution Diagram ───────────────────────────────────────────
438585
function buildMicroblogDiagram(type) {
586+
if (type === 'worktree') { buildWorktreeDiagram(); return; }
439587
if (type !== 'deformable-conv') return;
440588
const container = document.getElementById('microblog-diagram-deformable-conv');
441589
if (!container) return;

styles/components/microblog.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,23 @@
431431
margin-top: 0.2rem;
432432
}
433433

434+
/* Worktree diagram */
435+
.wt-diagram-area {
436+
transition: opacity 0.2s ease;
437+
padding: 0.4rem 0;
438+
}
439+
440+
.wt-diagram-area.wt-fading {
441+
opacity: 0;
442+
}
443+
444+
.wt-svg {
445+
width: 100%;
446+
max-width: 320px;
447+
margin: 0 auto;
448+
display: block;
449+
}
450+
434451
@media (max-width: 768px) {
435452
.microblog-tag-btn {
436453
font-size: 0.6rem;

0 commit comments

Comments
 (0)