diff --git a/Rakefile b/Rakefile
index 18b408e..e3bc070 100644
--- a/Rakefile
+++ b/Rakefile
@@ -136,3 +136,183 @@ namespace :book do
end
task :default => "book:build"
+
+# ---------------------------------------------------------------------------
+# figures: namespace — JSON source → SVG/PNG figure pipeline
+# ---------------------------------------------------------------------------
+
+namespace :figures do
+ FIGURES_DIR = File.join(__dir__, 'figures')
+ IMAGES_DIR = File.join(__dir__, 'images')
+ RENDERER = File.join(__dir__, 'bin', 'render-figures.rb')
+
+ FIGURE_SOURCES = FileList["#{FIGURES_DIR}/*.json"].exclude("#{FIGURES_DIR}/_*.json")
+
+ desc 'Validate all figures/*.json against figures/_schema.json'
+ task :validate do
+ require 'json'
+ schema_path = File.join(FIGURES_DIR, '_schema.json')
+ unless File.exist?(schema_path)
+ abort "Missing #{schema_path}"
+ end
+ errors = []
+ FIGURE_SOURCES.each do |f|
+ begin
+ JSON.parse(File.read(f))
+ rescue JSON::ParserError => e
+ errors << "#{f}: #{e.message}"
+ end
+ end
+ if errors.any?
+ errors.each { |e| puts " INVALID: #{e}" }
+ abort "#{errors.size} invalid figure(s)"
+ end
+ puts " #{FIGURE_SOURCES.size} figure(s) valid JSON"
+ end
+
+ desc 'Render figures/*.json to images/*.svg'
+ task :build => :validate do
+ sh "ruby #{RENDERER}"
+ end
+
+ desc 'Render a single figure by name (e.g. rake figures:render[basic-branching-1])'
+ task :render, [:name] => :validate do |_t, args|
+ name = args[:name] or abort "Usage: rake figures:render[name]"
+ src = File.join(FIGURES_DIR, "#{name}.json")
+ abort "No such figure: #{src}" unless File.exist?(src)
+ sh "ruby #{RENDERER} #{src}"
+ end
+
+ desc 'Watch figures/*.json and re-render the corresponding SVG on change'
+ task :watch do
+ require 'json'
+ puts " Watching #{FIGURES_DIR} for changes (Ctrl-C to stop)..."
+ mtimes = {}
+ FIGURE_SOURCES.each { |f| mtimes[f] = File.mtime(f) }
+
+ loop do
+ Dir.glob("#{FIGURES_DIR}/*.json").each do |f|
+ next if File.basename(f).start_with?('_')
+ prev = mtimes[f]
+ cur = File.mtime(f)
+ next if prev == cur
+
+ mtimes[f] = cur
+ base = File.basename(f, '.json')
+ if prev.nil?
+ puts " + #{base}.json (new)"
+ else
+ puts " ~ #{base}.json changed"
+ end
+
+ begin
+ JSON.parse(File.read(f))
+ sh "ruby #{RENDERER} #{f}"
+ rescue JSON::ParserError => e
+ puts " ! #{base}.json: #{e.message}"
+ rescue => e
+ puts " ! #{base}.json render failed: #{e.message}"
+ end
+ end
+ sleep 0.5
+ end
+ end
+
+ desc 'Rasterize images/*.svg (from figures/) to images/*.png at 3x via rsvg-convert'
+ task :raster => :build do
+ rsvg = `which rsvg-convert`.strip
+ abort "rsvg-convert not found — install librsvg (brew install librsvg)" if rsvg.empty?
+
+ # Font check: verify rsvg-convert resolves JetBrains Mono via fontconfig
+ font_check = `fc-match 'JetBrains Mono' 2>/dev/null`.strip
+ unless font_check.downcase.include?('jetbrainsmono')
+ abort "JetBrains Mono not found by fontconfig.\n" \
+ "Locally: install the font. CI: set XDG_DATA_HOME or FONTCONFIG_FILE " \
+ "to expose theme/pdf/fonts/."
+ end
+
+ FIGURE_SOURCES.each do |json_path|
+ base = File.basename(json_path, '.json')
+ svg_path = File.join(IMAGES_DIR, "#{base}.svg")
+ png_path = File.join(IMAGES_DIR, "#{base}.png")
+ next unless File.exist?(svg_path)
+
+ # Read the viewBox to compute a 3x width
+ vb = File.read(svg_path)[/viewBox="0 0 ([\d.]+) ([\d.]+)"/, 1]
+ if vb
+ width = (vb.to_f * 3).round
+ sh "rsvg-convert -w #{width} #{svg_path} -o #{png_path}"
+ else
+ sh "rsvg-convert #{svg_path} -o #{png_path}"
+ end
+ end
+ end
+
+ desc 'Fail if committed images/ is stale relative to figures/ (run on CI after figures:build)'
+ task :check => :validate do
+ require 'tmpdir'
+ require 'json'
+ Dir.mktmpdir('figures-check') do |tmpdir|
+ # Render all figures into a temp dir and compare with committed images/
+ renderer_env = "FIGURES_DIR=#{FIGURES_DIR} IMAGES_DIR=#{tmpdir}"
+ sh "#{renderer_env} ruby #{RENDERER}", :verbose => false do |ok, _|
+ abort "Renderer failed" unless ok
+ end
+ stale = []
+ FIGURE_SOURCES.each do |json_path|
+ base = File.basename(json_path, '.json')
+ committed = File.join(IMAGES_DIR, "#{base}.svg")
+ rendered = File.join(tmpdir, "#{base}.svg")
+ if File.exist?(committed) && File.exist?(rendered)
+ stale << base if File.read(rendered) != File.read(committed)
+ else
+ stale << base
+ end
+ end
+ if stale.any?
+ stale.each { |b| puts " STALE: #{b}.svg" }
+ abort "#{stale.size} stale figure(s) — run: rake figures:build && git add images/"
+ end
+ puts " All #{FIGURE_SOURCES.size} figure(s) up to date"
+ end
+ end
+
+ desc 'Build a side-by-side review HTML page at tmp/figure-review.html'
+ task :review => :build do
+ require 'fileutils'
+ FileUtils.mkdir_p File.join(__dir__, 'tmp')
+ out = File.join(__dir__, 'tmp', 'figure-review.html')
+
+ rows = FIGURE_SOURCES.sort.map do |json_path|
+ base = File.basename(json_path, '.json')
+ old_png = "file://#{IMAGES_DIR}/#{base}.png"
+ new_svg = "file://#{IMAGES_DIR}/#{base}.svg"
+ master_flag = File.read(json_path).include?('"master"') ? 'contains master' : ''
+ <<~ROW
+
+ | #{base} |
+ #{master_flag} |
+  |
+  |
+
+ ROW
+ end.join
+
+ File.write(out, <<~HTML)
+
+
+ Figure review
+
+
+ Figure review — #{FIGURE_SOURCES.size} figures
+ Left: committed PNG | Right: new SVG render
+
+ | Name | Flags | Old PNG | New SVG |
+ #{rows}
+
+
+ HTML
+ puts " Review page: #{out}"
+ puts " Open with: open tmp/figure-review.html"
+ end
+end
diff --git a/bin/render-figures.rb b/bin/render-figures.rb
new file mode 100755
index 0000000..b98eb3f
--- /dev/null
+++ b/bin/render-figures.rb
@@ -0,0 +1,658 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+# render-figures.rb — renders figures/*.json to images/*.svg
+#
+# Usage:
+# bin/render-figures.rb # render all figures/*.json
+# bin/render-figures.rb figures/foo.json # render one figure
+#
+# Requirements: Ruby stdlib only (json gem already in Gemfile).
+# Output is deterministic: byte-identical across runs given the same input.
+
+require 'json'
+require 'fileutils'
+
+REPO_ROOT = File.expand_path('..', __dir__)
+FIGURES_DIR = ENV.fetch('FIGURES_DIR', File.join(REPO_ROOT, 'figures'))
+IMAGES_DIR = ENV.fetch('IMAGES_DIR', File.join(REPO_ROOT, 'images'))
+PALETTE_FILE = File.join(FIGURES_DIR, '_palette.json')
+
+# ---------------------------------------------------------------------------
+# Palette
+# ---------------------------------------------------------------------------
+
+module Palette
+ DATA = JSON.parse(File.read(PALETTE_FILE))
+
+ def self.color(name)
+ return name if name.start_with?('#')
+ DATA['colors'].fetch(name) { raise "Unknown color: #{name.inspect}" }
+ end
+
+ def self.kind(name)
+ DATA['kinds'].fetch(name) { raise "Unknown kind: #{name.inspect}" }
+ end
+end
+
+# ---------------------------------------------------------------------------
+# Layout — resolves node positions
+# ---------------------------------------------------------------------------
+
+DEFAULT_GRID_X = 127
+DEFAULT_GRID_Y = 62
+DEFAULT_MARGIN = 4
+
+module Layout
+ # Returns { id => {x:, y:, w:, h:} } in absolute canvas coordinates.
+ def self.resolve(spec)
+ layout = spec['layout'] || 'grid'
+ gx = (spec.dig('grid', 'x') || DEFAULT_GRID_X).to_f
+ gy = (spec.dig('grid', 'y') || DEFAULT_GRID_Y).to_f
+ margin = (spec['margin'] || DEFAULT_MARGIN).to_f
+ nodes = spec['nodes'] || []
+
+ positions = {}
+ nodes.each do |n|
+ k = Palette.kind(n['kind'])
+ w = (n['w'] || k['w']).to_f
+ h = (n['h'] || k['h']).to_f
+
+ if layout == 'absolute' || (n.key?('x') && n.key?('y'))
+ x = n['x'].to_f
+ y = n['y'].to_f
+ else
+ col = n.fetch('col') { raise "Node #{n['id'].inspect}: missing 'col' in grid layout" }.to_f
+ row = n.fetch('row') { raise "Node #{n['id'].inspect}: missing 'row' in grid layout" }.to_f
+ x = margin + col * gx
+ y = margin + row * gy
+ end
+
+ x += (n['dx'] || 0).to_f
+ y += (n['dy'] || 0).to_f
+
+ positions[n['id']] = { x: x, y: y, w: w, h: h }
+ end
+ positions
+ end
+
+ # Returns [width, height] for the canvas.
+ def self.canvas_size(spec, positions)
+ return [spec.dig('canvas', 'w').to_f, spec.dig('canvas', 'h').to_f] if spec['canvas']
+
+ margin = (spec['margin'] || DEFAULT_MARGIN).to_f
+ max_x = positions.values.map { |p| p[:x] + p[:w] }.max || 0
+ max_y = positions.values.map { |p| p[:y] + p[:h] }.max || 0
+
+ # Also account for banners, rules, labels, groups
+ [max_x + margin, max_y + margin]
+ end
+end
+
+# ---------------------------------------------------------------------------
+# SVG helpers
+# ---------------------------------------------------------------------------
+
+module SVG
+ FONT_FAMILY = "'JetBrains Mono', 'Source Code Pro', monospace"
+ FONT_SIZE = 12
+ FONT_WEIGHT = 700
+ LINE_HEIGHT = 13.07 # matches existing SVG line-height
+
+ def self.esc(s)
+ s.gsub('&','&').gsub('<','<').gsub('>','>').gsub('"','"')
+ end
+
+ def self.round(v)
+ # Round to 3 decimal places; trim trailing zeros for compactness
+ s = format('%.3f', v)
+ s = s.sub(/\.?0+$/, '') if s.include?('.')
+ s
+ end
+
+ # Baked-arrowhead path, matching existing SVG idiom.
+ # Horizontal left-pointing arrow: shaft starts at (x1,y), apex at to_cx < x1
+ # m{base_x} {cy}h{shaft}v1h-{shaft}v4l-9-4.5 9-4.5z
+ def self.arrow_h(from_cx, to_cx, cy)
+ # Arrowhead at left (to_cx < from_cx): apex at to_cx (box edge), base 9px
+ # further out so the triangle sits outside the target box, not under it.
+ base = round(to_cx + 9)
+ shaft = round(from_cx - to_cx - 9) # 9px for arrowhead
+ "m#{base} #{round(cy)}h#{shaft}v1h-#{shaft}v4l-9-4.5 9-4.5z"
+ end
+
+ # Vertical downward-pointing arrow: tip at (cx, to_cy), shaft from (cx, from_cy)
+ def self.arrow_v_down(cx, from_cy, to_cy)
+ base_y = to_cy - 9
+ shaft = round(base_y - from_cy)
+ "m#{round(cx)} #{round(base_y)}v-#{shaft}h1v#{shaft}h4l-4.5 9-4.5-9h4z"
+ end
+
+ # Forward (downward-pointing connector from ref to commit below):
+ # tip at bottom of ref box, points down
+ def self.arrow_v_down_fwd(cx, from_cy, to_cy)
+ # from_cy = bottom of ref, to_cy = top of commit (arrowhead at to_cy)
+ base_y = to_cy - 9
+ shaft = round(base_y - from_cy)
+ "m#{round(cx)} #{round(base_y)}v-#{shaft}h1v#{shaft}h4l-4.5 9-4.5-9h4z"
+ end
+
+ # Vertical upward-pointing arrow: tip at (cx, to_cy), shaft from (cx, from_cy)
+ def self.arrow_v_up(cx, from_cy, to_cy)
+ base_y = to_cy + 9
+ shaft = round(from_cy - base_y)
+ "m#{round(cx)} #{round(base_y)}v#{shaft}h1v-#{shaft}h4l-4.5-9-4.5 9h4z"
+ end
+
+ # Diagonal arrow: a thin (1px-wide) shaft from (x1,y1) to a point 9px short
+ # of (x2,y2), followed by a filled triangular arrowhead with its apex at
+ # (x2,y2). Built from a unit direction vector and its perpendicular so the
+ # shaft is an actual filled quad (not a zero-width line), matching the
+ # orthogonal arrow idiom used elsewhere in this file.
+ def self.diagonal_arrow(x1, y1, x2, y2, hsize: 9.0, wing: 4.5, half: 0.5)
+ dx = x2 - x1; dy = y2 - y1
+ len = Math.sqrt(dx*dx + dy*dy)
+ ux = dx / len; uy = dy / len
+ px = -uy; py = ux # unit perpendicular
+
+ base_x = x2 - hsize * ux
+ base_y = y2 - hsize * uy
+
+ ax = x1 + half * px; ay = y1 + half * py
+ bx = base_x + half * px; by = base_y + half * py
+ cx = base_x + wing * px; cy = base_y + wing * py
+ ex = base_x - wing * px; ey = base_y - wing * py
+ fx = base_x - half * px; fy = base_y - half * py
+ gx = x1 - half * px; gy = y1 - half * py
+
+ "M#{round(ax)} #{round(ay)} L#{round(bx)} #{round(by)} " \
+ "L#{round(cx)} #{round(cy)} L#{round(x2)} #{round(y2)} " \
+ "L#{round(ex)} #{round(ey)} L#{round(fx)} #{round(fy)} " \
+ "L#{round(gx)} #{round(gy)} Z"
+ end
+
+ # Double-headed diagonal (or horizontal/vertical, since either dx or dy
+ # may be 0) arrow: a shaft between two points, each end trimmed by an
+ # arrowhead with its apex at (x1,y1) and (x2,y2) respectively.
+ def self.diagonal_arrow_both(x1, y1, x2, y2, hsize: 9.0, wing: 4.5, half: 0.5)
+ dx = x2 - x1; dy = y2 - y1
+ len = Math.sqrt(dx*dx + dy*dy)
+ ux = dx / len; uy = dy / len
+ px = -uy; py = ux # unit perpendicular
+
+ base1_x = x1 + hsize * ux; base1_y = y1 + hsize * uy
+ base2_x = x2 - hsize * ux; base2_y = y2 - hsize * uy
+
+ pts = [
+ [x1, y1],
+ [base1_x + wing * px, base1_y + wing * py],
+ [base1_x + half * px, base1_y + half * py],
+ [base2_x + half * px, base2_y + half * py],
+ [base2_x + wing * px, base2_y + wing * py],
+ [x2, y2],
+ [base2_x - wing * px, base2_y - wing * py],
+ [base2_x - half * px, base2_y - half * py],
+ [base1_x - half * px, base1_y - half * py],
+ [base1_x - wing * px, base1_y - wing * py]
+ ]
+
+ "M#{pts.map { |px2, py2| "#{round(px2)} #{round(py2)}" }.join(' L')} Z"
+ end
+
+ # Render a node box (pill or rect) with optional stroke.
+ def self.node_box(n, pos)
+ k = Palette.kind(n['kind'])
+ x = pos[:x]; y = pos[:y]; w = pos[:w]; h = pos[:h]
+ fill = Palette.color(n['fill'] || k['fill'])
+ stroke_color = n['stroke'] || k['stroke']
+
+ attrs = %(x="#{round(x)}" y="#{round(y)}" width="#{round(w)}" height="#{round(h)}")
+
+ if k['shape'] == 'pill'
+ rx = k['rx'] || 16.5
+ attrs += %( rx="#{rx}" ry="#{rx}")
+ end
+
+ stroke_attrs = stroke_color ? %( stroke="#{Palette.color(stroke_color)}" fill="none" ) : ''
+ inner = %()
+
+ label = n['label']
+ lines = Array(label)
+ text_fill = Palette.color(n['text'] || k['text'])
+ cx = round(x + w / 2.0)
+
+ if lines.size == 1
+ # 21 = 16.5 (centre) + 4.5 (cap-height offset) — matches existing SVG baseline
+ baseline_y = round(y + 21)
+ tspan = %(#{esc(lines.first)})
+ else
+ # Multi-line: vertically centre the text block.
+ # total height = (n-1)*LINE_HEIGHT + cap_height. cap_height ≈ FONT_SIZE*0.7 = 8.4px
+ cap_h = FONT_SIZE * 0.7
+ total_h = (lines.size - 1) * LINE_HEIGHT + cap_h
+ first_y = round(y + (h + cap_h) / 2.0 - (lines.size - 1) * LINE_HEIGHT / 2.0)
+ tspan = lines.each_with_index.map do |line, i|
+ dy_attr = i == 0 ? "y=\"#{first_y}\"" : "dy=\"#{LINE_HEIGHT}\""
+ %(#{esc(line)})
+ end.join
+ end
+
+ text_el = %(#{tspan})
+
+ "\n #{inner}\n #{text_el}\n"
+ end
+
+ # Render a git-object "content" box: a large rect with a hash label above
+ # it, a monospace key/value table (rows), and optional plain body text
+ # below. Used for the handful of figures (e.g. commit-and-tree) that show
+ # actual object contents rather than the small labeled symbol used
+ # elsewhere for commit/tree/blob nodes.
+ def self.object_box(n, pos)
+ k = Palette.kind(n['kind'])
+ x = pos[:x]; y = pos[:y]; w = pos[:w]
+ fill = Palette.color(n['fill'] || k['fill'])
+ text_fill = Palette.color(n['text'] || k['text'])
+ cx = round(x + w / 2.0)
+ pad = 16.0
+ align = n['align'] || 'left'
+
+ parts = [%()]
+
+ if n['hash']
+ parts << %(#{esc(n['hash'])})
+ end
+
+ rows = n['rows'] || []
+ tspans = []
+ if align == 'center'
+ row = rows.first
+ baseline_y = round(y + 23)
+ tspans << %(#{esc(row[0])}) +
+ %(#{esc(row[1])})
+ else
+ key_w = rows.map { |k2, _| k2.length }.max || 0
+ rows.each_with_index do |(key, value), i|
+ line_x = round(x + pad)
+ line_y = round(y + 23 + i * LINE_HEIGHT)
+ padded = ' ' * (key_w - key.length)
+ if i == 0
+ tspans << %(#{esc(key)}#{esc(padded)} #{esc(value)})
+ else
+ tspans << %(#{esc(key)}#{esc(padded)} #{esc(value)})
+ end
+ end
+ end
+
+ body = n['body'] || []
+ unless body.empty?
+ body_font_size = 9.33
+ body_line_height = 10.0
+ last_row_y = 23 + [rows.size - 1, 0].max * LINE_HEIGHT
+ body_start = last_row_y + 24
+ body.each_with_index do |line, i|
+ line_x = round(x + pad)
+ line_y = round(y + body_start + i * body_line_height)
+ tspans << %(#{esc(line)})
+ end
+ end
+
+ text_el = %(#{tspans.join})
+ parts << text_el
+
+ "\n #{parts.join("\n ")}\n"
+ end
+
+ # Wide banner arrow with inset text.
+ # Direction: horizontal only (left or right depending on from/to x).
+ def self.banner(b)
+ fx = b['from']['x'].to_f; fy = b['from']['y'].to_f
+ tx = b['to']['x'].to_f; ty = b['to']['y'].to_f
+ thickness = (b['thickness'] || 22).to_f
+ half = thickness / 2.0
+ label = b['label']
+ head = 9.0 # arrowhead protrusion
+
+ # Horizontal only for now
+ going_right = tx > fx
+ if going_right
+ shaft_x = fx; tip_x = tx
+ d = "M#{round(shaft_x)} #{round(fy - half)}" \
+ "H#{round(tip_x - head)}" \
+ "V#{round(fy - half - 4)}" \
+ "L#{round(tip_x)} #{round(fy)}" \
+ "L#{round(tip_x - head)} #{round(fy + half + 4)}" \
+ "V#{round(fy + half)}" \
+ "H#{round(shaft_x)}Z"
+ text_x = round((shaft_x + tip_x - head) / 2.0)
+ else
+ shaft_x = tx; tip_x = fx
+ d = "M#{round(shaft_x + head)} #{round(fy - half - 4)}" \
+ "L#{round(shaft_x)} #{round(fy)}" \
+ "L#{round(shaft_x + head)} #{round(fy + half + 4)}" \
+ "V#{round(fy + half)}" \
+ "H#{round(tip_x)}" \
+ "V#{round(fy - half)}" \
+ "H#{round(shaft_x + head)}Z"
+ text_x = round((shaft_x + head + tip_x) / 2.0)
+ end
+
+ baseline_y = round(fy + FONT_SIZE * 0.4)
+ text_el = %(#{esc(label)})
+
+ %(\n \n #{text_el}\n)
+ end
+
+ # Vertical rule line
+ def self.rule(r)
+ if r.key?('x')
+ x = round(r['x'].to_f)
+ y1 = round((r['y1'] || 0).to_f)
+ y2 = round((r['y2'] || 0).to_f)
+ %()
+ else
+ y = round(r['y'].to_f)
+ x1 = round((r['x1'] || 0).to_f)
+ x2 = round((r['x2'] || 0).to_f)
+ %()
+ end
+ end
+
+ # Free-floating text label
+ def self.floating_label(l)
+ lines = Array(l['text'])
+ x = round(l['x'].to_f); y = round(l['y'].to_f)
+ anchor = l['anchor'] || 'middle'
+ fill = l['fill'] ? Palette.color(l['fill']) : Palette.color('commit-text')
+ size = l['size'] || FONT_SIZE
+
+ if lines.size == 1
+ %(#{esc(lines.first)})
+ else
+ tspans = lines.each_with_index.map do |line, i|
+ dy = i == 0 ? "y=\"#{y}\"" : "dy=\"#{LINE_HEIGHT}\""
+ %(#{esc(line)})
+ end.join
+ %(#{tspans})
+ end
+ end
+
+ # Background group / swimlane box
+ def self.group_box(g)
+ x = round(g['x'].to_f); y = round(g['y'].to_f)
+ w = round(g['w'].to_f); h = round(g['h'].to_f)
+ fill = g['fill'] ? Palette.color(g['fill']) : 'none'
+ stroke = g['stroke'] ? Palette.color(g['stroke']) : Palette.color('rule')
+ inner = %()
+
+ if g['label']
+ text_el = %(#{esc(g['label'])})
+ "\n #{inner}\n #{text_el}\n"
+ else
+ inner
+ end
+ end
+end
+
+# ---------------------------------------------------------------------------
+# Edge routing
+# ---------------------------------------------------------------------------
+
+module Edges
+ def self.render(edge, positions)
+ from_id = edge['from']; to_id = edge['to']
+ fp = positions.fetch(from_id) { raise "Edge refers to unknown node: #{from_id.inspect}" }
+ tp = positions.fetch(to_id) { raise "Edge refers to unknown node: #{to_id.inspect}" }
+ route = edge['route'] || 'straight'
+ arrow = edge['arrow'] || 'back'
+ muted = edge['style'] == 'muted'
+
+ from_cx = fp[:x] + fp[:w] / 2.0
+ from_cy = fp[:y] + fp[:h] / 2.0
+ to_cx = tp[:x] + tp[:w] / 2.0
+ to_cy = tp[:y] + tp[:h] / 2.0
+
+ return render_none(fp, tp) if arrow == 'none'
+ return render_both(fp, tp, muted) if arrow == 'both'
+
+ case route
+ when 'diagonal'
+ # Connect edge of 'from' box to edge of 'to' box along the diagonal
+ dx = to_cx - from_cx; dy = to_cy - from_cy
+ len = Math.sqrt(dx*dx + dy*dy)
+ ux = dx / len; uy = dy / len
+ # Start from the 'from' box edge
+ half_from_w = fp[:w] / 2.0; half_from_h = fp[:h] / 2.0
+ t_from = [half_from_w / ux.abs, half_from_h / uy.abs].min rescue half_from_w
+ x1 = from_cx + ux * t_from; y1 = from_cy + uy * t_from
+ # End at the 'to' box edge
+ half_to_w = tp[:w] / 2.0; half_to_h = tp[:h] / 2.0
+ t_to = [half_to_w / ux.abs, half_to_h / uy.abs].min rescue half_to_w
+ x2 = to_cx - ux * t_to; y2 = to_cy - uy * t_to
+
+ if muted
+ d = SVG.diagonal_arrow(x1, y1, x2, y2, hsize: 6.0, wing: 3.0, half: 0.35)
+ %()
+ else
+ d = SVG.diagonal_arrow(x1, y1, x2, y2)
+ %()
+ end
+
+ when 'straight'
+ # Determine primary direction: horizontal or vertical
+ horiz = (to_cy - from_cy).abs < (to_cx - from_cx).abs
+
+ if horiz
+ if arrow == 'forward'
+ # 'from' box right edge → 'to' box left center: forward arrow
+ x1 = fp[:x] + fp[:w]
+ x2 = tp[:x]
+ cy = to_cy
+ base_x = x2 - 9
+ shaft = SVG.round(base_x - x1)
+ d = "m#{SVG.round(base_x)} #{SVG.round(cy)}h-#{shaft}v1h#{shaft}v4l9-4.5-9-4.5v4z"
+ else
+ # back arrow: tip at 'to' side, shaft from 'from' side
+ # from.right → gap → to.right+arrowhead tip
+ x1 = fp[:x] # left of from-box = shaft start going left is from right to left
+ x2 = tp[:x] + tp[:w] # right of to-box is where arrowhead tip is
+ cy = to_cy
+ d = SVG.arrow_h(fp[:x], tp[:x] + tp[:w], cy - 0.5)
+ end
+ else
+ # Vertical — determine actual direction from box positions
+ going_down = tp[:y] > fp[:y]
+ if arrow == 'forward'
+ if going_down
+ # from-box bottom → to-box top, arrowhead points down
+ y1 = fp[:y] + fp[:h]
+ y2 = tp[:y]
+ d = SVG.arrow_v_down_fwd(from_cx, y1, y2)
+ else
+ # from-box top → to-box bottom, arrowhead points up
+ y1 = fp[:y] # top of from-box (shaft start)
+ y2 = tp[:y] + tp[:h] # bottom of to-box (arrowhead tip)
+ d = SVG.arrow_v_up(from_cx, y1, y2)
+ end
+ else
+ # back arrow: arrowhead at 'to' node, pointing away from 'from'
+ if going_down
+ # to is below from: tip at top of to-box, pointing down from from-bottom
+ y1 = fp[:y] + fp[:h] # bottom of from
+ y2 = tp[:y] # top of to
+ d = SVG.arrow_v_down(from_cx, y1, y2)
+ else
+ # to is above from: tip at bottom of to-box, pointing up
+ y1 = fp[:y] # top of from
+ y2 = tp[:y] + tp[:h] # bottom of to
+ d = SVG.arrow_v_up(from_cx, y1, y2)
+ end
+ end
+ end
+
+ %()
+
+ when 'elbow'
+ # Simple elbow: horizontal then vertical
+ # From right edge of from-box to top/bottom of to-box
+ mid_x = (fp[:x] + fp[:w] + tp[:x] + tp[:w] / 2.0) / 2.0
+ # TODO: implement elbow routing; falling back to diagonal for now
+ dx = to_cx - from_cx; dy = to_cy - from_cy
+ x1 = fp[:x] + fp[:w]; y1 = from_cy
+ x2 = tp[:x] + tp[:w] / 2.0; y2 = tp[:y]
+ d = SVG.diagonal_arrow(x1, y1, x2, y2)
+ %()
+ end
+ end
+
+ # Plain connecting line between the facing edges of the two boxes, with no
+ # arrowhead at either end. Works for any relative position (horizontal,
+ # vertical, or diagonal) since it uses the same edge-intersection math as
+ # render_both.
+ def self.render_none(fp, tp)
+ from_cx = fp[:x] + fp[:w] / 2.0; from_cy = fp[:y] + fp[:h] / 2.0
+ to_cx = tp[:x] + tp[:w] / 2.0; to_cy = tp[:y] + tp[:h] / 2.0
+
+ dx = to_cx - from_cx; dy = to_cy - from_cy
+ len = Math.sqrt(dx*dx + dy*dy)
+ ux = dx / len; uy = dy / len
+
+ half_from_w = fp[:w] / 2.0; half_from_h = fp[:h] / 2.0
+ t_from = ux.zero? ? half_from_h / uy.abs : (uy.zero? ? half_from_w / ux.abs : [half_from_w / ux.abs, half_from_h / uy.abs].min)
+ x1 = from_cx + ux * t_from; y1 = from_cy + uy * t_from
+
+ half_to_w = tp[:w] / 2.0; half_to_h = tp[:h] / 2.0
+ t_to = ux.zero? ? half_to_h / uy.abs : (uy.zero? ? half_to_w / ux.abs : [half_to_w / ux.abs, half_to_h / uy.abs].min)
+ x2 = to_cx - ux * t_to; y2 = to_cy - uy * t_to
+
+ %()
+ end
+
+ # Double-headed arrow between the facing edges of the two boxes. Works for
+ # any relative position (horizontal, vertical, or diagonal) since the
+ # underlying arrow shape is direction-agnostic.
+ def self.render_both(fp, tp, muted)
+ from_cx = fp[:x] + fp[:w] / 2.0; from_cy = fp[:y] + fp[:h] / 2.0
+ to_cx = tp[:x] + tp[:w] / 2.0; to_cy = tp[:y] + tp[:h] / 2.0
+
+ dx = to_cx - from_cx; dy = to_cy - from_cy
+ len = Math.sqrt(dx*dx + dy*dy)
+ ux = dx / len; uy = dy / len
+
+ half_from_w = fp[:w] / 2.0; half_from_h = fp[:h] / 2.0
+ t_from = ux.zero? ? half_from_h / uy.abs : (uy.zero? ? half_from_w / ux.abs : [half_from_w / ux.abs, half_from_h / uy.abs].min)
+ x1 = from_cx + ux * t_from; y1 = from_cy + uy * t_from
+
+ half_to_w = tp[:w] / 2.0; half_to_h = tp[:h] / 2.0
+ t_to = ux.zero? ? half_to_h / uy.abs : (uy.zero? ? half_to_w / ux.abs : [half_to_w / ux.abs, half_to_h / uy.abs].min)
+ x2 = to_cx - ux * t_to; y2 = to_cy - uy * t_to
+
+ if muted
+ d = SVG.diagonal_arrow_both(x1, y1, x2, y2, hsize: 6.0, wing: 3.0, half: 0.35)
+ %()
+ else
+ d = SVG.diagonal_arrow_both(x1, y1, x2, y2)
+ %()
+ end
+ end
+end
+
+# ---------------------------------------------------------------------------
+# Top-level renderer
+# ---------------------------------------------------------------------------
+
+def render(spec)
+ positions = Layout.resolve(spec)
+ cw, ch = Layout.canvas_size(spec, positions)
+ nodes = spec['nodes'] || []
+ edges = spec['edges'] || []
+ banners = spec['banners'] || []
+ rules = spec['rules'] || []
+ labels = spec['labels'] || []
+ groups = spec['groups'] || []
+ title = spec['title'] || ''
+
+ parts = []
+
+ # Groups (background, drawn first)
+ groups.each { |g| parts << SVG.group_box(g) }
+
+ # Rules
+ rules.each { |r| parts << SVG.rule(r) }
+
+ # Banners
+ banners.each { |b| parts << SVG.banner(b) }
+
+ # Edges (drawn before nodes so nodes appear on top)
+ edges.each do |e|
+ parts << Edges.render(e, positions)
+ end
+
+ # Nodes
+ nodes.each do |n|
+ if n['kind'].end_with?('-full')
+ parts << SVG.object_box(n, positions[n['id']])
+ else
+ parts << SVG.node_box(n, positions[n['id']])
+ end
+ end
+
+ # Free-floating labels
+ labels.each { |l| parts << SVG.floating_label(l) }
+
+ vw = SVG.round(cw); vh = SVG.round(ch)
+ body = parts.map { |p| p.each_line.map { |l| " #{l}" }.join }.join("\n")
+
+ # Deterministic, minimal SVG — no generator comment, no timestamps, no random ids.
+ # width/height at 2x for crisp browser rendering; CSS can override to scale.
+ w2x = SVG.round(cw * 2); h2x = SVG.round(ch * 2)
+ <<~SVG
+
+
+ SVG
+end
+
+# ---------------------------------------------------------------------------
+# CLI entry point
+# ---------------------------------------------------------------------------
+
+def main(argv)
+ targets = if argv.empty?
+ Dir[File.join(FIGURES_DIR, '*.json')].reject { |f| File.basename(f).start_with?('_') }.sort
+ else
+ argv
+ end
+
+ targets.each do |json_path|
+ begin
+ spec = JSON.parse(File.read(json_path))
+ svg_data = render(spec)
+ base = File.basename(json_path, '.json')
+ out_path = File.join(IMAGES_DIR, "#{base}.svg")
+ File.write(out_path, svg_data)
+ puts " rendered #{base}.svg"
+ rescue => e
+ warn "ERROR rendering #{json_path}: #{e.message}"
+ warn e.backtrace.first(3).join("\n")
+ exit 1
+ end
+ end
+end
+
+main(ARGV) if $PROGRAM_NAME == __FILE__
diff --git a/bin/svg-to-figure.rb b/bin/svg-to-figure.rb
new file mode 100755
index 0000000..86454fb
--- /dev/null
+++ b/bin/svg-to-figure.rb
@@ -0,0 +1,398 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+# svg-to-figure.rb — bootstrap JSON figure specs from existing images/*.svg
+#
+# Usage:
+# bin/svg-to-figure.rb # process all images/*.svg
+# bin/svg-to-figure.rb images/basic-branching-3.svg
+#
+# Outputs figures/.json for each SVG that doesn't already have one.
+# Existing figures/*.json are never overwritten.
+#
+# Also emits tmp/extractor-report.json with per-figure residuals and TODO items
+# so the review harness can order figures by difficulty.
+
+require 'json'
+require 'rexml/document'
+require 'fileutils'
+
+REPO_ROOT = File.expand_path('..', __dir__)
+FIGURES_DIR = File.join(REPO_ROOT, 'figures')
+IMAGES_DIR = File.join(REPO_ROOT, 'images')
+PALETTE_FILE = File.join(FIGURES_DIR, '_palette.json')
+REPORT_FILE = File.join(REPO_ROOT, 'tmp', 'extractor-report.json')
+
+PALETTE = JSON.parse(File.read(PALETTE_FILE))
+
+# ---------------------------------------------------------------------------
+# Transform math — flatten nested SVG transform attributes to absolute coords
+# ---------------------------------------------------------------------------
+
+# Parse a single transform string into a 3x3 affine matrix [a,b,c,d,e,f]
+# (same as SVG matrix(a b c d e f), applied as: x' = ax+cy+e, y' = bx+dy+f)
+def parse_transform(str)
+ return [1,0,0,1,0,0] if str.nil? || str.strip.empty?
+ str = str.strip
+ case str
+ when /^translate\(\s*([-\d.]+)[\s,]+([-\d.]+)\s*\)/
+ [1, 0, 0, 1, $1.to_f, $2.to_f]
+ when /^translate\(\s*([-\d.]+)\s*\)/
+ [1, 0, 0, 1, $1.to_f, 0]
+ when /^scale\(\s*([-\d.]+)[\s,]+([-\d.]+)\s*\)/
+ [$1.to_f, 0, 0, $2.to_f, 0, 0]
+ when /^scale\(\s*([-\d.]+)\s*\)/
+ s = $1.to_f; [s, 0, 0, s, 0, 0]
+ when /^matrix\(\s*([-\d.]+)[\s,]+([-\d.]+)[\s,]+([-\d.]+)[\s,]+([-\d.]+)[\s,]+([-\d.]+)[\s,]+([-\d.]+)\s*\)/
+ [$1, $2, $3, $4, $5, $6].map(&:to_f)
+ else
+ [1, 0, 0, 1, 0, 0]
+ end
+end
+
+# Multiply two affine matrices
+def mul_matrix(m1, m2)
+ a1,b1,c1,d1,e1,f1 = m1
+ a2,b2,c2,d2,e2,f2 = m2
+ [
+ a1*a2 + c1*b2,
+ b1*a2 + d1*b2,
+ a1*c2 + c1*d2,
+ b1*c2 + d1*d2,
+ a1*e2 + c1*f2 + e1,
+ b1*e2 + d1*f2 + f1
+ ]
+end
+
+# Apply matrix to a point
+def apply_matrix(m, x, y)
+ a,b,c,d,e,f = m
+ [a*x + c*y + e, b*x + d*y + f]
+end
+
+# Walk the REXML tree and collect all rects/paths/texts with their absolute coords.
+# Returns an array of hashes.
+def collect_elements(node, parent_matrix = [1,0,0,1,0,0], results = [])
+ node.elements.each do |el|
+ local_tr = parse_transform(el.attributes['transform'])
+ matrix = mul_matrix(parent_matrix, local_tr)
+
+ case el.name
+ when 'rect'
+ x = el.attributes['x'].to_f
+ y = el.attributes['y'].to_f
+ w = el.attributes['width'].to_f
+ h = el.attributes['height'].to_f
+ rx = el.attributes['rx'].to_f
+ style = el.attributes['style'] || ''
+ fill = extract_fill(el, style)
+ stroke = extract_stroke(el, style)
+
+ ax, ay = apply_matrix(matrix, x, y)
+ # Also transform width/height to handle scale()
+ aw = w * matrix[0].abs
+ ah = h * matrix[3].abs
+
+ results << { type: :rect, x: ax, y: ay, w: aw, h: ah, rx: rx, fill: fill, stroke: stroke, matrix: matrix }
+
+ when 'text'
+ text_content = el.texts.map(&:value).join +
+ el.elements.map { |c| c.texts.map(&:value).join }.join
+ tspans = []
+ el.elements.each('tspan') { |ts| tspans << ts.text.to_s.strip }
+ text_content = tspans.any? ? tspans.join("\n") : text_content.strip
+ # text position from x/y attrs or tspan
+ tx = (el.attributes['x'] || el.elements['tspan']&.attributes['x']).to_f
+ ty = (el.attributes['y'] || el.elements['tspan']&.attributes['y']).to_f
+ atx, aty = apply_matrix(matrix, tx, ty)
+ results << { type: :text, x: atx, y: aty, content: text_content, lines: tspans }
+ end
+
+ collect_elements(el, matrix, results) if el.elements.size > 0
+ end
+ results
+end
+
+def extract_fill(el, style)
+ if (m = style.match(/(?:^|;)fill:\s*([^;]+)/))
+ m[1].strip
+ elsif el.attributes['fill']
+ el.attributes['fill']
+ else
+ nil
+ end
+end
+
+def extract_stroke(el, style)
+ if (m = style.match(/(?:^|;)stroke:\s*([^;]+)/))
+ v = m[1].strip
+ v unless v == 'none'
+ elsif el.attributes['stroke']
+ v = el.attributes['stroke']
+ v unless v == 'none'
+ else
+ nil
+ end
+end
+
+# ---------------------------------------------------------------------------
+# Node classification — map (w, h, fill) → kind
+# ---------------------------------------------------------------------------
+
+COLOR_ALIASES = {
+ '#efefe7' => 'commit', '#f44d27' => 'ref', '#00909a' => 'tree',
+ '#cd9f00' => 'blob', '#b8e986' => 'jessica', '#fcc161' => 'john',
+ '#deb9ff' => 'josie', 'none' => nil, '#f0f0f0' => nil
+}.freeze
+
+# Normalise a fill value to a palette color name or nil
+def classify_fill(fill_raw)
+ return nil unless fill_raw
+ f = fill_raw.downcase.strip
+ return nil if f == 'none' || f == 'transparent'
+ # Direct match
+ return COLOR_ALIASES[f] if COLOR_ALIASES.key?(f)
+ # Check palette colors
+ PALETTE['colors'].each do |name, hex|
+ return name if hex.downcase == f
+ end
+ nil
+end
+
+def classify_rect(r)
+ fill_name = classify_fill(r[:fill])
+ w = r[:w].round; h = r[:h].round
+ rx = r[:rx].round
+
+ # Try to match palette kinds by (w, h, shape)
+ PALETTE['kinds'].each do |kind, spec|
+ kw = spec['w'].to_i; kh = spec['h'].to_i
+ next unless (kw - w).abs <= 2 && (kh - h).abs <= 2
+ kfill = spec['fill']
+ # Match by fill or shape
+ if fill_name && (fill_name == kfill || PALETTE['colors'][kfill] == r[:fill]&.downcase)
+ return kind
+ end
+ # Fallback: match by size + shape type
+ pill = (spec['shape'] == 'pill')
+ if pill && rx >= 10 && fill_name == kfill
+ return kind
+ end
+ end
+
+ # Heuristic fallbacks
+ return 'commit' if fill_name == 'commit' || r[:fill]&.downcase == '#efefe7'
+ return 'ref' if fill_name == 'ref' || r[:fill]&.downcase == '#f44d27'
+ return 'tree' if fill_name == 'tree' || r[:fill]&.downcase == '#00909a'
+ return 'blob' if fill_name == 'blob' || r[:fill]&.downcase == '#cd9f00'
+ return 'jessica' if r[:fill]&.downcase == '#b8e986'
+ return 'john' if r[:fill]&.downcase == '#fcc161'
+ return 'josie' if r[:fill]&.downcase == '#deb9ff'
+ return 'other' if fill_name == 'commit' && r[:stroke]
+
+ nil # unclassified
+end
+
+# ---------------------------------------------------------------------------
+# Grid inference — cluster x/y centers and fit a pitch
+# ---------------------------------------------------------------------------
+
+# Cluster a sorted array of values into groups within `tolerance`
+def cluster(vals, tolerance = 8.0)
+ return [] if vals.empty?
+ groups = [[vals.first]]
+ vals[1..].each do |v|
+ if v - groups.last.last <= tolerance
+ groups.last << v
+ else
+ groups << [v]
+ end
+ end
+ groups.map { |g| g.sum / g.size.to_f }
+end
+
+# Given cluster centers, infer a grid pitch and origin
+def infer_grid(centers)
+ return { origin: centers.first, pitch: nil } if centers.size < 2
+ diffs = centers.each_cons(2).map { |a, b| b - a }
+ pitch = diffs.sum / diffs.size.to_f
+ { origin: centers.first, pitch: pitch.round(1) }
+end
+
+# Snap a value to a grid and return (col_or_row, residual)
+def snap_to_grid(val, origin, pitch)
+ return [0, val - origin] unless pitch && pitch > 0
+ col = ((val - origin) / pitch).round
+ residual = val - (origin + col * pitch)
+ [col, residual]
+end
+
+# ---------------------------------------------------------------------------
+# Main extractor
+# ---------------------------------------------------------------------------
+
+def extract_figure(svg_path)
+ name = File.basename(svg_path, '.svg')
+ doc = REXML::Document.new(File.read(svg_path))
+ root = doc.root
+
+ # Get the root viewBox
+ vb = root.attributes['viewBox']
+ canvas_w, canvas_h = if vb && (m = vb.match(/[\d.]+\s+[\d.]+\s+([\d.]+)\s+([\d.]+)/))
+ [m[1].to_f, m[2].to_f]
+ else
+ [nil, nil]
+ end
+
+ elements = collect_elements(root)
+
+ # Separate rects and texts
+ rects = elements.select { |e| e[:type] == :rect }
+ texts = elements.select { |e| e[:type] == :text }
+
+ # Classify each rect to a node kind, filter out unclassified
+ node_rects = rects.filter_map do |r|
+ kind = classify_rect(r)
+ next unless kind
+ { kind: kind, x: r[:x], y: r[:y], w: r[:w], h: r[:h] }
+ end
+
+ # Match each node rect to nearby text
+ nodes = node_rects.map.with_index do |nr, i|
+ cx = nr[:x] + nr[:w] / 2.0
+ cy = nr[:y] + nr[:h] / 2.0
+ # Find text whose absolute x is nearest the node center and y within the node
+ best = texts.min_by do |t|
+ x_dist = (t[:x] - cx).abs
+ in_box = t[:y] >= nr[:y] - 5 && t[:y] <= nr[:y] + nr[:h] + 5
+ in_box ? x_dist : 1e9
+ end
+ label = if best && (best[:x] - cx).abs < nr[:w]
+ best[:lines].size > 1 ? best[:lines] : best[:content].strip
+ else
+ "node#{i}"
+ end
+ { id: "node#{i}", kind: nr[:kind], label: label, x: nr[:x].round(2), y: nr[:y].round(2),
+ w: nr[:w].round(2), h: nr[:h].round(2), _cx: cx, _cy: cy }
+ end
+
+ # Assign stable ids based on label content
+ nodes.each do |n|
+ lbl = Array(n[:label]).first.to_s.strip
+ n[:id] = lbl.gsub(/[^a-z0-9]/i, '-').downcase.gsub(/-+/, '-').gsub(/^-|-$/, '')
+ n[:id] = "node_#{n[:id]}" if n[:id].empty? || n[:id].match?(/^\d/)
+ end
+ # Deduplicate ids
+ seen = Hash.new(0)
+ nodes.each do |n|
+ count = seen[n[:id]] += 1
+ n[:id] = "#{n[:id]}-#{count}" if count > 1
+ end
+
+ # Grid inference
+ xs = nodes.map { |n| n[:_cx] }.sort
+ ys = nodes.map { |n| n[:_cy] }.sort
+ x_centers = cluster(xs)
+ y_centers = cluster(ys)
+ x_grid = infer_grid(x_centers)
+ y_grid = infer_grid(y_centers)
+
+ # Snap nodes to grid
+ max_residual = 0.0
+ nodes.each do |n|
+ col, rx = snap_to_grid(n[:_cx], x_grid[:origin], x_grid[:pitch])
+ row, ry = snap_to_grid(n[:_cy], y_grid[:origin], y_grid[:pitch])
+ n[:col] = col; n[:row] = row
+ n[:dx] = rx.round(2) unless rx.abs < 0.5
+ n[:dy] = ry.round(2) unless ry.abs < 0.5
+ max_residual = [max_residual, rx.abs, ry.abs].max
+ end
+
+ # Compute grid pitch; fall back to per-node absolute coords if no pitch
+ use_grid = x_grid[:pitch] && y_grid[:pitch] && nodes.size > 1
+ grid_x = x_grid[:pitch]&.round(1)
+ grid_y = y_grid[:pitch]&.round(1)
+
+ # Build JSON spec
+ json_nodes = nodes.map do |n|
+ nd = { 'id' => n[:id], 'kind' => n[:kind], 'label' => n[:label] }
+ if use_grid
+ nd['col'] = n[:col]; nd['row'] = n[:row]
+ nd['dx'] = n[:dx] if n[:dx]
+ nd['dy'] = n[:dy] if n[:dy]
+ else
+ nd['x'] = n[:x]; nd['y'] = n[:y]
+ end
+ nd
+ end
+
+ spec = { '$schema' => './_schema.json', 'title' => name.gsub('-', ' ') }
+ if use_grid
+ spec['grid'] = { 'x' => grid_x, 'y' => grid_y } if grid_x && grid_y
+ else
+ spec['layout'] = 'absolute'
+ spec['canvas'] = { 'w' => canvas_w&.round(2), 'h' => canvas_h&.round(2) }
+ end
+ spec['nodes'] = json_nodes
+ spec['edges'] = [] # TODO: edge detection not yet implemented
+
+ todos = []
+ todos << "max grid residual #{max_residual.round(1)}px — review node positions" if max_residual > 3
+ todos << "no grid pitch inferred — using absolute coords" unless use_grid
+ todos << "edge detection not implemented — add edges manually"
+ todos << "canvas size: #{canvas_w&.round}x#{canvas_h&.round}"
+
+ { spec: spec, name: name, max_residual: max_residual.round(2), todos: todos, node_count: nodes.size }
+end
+
+# ---------------------------------------------------------------------------
+# CLI
+# ---------------------------------------------------------------------------
+
+FileUtils.mkdir_p(FIGURES_DIR)
+FileUtils.mkdir_p(File.join(REPO_ROOT, 'tmp'))
+
+report_only = ARGV.delete('--report')
+svg_args = ARGV.reject { |a| a.start_with?('--') }
+
+targets = if svg_args.empty?
+ Dir[File.join(IMAGES_DIR, '*.svg')].sort
+else
+ svg_args
+end
+# Skip the legend/stencil sheet — it's not a figure
+targets.reject! { |p| File.basename(p, '.svg') == 'symbols' }
+
+report = []
+
+targets.each do |svg_path|
+ name = File.basename(svg_path, '.svg')
+ out_path = File.join(FIGURES_DIR, "#{name}.json")
+
+ begin
+ result = extract_figure(svg_path)
+ report << { name: name, nodes: result[:node_count], max_residual: result[:max_residual], todos: result[:todos] }
+
+ if File.exist?(out_path)
+ puts " skip #{name} (already exists, residual #{result[:max_residual]}px)"
+ next
+ end
+
+ next if report_only
+
+ spec = result[:spec]
+ json = JSON.pretty_generate(spec)
+ File.write(out_path, json + "\n")
+ flag = result[:max_residual] > 3 ? ' ⚠' : ''
+ puts " extracted #{name} (#{result[:node_count]} nodes, residual #{result[:max_residual]}px)#{flag}"
+ rescue => e
+ warn " ERROR #{name}: #{e.message}"
+ warn e.backtrace.first(3).join("\n")
+ report << { name: name, error: e.message }
+ end
+end
+
+# Write report sorted by max_residual descending (hard cases first)
+report.sort_by! { |r| -(r[:max_residual] || 999) }
+File.write(REPORT_FILE, JSON.pretty_generate(report))
+puts "\nReport: #{REPORT_FILE}"
diff --git a/figures/_palette.json b/figures/_palette.json
new file mode 100644
index 0000000..dd990e9
--- /dev/null
+++ b/figures/_palette.json
@@ -0,0 +1,31 @@
+{
+ "colors": {
+ "commit": "#efefe7",
+ "commit-text": "gray",
+ "tree": "#00909a",
+ "blob": "#cd9f00",
+ "ref": "#f44d27",
+ "jessica": "#b8e986",
+ "john": "#fcc161",
+ "josie": "#deb9ff",
+ "light-text": "#f0f0f0",
+ "line": "#8f8981",
+ "rule": "#979797",
+ "hash": "#413932"
+ },
+ "kinds": {
+ "commit": { "shape": "pill", "w": 89, "h": 33, "rx": 16.5, "fill": "commit", "text": "commit-text" },
+ "tree": { "shape": "pill", "w": 89, "h": 33, "rx": 16.5, "fill": "tree", "text": "light-text" },
+ "blob": { "shape": "pill", "w": 89, "h": 33, "rx": 16.5, "fill": "blob", "text": "light-text" },
+ "commit-full": { "shape": "rect", "w": 205, "h": 98, "fill": "commit", "text": "commit-text" },
+ "tree-full": { "shape": "rect", "w": 205, "h": 98, "fill": "tree", "text": "light-text" },
+ "blob-full": { "shape": "rect", "w": 205, "h": 98, "fill": "blob", "text": "light-text" },
+ "jessica": { "shape": "pill", "w": 89, "h": 33, "rx": 16.5, "fill": "jessica","text": "commit-text" },
+ "john": { "shape": "pill", "w": 89, "h": 33, "rx": 16.5, "fill": "john", "text": "commit-text" },
+ "josie": { "shape": "pill", "w": 89, "h": 33, "rx": 16.5, "fill": "josie", "text": "commit-text" },
+ "ref": { "shape": "rect", "w": 89, "h": 33, "fill": "ref", "text": "light-text" },
+ "symref": { "shape": "rect", "w": 86, "h": 33, "fill": "tree", "text": "light-text" },
+ "goldref": { "shape": "rect", "w": 108, "h": 33, "fill": "blob", "text": "light-text" },
+ "other": { "shape": "rect", "w": 89, "h": 33, "fill": "commit", "text": "commit-text", "stroke": "rule" }
+ }
+}
diff --git a/figures/_schema.json b/figures/_schema.json
new file mode 100644
index 0000000..02c3cfa
--- /dev/null
+++ b/figures/_schema.json
@@ -0,0 +1,209 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/progit/progit3/figures/_schema.json",
+ "title": "Pro Git figure spec",
+ "description": "Source format for a Pro Git diagram figure. Rendered to SVG by bin/render-figures.rb.",
+ "type": "object",
+ "required": ["title"],
+ "additionalProperties": false,
+ "properties": {
+ "$schema": { "type": "string" },
+ "title": {
+ "description": "Figure title, emitted as SVG for accessibility.",
+ "type": "string"
+ },
+ "layout": {
+ "description": "Positioning mode. 'grid' (default) places nodes by col/row on a regular grid. 'absolute' uses x/y coordinates directly.",
+ "type": "string",
+ "enum": ["grid", "absolute"],
+ "default": "grid"
+ },
+ "grid": {
+ "description": "Grid pitch in pixels. Overrides the default 127x58.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "x": { "type": "number", "exclusiveMinimum": 0 },
+ "y": { "type": "number", "exclusiveMinimum": 0 }
+ }
+ },
+ "margin": {
+ "description": "Canvas margin in pixels around all content. Default 4.",
+ "type": "number",
+ "default": 4
+ },
+ "canvas": {
+ "description": "Explicit canvas size. If omitted, computed from node extents + margin.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["w", "h"],
+ "properties": {
+ "w": { "type": "number", "exclusiveMinimum": 0 },
+ "h": { "type": "number", "exclusiveMinimum": 0 }
+ }
+ },
+ "nodes": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["id", "kind"],
+ "additionalProperties": false,
+ "properties": {
+ "id": { "type": "string", "description": "Unique node identifier, referenced by edges." },
+ "kind": {
+ "type": "string",
+ "enum": ["commit","tree","blob","jessica","john","josie","ref","symref","goldref","other","commit-full","tree-full","blob-full"],
+ "description": "Symbol kind, mapped to geometry and colors in _palette.json. The '-full' kinds render a large git-object content box (hash label above, key/value rows, optional body text) instead of a plain labeled symbol."
+ },
+ "label": {
+ "description": "Text content. String for single line; array of strings for multi-line. Not used by '-full' kinds (use hash/rows/body instead).",
+ "oneOf": [
+ { "type": "string" },
+ { "type": "array", "items": { "type": "string" }, "minItems": 2 }
+ ]
+ },
+ "hash": { "type": "string", "description": "('-full' kinds only) Object hash shown as a label above the box." },
+ "rows": {
+ "type": "array",
+ "description": "('-full' kinds only) Key/value table rendered inside the box, monospace-aligned. First row's key is underlined (marks the object type line).",
+ "items": {
+ "type": "array",
+ "items": { "type": "string" },
+ "minItems": 2,
+ "maxItems": 2
+ }
+ },
+ "body": {
+ "type": "array",
+ "description": "('-full' kinds only) Plain left-aligned lines rendered below rows, separated by a blank-line gap. Use an empty string for a blank line.",
+ "items": { "type": "string" }
+ },
+ "align": {
+ "type": "string",
+ "enum": ["left", "center"],
+ "default": "left",
+ "description": "('-full' kinds only) 'center' renders the single header row centered (used for blob boxes); 'left' renders a left-aligned key/value table (used for commit/tree boxes)."
+ },
+ "col": { "type": "number", "description": "Grid column (grid layout only)." },
+ "row": { "type": "number", "description": "Grid row (grid layout only)." },
+ "x": { "type": "number", "description": "Absolute x coordinate of top-left corner (absolute layout or override)." },
+ "y": { "type": "number", "description": "Absolute y coordinate of top-left corner (absolute layout or override)." },
+ "dx": { "type": "number", "description": "Horizontal nudge added to grid-computed x." },
+ "dy": { "type": "number", "description": "Vertical nudge added to grid-computed y." },
+ "w": { "type": "number", "description": "Width override (uses kind default if omitted)." },
+ "h": { "type": "number", "description": "Height override (uses kind default if omitted)." },
+ "fill": { "type": "string", "description": "Fill color override (named key from palette colors, or hex)." },
+ "text": { "type": "string", "description": "Text color override (named key or hex)." },
+ "stroke":{ "type": "string", "description": "Stroke color override (named key or hex)." }
+ }
+ }
+ },
+ "edges": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["from", "to"],
+ "additionalProperties": false,
+ "properties": {
+ "from": { "type": "string", "description": "Source node id." },
+ "to": { "type": "string", "description": "Target node id." },
+ "route": {
+ "type": "string",
+ "enum": ["straight", "diagonal", "elbow"],
+ "default": "straight",
+ "description": "Arrow routing. 'straight' is horizontal or vertical. 'diagonal' is a direct line. 'elbow' has one bend."
+ },
+ "arrow": {
+ "type": "string",
+ "enum": ["back", "forward", "both", "none"],
+ "default": "back",
+ "description": "Arrowhead placement. 'back' means the arrowhead is at the 'to' end pointing toward 'from' (the Git parent-pointing convention)."
+ },
+ "style": {
+ "type": "string",
+ "enum": ["normal", "muted"],
+ "default": "normal",
+ "description": "'muted' renders a thinner, lower-opacity arrow for secondary/background relationships (e.g. pull lines fanning out from a shared remote)."
+ },
+ "label": { "type": "string", "description": "Optional edge label." }
+ }
+ }
+ },
+ "banners": {
+ "description": "Wide block arrows with inset text, used in workflow diagrams (areas, lifecycle, etc.).",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["label", "from", "to"],
+ "additionalProperties": false,
+ "properties": {
+ "label": { "type": "string" },
+ "from": {
+ "type": "object",
+ "required": ["x","y"],
+ "additionalProperties": false,
+ "properties": { "x": {"type":"number"}, "y": {"type":"number"} }
+ },
+ "to": {
+ "type": "object",
+ "required": ["x","y"],
+ "additionalProperties": false,
+ "properties": { "x": {"type":"number"}, "y": {"type":"number"} }
+ },
+ "thickness": { "type": "number", "description": "Arrow shaft height in pixels. Default 22." }
+ }
+ }
+ },
+ "rules": {
+ "description": "Vertical or horizontal divider lines.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "x": { "type": "number", "description": "X position for a vertical rule." },
+ "y": { "type": "number", "description": "Y position for a horizontal rule." },
+ "x1": { "type": "number" },
+ "x2": { "type": "number" },
+ "y1": { "type": "number" },
+ "y2": { "type": "number" }
+ }
+ }
+ },
+ "labels": {
+ "description": "Free-floating text labels.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["text", "x", "y"],
+ "additionalProperties": false,
+ "properties": {
+ "text": { "oneOf": [{"type":"string"}, {"type":"array","items":{"type":"string"}}] },
+ "x": { "type": "number" },
+ "y": { "type": "number" },
+ "anchor": { "type": "string", "enum": ["start","middle","end"], "default": "middle" },
+ "fill": { "type": "string" },
+ "size": { "type": "number" }
+ }
+ }
+ },
+ "groups": {
+ "description": "Labelled background boxes or swimlanes.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["x","y","w","h"],
+ "additionalProperties": false,
+ "properties": {
+ "x": { "type": "number" },
+ "y": { "type": "number" },
+ "w": { "type": "number" },
+ "h": { "type": "number" },
+ "label": { "type": "string" },
+ "fill": { "type": "string" },
+ "stroke": { "type": "string" }
+ }
+ }
+ }
+ }
+}
diff --git a/figures/advance-master.json b/figures/advance-master.json
new file mode 100644
index 0000000..24936df
--- /dev/null
+++ b/figures/advance-master.json
@@ -0,0 +1,81 @@
+{
+ "$schema": "./_schema.json",
+ "title": "advance master",
+ "grid": {
+ "x": 144.9,
+ "y": 50.0
+ },
+ "nodes": [
+ {
+ "id": "f30ab",
+ "kind": "commit",
+ "label": "f30ab",
+ "col": 2,
+ "row": 3,
+ "dy": 5.0
+ },
+ {
+ "id": "node_34ac2",
+ "kind": "commit",
+ "label": "34ac2",
+ "col": 1,
+ "row": 3,
+ "dy": 5.0
+ },
+ {
+ "id": "node_98ca9",
+ "kind": "commit",
+ "label": "98ca9",
+ "col": 0,
+ "row": 3,
+ "dy": 5.0
+ },
+ {
+ "id": "node_87ab2",
+ "kind": "commit",
+ "label": "87ab2",
+ "col": 3,
+ "row": 4,
+ "dy": -12.0
+ },
+ {
+ "id": "c2b9e",
+ "kind": "commit",
+ "label": "c2b9e",
+ "col": 3,
+ "row": 2,
+ "dy": 22.0
+ },
+ {
+ "id": "testing",
+ "kind": "ref",
+ "label": "testing",
+ "col": 3,
+ "row": 5
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 1,
+ "dy": 11.0
+ },
+ {
+ "id": "head",
+ "kind": "blob",
+ "label": "HEAD",
+ "col": 3,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_34ac2", "to": "node_98ca9" },
+ { "from": "f30ab", "to": "node_34ac2" },
+ { "from": "c2b9e", "to": "f30ab", "route": "diagonal" },
+ { "from": "node_87ab2", "to": "f30ab", "route": "diagonal" },
+ { "from": "master", "to": "c2b9e", "arrow": "forward" },
+ { "from": "testing", "to": "node_87ab2", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" }
+ ]
+}
diff --git a/figures/advance-testing.json b/figures/advance-testing.json
new file mode 100644
index 0000000..8272fca
--- /dev/null
+++ b/figures/advance-testing.json
@@ -0,0 +1,73 @@
+{
+ "$schema": "./_schema.json",
+ "title": "advance testing",
+ "grid": {
+ "x": 89.1,
+ "y": 61.3
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0,
+ "dx": 32.7
+ },
+ {
+ "id": "f30ab",
+ "kind": "commit",
+ "label": "f30ab",
+ "col": 3,
+ "row": 1,
+ "dx": 22.7
+ },
+ {
+ "id": "node_87ab2",
+ "kind": "commit",
+ "label": "87ab2",
+ "col": 5,
+ "row": 1,
+ "dx": -10.5
+ },
+ {
+ "id": "node_34ac2",
+ "kind": "commit",
+ "label": "34ac2",
+ "col": 2,
+ "row": 1,
+ "dx": -33.2
+ },
+ {
+ "id": "node_98ca9",
+ "kind": "commit",
+ "label": "98ca9",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "testing",
+ "kind": "ref",
+ "label": "testing",
+ "col": 5,
+ "row": 2,
+ "dx": -0.5
+ },
+ {
+ "id": "head",
+ "kind": "goldref",
+ "label": "HEAD",
+ "col": 5,
+ "row": 3,
+ "dx": 0.5
+ }
+ ],
+ "edges": [
+ { "from": "node_34ac2", "to": "node_98ca9" },
+ { "from": "f30ab", "to": "node_34ac2" },
+ { "from": "node_87ab2", "to": "f30ab" },
+ { "from": "master", "to": "f30ab", "arrow": "forward" },
+ { "from": "testing", "to": "node_87ab2", "arrow": "forward" },
+ { "from": "head", "to": "testing", "arrow": "forward" }
+ ]
+}
diff --git a/figures/areas.json b/figures/areas.json
new file mode 100644
index 0000000..9e6b4e0
--- /dev/null
+++ b/figures/areas.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "./_schema.json",
+ "title": "Working tree, staging area, and Git directory",
+ "layout": "absolute",
+ "canvas": { "w": 401, "h": 215 },
+ "nodes": [
+ { "id": "wt", "kind": "ref", "label": ["Working", "Tree"],
+ "x": 4, "y": 4, "w": 110.58, "h": 38 },
+ { "id": "sa", "kind": "tree", "label": ["Staging", "Area"],
+ "x": 144.74, "y": 4, "w": 110.58, "h": 38 },
+ { "id": "gd", "kind": "other", "label": [".git directory", "(Repository)"],
+ "x": 285.48, "y": 4, "w": 110.58, "h": 38 }
+ ],
+ "rules": [
+ { "x": 60, "y1": 26, "y2": 211 },
+ { "x": 200, "y1": 26, "y2": 211 },
+ { "x": 340, "y1": 26, "y2": 211 }
+ ],
+ "banners": [
+ { "label": "Checkout the project",
+ "from": { "x": 340, "y": 81 }, "to": { "x": 60, "y": 81 }, "thickness": 22 },
+ { "label": "Stage Fixes",
+ "from": { "x": 60, "y": 133 }, "to": { "x": 200, "y": 133 }, "thickness": 22 },
+ { "label": "Commit",
+ "from": { "x": 200, "y": 180 }, "to": { "x": 340, "y": 180 }, "thickness": 22 }
+ ]
+}
diff --git a/figures/basic-branching-1.json b/figures/basic-branching-1.json
new file mode 100644
index 0000000..920a9e4
--- /dev/null
+++ b/figures/basic-branching-1.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "./_schema.json",
+ "title": "A simple commit history",
+ "grid": { "x": 127, "y": 58 },
+ "nodes": [
+ { "id": "c0", "kind": "commit", "label": "C0", "col": 0, "row": 1 },
+ { "id": "c1", "kind": "commit", "label": "C1", "col": 1, "row": 1 },
+ { "id": "c2", "kind": "commit", "label": "C2", "col": 2, "row": 1 },
+ { "id": "main", "kind": "ref", "label": "main", "col": 2, "row": 0 }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "main","to": "c2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-branching-2.json b/figures/basic-branching-2.json
new file mode 100644
index 0000000..de5d457
--- /dev/null
+++ b/figures/basic-branching-2.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "./_schema.json",
+ "title": "Creating a new branch pointer",
+ "grid": { "x": 127, "y": 62 },
+ "nodes": [
+ { "id": "c0", "kind": "commit", "label": "C0", "col": 0, "row": 1 },
+ { "id": "c1", "kind": "commit", "label": "C1", "col": 1, "row": 1 },
+ { "id": "c2", "kind": "commit", "label": "C2", "col": 2, "row": 1 },
+ { "id": "main", "kind": "ref", "label": "main", "col": 2, "row": 0 },
+ { "id": "iss53", "kind": "ref", "label": "iss53", "col": 2, "row": 2 }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "main", "to": "c2", "arrow": "forward" },
+ { "from": "iss53","to": "c2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-branching-3.json b/figures/basic-branching-3.json
new file mode 100644
index 0000000..3993561
--- /dev/null
+++ b/figures/basic-branching-3.json
@@ -0,0 +1,59 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic branching 3",
+ "grid": {
+ "x": 127.0,
+ "y": 62.0
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "iss53",
+ "kind": "ref",
+ "label": "iss53",
+ "col": 3,
+ "row": 2
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "master", "to": "c2", "arrow": "forward" },
+ { "from": "iss53", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-branching-4.json b/figures/basic-branching-4.json
new file mode 100644
index 0000000..5ecd50b
--- /dev/null
+++ b/figures/basic-branching-4.json
@@ -0,0 +1,80 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic branching 4",
+ "grid": {
+ "x": 127.0,
+ "y": 60.7
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "hotfix",
+ "kind": "ref",
+ "label": "hotfix",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dy": -1.4
+ },
+ {
+ "id": "iss53",
+ "kind": "ref",
+ "label": "iss53",
+ "col": 3,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "master", "to": "c2", "arrow": "forward" },
+ { "from": "hotfix", "to": "c4", "arrow": "forward" },
+ { "from": "iss53", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-branching-5.json b/figures/basic-branching-5.json
new file mode 100644
index 0000000..54d4f01
--- /dev/null
+++ b/figures/basic-branching-5.json
@@ -0,0 +1,77 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic branching 5",
+ "grid": {
+ "x": 127.0,
+ "y": 60.0
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 2
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "hotfix",
+ "kind": "ref",
+ "label": "hotfix",
+ "col": 3,
+ "row": 1,
+ "dy": -2.0
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 3,
+ "dy": -2.0
+ },
+ {
+ "id": "iss53",
+ "kind": "ref",
+ "label": "iss53",
+ "col": 3,
+ "row": 4
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "master", "to": "c4", "arrow": "forward" },
+ { "from": "hotfix", "to": "c4", "arrow": "forward" },
+ { "from": "iss53", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-branching-6.json b/figures/basic-branching-6.json
new file mode 100644
index 0000000..e82ae1b
--- /dev/null
+++ b/figures/basic-branching-6.json
@@ -0,0 +1,85 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic branching 6",
+ "grid": {
+ "x": 126.7,
+ "y": 60.7
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 1,
+ "dx": 0.6,
+ "dy": 1.3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0,
+ "dx": 0.9
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1,
+ "dx": 0.9,
+ "dy": 1.3
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 4,
+ "row": 2,
+ "dy": -1.4
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dx": 0.9,
+ "dy": -1.4
+ },
+ {
+ "id": "iss53",
+ "kind": "ref",
+ "label": "iss53",
+ "col": 4,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c5", "to": "c3" },
+ { "from": "master", "to": "c4", "arrow": "forward" },
+ { "from": "iss53", "to": "c5", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-merging-1.json b/figures/basic-merging-1.json
new file mode 100644
index 0000000..93bd74f
--- /dev/null
+++ b/figures/basic-merging-1.json
@@ -0,0 +1,84 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic merging 1",
+ "grid": {
+ "x": 126.8,
+ "y": 60.7
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0,
+ "dx": 0.6
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1,
+ "dx": 0.6,
+ "dy": 1.3
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 4,
+ "row": 2,
+ "dy": -1.4
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dx": 0.6,
+ "dy": -1.4
+ },
+ {
+ "id": "iss53",
+ "kind": "ref",
+ "label": "iss53",
+ "col": 4,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c5", "to": "c3" },
+ { "from": "master", "to": "c4", "arrow": "forward" },
+ { "from": "iss53", "to": "c5", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-merging-2.json b/figures/basic-merging-2.json
new file mode 100644
index 0000000..38d90c0
--- /dev/null
+++ b/figures/basic-merging-2.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "./_schema.json",
+ "title": "A merge commit",
+ "grid": { "x": 127, "y": 58 },
+ "nodes": [
+ { "id": "c0", "kind": "commit", "label": "C0", "col": 0, "row": 1 },
+ { "id": "c1", "kind": "commit", "label": "C1", "col": 1, "row": 1 },
+ { "id": "c2", "kind": "commit", "label": "C2", "col": 2, "row": 1 },
+ { "id": "c4", "kind": "commit", "label": "C4", "col": 3, "row": 1 },
+ { "id": "c3", "kind": "commit", "label": "C3", "col": 3, "row": 2 },
+ { "id": "c5", "kind": "commit", "label": "C5", "col": 4, "row": 2 },
+ { "id": "c6", "kind": "commit", "label": "C6", "col": 5, "row": 1 },
+ { "id": "main", "kind": "ref", "label": "main", "col": 5, "row": 0 },
+ { "id": "iss53", "kind": "ref", "label": "iss53", "col": 4, "row": 3 }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c5", "to": "c3" },
+ { "from": "c6", "to": "c4" },
+ { "from": "c6", "to": "c5", "route": "diagonal" },
+ { "from": "main", "to": "c6", "arrow": "forward" },
+ { "from": "iss53","to": "c5", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-rebase-1.json b/figures/basic-rebase-1.json
new file mode 100644
index 0000000..bcbe95c
--- /dev/null
+++ b/figures/basic-rebase-1.json
@@ -0,0 +1,72 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic rebase 1",
+ "grid": {
+ "x": 127.3,
+ "y": 60.2
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 2,
+ "dy": -1.9
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 2,
+ "dy": -1.9
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 2,
+ "dx": -0.6,
+ "dy": -1.9
+ },
+ {
+ "id": "experiment",
+ "kind": "ref",
+ "label": "experiment",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dy": -1.9
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c4", "to": "c2", "route": "diagonal" },
+ { "from": "experiment", "to": "c4", "arrow": "forward" },
+ { "from": "master", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-rebase-2.json b/figures/basic-rebase-2.json
new file mode 100644
index 0000000..2228063
--- /dev/null
+++ b/figures/basic-rebase-2.json
@@ -0,0 +1,86 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic rebase 2",
+ "grid": {
+ "x": 127.5,
+ "y": 60.2
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 2,
+ "dy": -1.9
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 2,
+ "dx": -0.5,
+ "dy": -1.9
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 2,
+ "dx": -1.0,
+ "dy": -1.9
+ },
+ {
+ "id": "experiment",
+ "kind": "ref",
+ "label": "experiment",
+ "col": 3,
+ "row": 0,
+ "dx": -0.5
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1,
+ "dx": -0.5
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dx": -0.5,
+ "dy": -1.9
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 4,
+ "row": 2,
+ "dy": -1.9
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c4", "to": "c2", "route": "diagonal" },
+ { "from": "c5", "to": "c3" },
+ { "from": "c5", "to": "c4", "route": "diagonal" },
+ { "from": "experiment", "to": "c4", "arrow": "forward" },
+ { "from": "master", "to": "c5", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-rebase-3.json b/figures/basic-rebase-3.json
new file mode 100644
index 0000000..266b300
--- /dev/null
+++ b/figures/basic-rebase-3.json
@@ -0,0 +1,86 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic rebase 3",
+ "grid": {
+ "x": 127.5,
+ "y": 60.6
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 1,
+ "dy": -1.35
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 1,
+ "dx": -0.5,
+ "dy": -1.35
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 1,
+ "dx": -1.0,
+ "dy": -1.35
+ },
+ {
+ "id": "experiment",
+ "kind": "ref",
+ "label": "experiment",
+ "col": 4,
+ "row": 0,
+ "dy": -1.25
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 0,
+ "dx": -0.5,
+ "dy": 1.25
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 1,
+ "dx": -0.5,
+ "dy": -1.35
+ },
+ {
+ "id": "c4-2",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 4,
+ "row": 1,
+ "dy": -1.35
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 2
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c4", "to": "c2", "route": "diagonal" },
+ { "from": "c4-2", "to": "c3" },
+ { "from": "experiment", "to": "c4-2", "arrow": "forward" },
+ { "from": "master", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/basic-rebase-4.json b/figures/basic-rebase-4.json
new file mode 100644
index 0000000..e31e479
--- /dev/null
+++ b/figures/basic-rebase-4.json
@@ -0,0 +1,75 @@
+{
+ "$schema": "./_schema.json",
+ "title": "basic rebase 4",
+ "grid": {
+ "x": 127.5,
+ "y": 61.3
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 0,
+ "row": 1,
+ "dy": -0.8
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 1,
+ "dx": -0.5,
+ "dy": -0.8
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 1,
+ "dx": -1.0,
+ "dy": -0.8
+ },
+ {
+ "id": "experiment",
+ "kind": "ref",
+ "label": "experiment",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 1,
+ "dx": -0.5,
+ "dy": -0.8
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 4,
+ "row": 1,
+ "dy": -0.8
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 2
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c4", "to": "c3" },
+ { "from": "experiment", "to": "c4", "arrow": "forward" },
+ { "from": "master", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/benevolent-dictator.json b/figures/benevolent-dictator.json
new file mode 100644
index 0000000..d6aac50
--- /dev/null
+++ b/figures/benevolent-dictator.json
@@ -0,0 +1,88 @@
+{
+ "$schema": "./_schema.json",
+ "title": "benevolent dictator",
+ "grid": {
+ "x": 68.7,
+ "y": 77.9
+ },
+ "nodes": [
+ {
+ "id": "lieutenant",
+ "kind": "symref",
+ "label": "lieutenant",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "lieutenant-2",
+ "kind": "symref",
+ "label": "lieutenant",
+ "col": 1,
+ "row": 1,
+ "dx": 29.66
+ },
+ {
+ "id": "developer",
+ "kind": "commit",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "dictator",
+ "kind": "ref",
+ "label": "dictator",
+ "col": 0,
+ "row": 0,
+ "dx": 11.36,
+ "dy": 1.25
+ },
+ {
+ "id": "developer-2",
+ "kind": "commit",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "developer-3",
+ "kind": "commit",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "blessed",
+ "kind": "blob",
+ "label": [
+ "blessed",
+ "repository"
+ ],
+ "col": 4,
+ "row": 0,
+ "dy": -1.25
+ }
+ ],
+ "edges": [
+ { "from": "developer", "to": "lieutenant", "arrow": "forward" },
+ { "from": "developer-2", "to": "lieutenant-2", "arrow": "forward", "route": "diagonal" },
+ { "from": "developer-3", "to": "lieutenant-2", "arrow": "forward", "route": "diagonal" },
+ { "from": "lieutenant", "to": "dictator", "arrow": "forward" },
+ { "from": "lieutenant-2", "to": "dictator", "arrow": "forward", "route": "diagonal" },
+ { "from": "dictator", "to": "blessed", "arrow": "forward" },
+ { "from": "blessed", "to": "developer-3", "arrow": "forward" },
+ { "from": "blessed", "to": "lieutenant", "arrow": "forward", "route": "diagonal", "style": "muted" },
+ { "from": "blessed", "to": "lieutenant-2", "arrow": "forward", "route": "diagonal", "style": "muted" },
+ { "from": "blessed", "to": "developer", "arrow": "forward", "route": "diagonal", "style": "muted" },
+ { "from": "blessed", "to": "developer-2", "arrow": "forward", "route": "diagonal", "style": "muted" }
+ ]
+}
diff --git a/figures/branch-and-history.json b/figures/branch-and-history.json
new file mode 100644
index 0000000..bb8debd
--- /dev/null
+++ b/figures/branch-and-history.json
@@ -0,0 +1,93 @@
+{
+ "$schema": "./_schema.json",
+ "title": "branch and history",
+ "grid": {
+ "x": 88.3,
+ "y": 62.7
+ },
+ "nodes": [
+ {
+ "id": "snapshot-c",
+ "kind": "symref",
+ "label": "Snapshot C",
+ "col": 3,
+ "row": 3,
+ "dx": 25.1
+ },
+ {
+ "id": "snapshot-b",
+ "kind": "symref",
+ "label": "Snapshot B",
+ "col": 2,
+ "row": 3,
+ "dx": -31.6
+ },
+ {
+ "id": "snapshot-a",
+ "kind": "symref",
+ "label": "Snapshot A",
+ "col": 0,
+ "row": 3
+ },
+ {
+ "id": "f30ab",
+ "kind": "commit",
+ "label": "f30ab",
+ "col": 3,
+ "row": 2,
+ "dx": 25.1,
+ "dy": -9.4
+ },
+ {
+ "id": "node_34ac2",
+ "kind": "commit",
+ "label": "34ac2",
+ "col": 2,
+ "row": 2,
+ "dx": -31.6,
+ "dy": -9.4
+ },
+ {
+ "id": "node_98ca9",
+ "kind": "commit",
+ "label": "98ca9",
+ "col": 0,
+ "row": 2,
+ "dy": -9.4
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 1,
+ "dy": -8.7
+ },
+ {
+ "id": "head",
+ "kind": "goldref",
+ "label": "HEAD",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "v1-0",
+ "kind": "ref",
+ "label": "v1.0",
+ "col": 3,
+ "row": 1,
+ "dx": -23.9,
+ "dy": -8.7
+ }
+ ],
+ "edges": [
+ { "from": "node_34ac2", "to": "node_98ca9" },
+ { "from": "f30ab", "to": "node_34ac2" },
+ { "from": "f30ab", "to": "snapshot-c", "arrow": "forward" },
+ { "from": "node_34ac2", "to": "snapshot-b", "arrow": "forward" },
+ { "from": "node_98ca9", "to": "snapshot-a", "arrow": "forward" },
+ { "from": "master", "to": "f30ab", "arrow": "forward", "route": "diagonal" },
+ { "from": "v1-0", "to": "f30ab", "arrow": "forward", "route": "diagonal" },
+ { "from": "head", "to": "master", "arrow": "forward" }
+ ]
+}
diff --git a/figures/centralized.json b/figures/centralized.json
new file mode 100644
index 0000000..40518d4
--- /dev/null
+++ b/figures/centralized.json
@@ -0,0 +1,49 @@
+{
+ "$schema": "./_schema.json",
+ "title": "centralized",
+ "grid": {
+ "x": 225.0,
+ "y": 42.5
+ },
+ "nodes": [
+ {
+ "id": "node0",
+ "kind": "tree",
+ "label": "node0",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "node1",
+ "kind": "commit",
+ "label": "node1",
+ "col": 1,
+ "row": 1,
+ "dy": -9.0
+ },
+ {
+ "id": "node2",
+ "kind": "commit",
+ "label": "node2",
+ "col": 1,
+ "row": 2,
+ "dy": 8.5
+ },
+ {
+ "id": "node3",
+ "kind": "commit",
+ "label": "node3",
+ "col": 1,
+ "row": 4,
+ "dy": -16.5
+ },
+ {
+ "id": "file",
+ "kind": "tree",
+ "label": "File",
+ "col": 0,
+ "row": 4
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/centralized_workflow.json b/figures/centralized_workflow.json
new file mode 100644
index 0000000..89d9dbf
--- /dev/null
+++ b/figures/centralized_workflow.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "./_schema.json",
+ "title": "centralized_workflow",
+ "grid": {
+ "x": 106.8,
+ "y": 81.5
+ },
+ "nodes": [
+ {
+ "id": "developer",
+ "kind": "ref",
+ "label": "developer",
+ "col": 1,
+ "row": 1,
+ "dx": -0.8
+ },
+ {
+ "id": "developer-2",
+ "kind": "ref",
+ "label": "developer",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "developer-3",
+ "kind": "ref",
+ "label": "developer",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "shared",
+ "kind": "blob",
+ "label": [
+ "shared",
+ "repository"
+ ],
+ "col": 1,
+ "row": 0,
+ "dx": -0.8
+ }
+ ],
+ "edges": [
+ { "from": "shared", "to": "developer", "arrow": "both" },
+ { "from": "shared", "to": "developer-2", "arrow": "both", "route": "diagonal" },
+ { "from": "shared", "to": "developer-3", "arrow": "both", "route": "diagonal" }
+ ]
+}
diff --git a/figures/checkout-master.json b/figures/checkout-master.json
new file mode 100644
index 0000000..607d0c6
--- /dev/null
+++ b/figures/checkout-master.json
@@ -0,0 +1,71 @@
+{
+ "$schema": "./_schema.json",
+ "title": "checkout master",
+ "grid": {
+ "x": 145.0,
+ "y": 61.3
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "f30ab",
+ "kind": "commit",
+ "label": "f30ab",
+ "col": 2,
+ "row": 2,
+ "dy": -0.6
+ },
+ {
+ "id": "node_87ab2",
+ "kind": "commit",
+ "label": "87ab2",
+ "col": 3,
+ "row": 2,
+ "dy": -0.6
+ },
+ {
+ "id": "node_34ac2",
+ "kind": "commit",
+ "label": "34ac2",
+ "col": 1,
+ "row": 2,
+ "dy": -0.6
+ },
+ {
+ "id": "node_98ca9",
+ "kind": "commit",
+ "label": "98ca9",
+ "col": 0,
+ "row": 2,
+ "dy": -0.6
+ },
+ {
+ "id": "testing",
+ "kind": "ref",
+ "label": "testing",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "head",
+ "kind": "blob",
+ "label": "HEAD",
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_34ac2", "to": "node_98ca9" },
+ { "from": "f30ab", "to": "node_34ac2" },
+ { "from": "node_87ab2", "to": "f30ab" },
+ { "from": "master", "to": "f30ab", "arrow": "forward" },
+ { "from": "testing", "to": "node_87ab2", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" }
+ ]
+}
diff --git a/figures/clean.json b/figures/clean.json
new file mode 100644
index 0000000..0bb1716
--- /dev/null
+++ b/figures/clean.json
@@ -0,0 +1,72 @@
+{
+ "$schema": "./_schema.json",
+ "title": "clean",
+ "grid": {
+ "x": 150.0,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "filea-txt",
+ "kind": "commit",
+ "label": "fileA.txt",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "filea-txt-2",
+ "kind": "blob",
+ "label": "fileA.txt'",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "fileb-txt",
+ "kind": "blob",
+ "label": "fileB.txt'",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "fileb-txt-2",
+ "kind": "commit",
+ "label": "fileB.txt",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "filec-rb",
+ "kind": "commit",
+ "label": "fileC.rb",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "filec-rb-2",
+ "kind": "commit",
+ "label": "fileC.rb",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "smudge",
+ "kind": "ref",
+ "label": "smudge",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "clean",
+ "kind": "ref",
+ "label": "clean",
+ "col": 1,
+ "row": 1
+ }
+ ],
+ "edges": [
+ { "from": "filea-txt", "to": "smudge", "arrow": "forward" },
+ { "from": "smudge", "to": "filea-txt-2", "arrow": "forward" },
+ { "from": "fileb-txt", "to": "fileb-txt-2", "arrow": "forward" },
+ { "from": "filec-rb-2", "to": "filec-rb" }
+ ]
+}
diff --git a/figures/commit-and-tree.json b/figures/commit-and-tree.json
new file mode 100644
index 0000000..410393a
--- /dev/null
+++ b/figures/commit-and-tree.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "commit and tree",
+ "grid": {
+ "x": 268.0,
+ "y": 146.0
+ },
+ "nodes": [
+ {
+ "id": "node_92ec2",
+ "kind": "tree-full",
+ "hash": "92ec2",
+ "rows": [
+ ["tree", "size"],
+ ["blob", "5b1d3 README"],
+ ["blob", "911e7 LICENSE"],
+ ["blob", "cba0a test.rb"]
+ ],
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "the-initial-commit-of-my-project",
+ "kind": "commit-full",
+ "hash": "98ca9",
+ "rows": [
+ ["commit", "size"],
+ ["tree", "92ec2"],
+ ["author", "Scott"],
+ ["committer", "Scott"]
+ ],
+ "body": [
+ "",
+ "The initial commit of my project"
+ ],
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "node_5b1d3",
+ "kind": "blob-full",
+ "hash": "5b1d3",
+ "rows": [["blob", "size"]],
+ "align": "center",
+ "body": [
+ "== Testing library",
+ "",
+ "This library is used to test",
+ "Ruby projects."
+ ],
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "node_911e7",
+ "kind": "blob-full",
+ "hash": "911e7",
+ "rows": [["blob", "size"]],
+ "align": "center",
+ "body": [
+ "The MIT License",
+ "",
+ "Copyright (c) 2008 Scott Chacon",
+ "",
+ "Permission is hereby granted,",
+ "free of charge, to any person"
+ ],
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "cba0a",
+ "kind": "blob-full",
+ "hash": "cba0a",
+ "rows": [["blob", "size"]],
+ "align": "center",
+ "body": [
+ "require 'logger'",
+ "require 'test/unit'",
+ "",
+ "",
+ "class Test::Unit::TestCase"
+ ],
+ "col": 2,
+ "row": 2
+ }
+ ],
+ "edges": [
+ { "from": "the-initial-commit-of-my-project", "to": "node_92ec2", "arrow": "forward" },
+ { "from": "node_92ec2", "to": "node_5b1d3", "arrow": "forward", "route": "diagonal" },
+ { "from": "node_92ec2", "to": "node_911e7", "arrow": "forward" },
+ { "from": "node_92ec2", "to": "cba0a", "arrow": "forward", "route": "diagonal" }
+ ]
+}
diff --git a/figures/commits-and-parents.json b/figures/commits-and-parents.json
new file mode 100644
index 0000000..cc31720
--- /dev/null
+++ b/figures/commits-and-parents.json
@@ -0,0 +1,66 @@
+{
+ "$schema": "./_schema.json",
+ "title": "commits and parents",
+ "grid": {
+ "x": 284.5,
+ "y": 143.0
+ },
+ "nodes": [
+ {
+ "id": "snapshot-a",
+ "kind": "tree",
+ "label": "Snapshot A",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "snapshot-b",
+ "kind": "tree",
+ "label": "Snapshot B",
+ "col": 1,
+ "row": 1,
+ "dx": -0.75
+ },
+ {
+ "id": "snapshot-c",
+ "kind": "tree",
+ "label": "Snapshot C",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "fixed-bug-1328-stack-overflow",
+ "kind": "commit",
+ "label": [
+ "Fixed bug #1328 - stack overflow",
+ "under certain conditions"
+ ],
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "the-initial-commit-of-my-project",
+ "kind": "commit",
+ "label": "The initial commit of my project",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "add-feature-32-ability-to-add-new",
+ "kind": "commit",
+ "label": [
+ "add feature #32 - ability to add new",
+ "formats to the central interface"
+ ],
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "fixed-bug-1328-stack-overflow", "to": "the-initial-commit-of-my-project", "arrow": "back" },
+ { "from": "add-feature-32-ability-to-add-new", "to": "fixed-bug-1328-stack-overflow", "arrow": "back" },
+ { "from": "the-initial-commit-of-my-project", "to": "snapshot-a", "arrow": "forward" },
+ { "from": "fixed-bug-1328-stack-overflow", "to": "snapshot-b", "arrow": "forward" },
+ { "from": "add-feature-32-ability-to-add-new", "to": "snapshot-c", "arrow": "forward" }
+ ]
+}
diff --git a/figures/data-model-1.json b/figures/data-model-1.json
new file mode 100644
index 0000000..5b8a2d9
--- /dev/null
+++ b/figures/data-model-1.json
@@ -0,0 +1,56 @@
+{
+ "$schema": "./_schema.json",
+ "title": "data model 1",
+ "grid": {
+ "x": 113.5,
+ "y": 90.0
+ },
+ "nodes": [
+ {
+ "id": "tree-1",
+ "kind": "tree",
+ "label": "tree",
+ "col": 1,
+ "row": 0,
+ "dy": 12.5
+ },
+ {
+ "id": "tree-2",
+ "kind": "tree",
+ "label": "tree",
+ "col": 2,
+ "row": 1,
+ "dy": 18.5
+ },
+ {
+ "id": "blob-1",
+ "kind": "blob",
+ "label": "blob",
+ "col": 0,
+ "row": 1,
+ "dy": 21.5
+ },
+ {
+ "id": "blob-2",
+ "kind": "blob",
+ "label": "blob",
+ "col": 1,
+ "row": 1,
+ "dy": 21.5
+ },
+ {
+ "id": "blob-3",
+ "kind": "blob",
+ "label": "blob",
+ "col": 2,
+ "row": 2,
+ "dy": 21.5
+ }
+ ],
+ "edges": [
+ { "from": "tree-1", "to": "blob-1", "arrow": "forward", "route": "diagonal" },
+ { "from": "tree-1", "to": "blob-2", "arrow": "forward" },
+ { "from": "tree-1", "to": "tree-2", "arrow": "forward", "route": "diagonal" },
+ { "from": "tree-2", "to": "blob-3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/data-model-2.json b/figures/data-model-2.json
new file mode 100644
index 0000000..3ee0b59
--- /dev/null
+++ b/figures/data-model-2.json
@@ -0,0 +1,56 @@
+{
+ "$schema": "./_schema.json",
+ "title": "data model 2",
+ "grid": {
+ "x": 115.5,
+ "y": 88.8
+ },
+ "nodes": [
+ {
+ "id": "tree-3c4e9c",
+ "kind": "tree",
+ "label": "tree",
+ "col": 1,
+ "row": 0,
+ "dy": 12.5
+ },
+ {
+ "id": "tree-d8329f",
+ "kind": "tree",
+ "label": "tree",
+ "col": 2,
+ "row": 1,
+ "dy": 18.5
+ },
+ {
+ "id": "blob-fa49b0",
+ "kind": "blob",
+ "label": "\"new file\"",
+ "col": 0,
+ "row": 1,
+ "dy": 21.5
+ },
+ {
+ "id": "blob-1f7a7a",
+ "kind": "blob",
+ "label": "\"version 2\"",
+ "col": 1,
+ "row": 1,
+ "dy": 21.5
+ },
+ {
+ "id": "blob-83baae",
+ "kind": "blob",
+ "label": "\"version 1\"",
+ "col": 2,
+ "row": 2,
+ "dy": 21.5
+ }
+ ],
+ "edges": [
+ { "from": "tree-3c4e9c", "to": "blob-fa49b0", "arrow": "forward", "route": "diagonal" },
+ { "from": "tree-3c4e9c", "to": "blob-1f7a7a", "arrow": "forward" },
+ { "from": "tree-3c4e9c", "to": "tree-d8329f", "arrow": "forward", "route": "diagonal" },
+ { "from": "tree-d8329f", "to": "blob-83baae", "arrow": "forward" }
+ ]
+}
diff --git a/figures/data-model-3.json b/figures/data-model-3.json
new file mode 100644
index 0000000..cdeeae6
--- /dev/null
+++ b/figures/data-model-3.json
@@ -0,0 +1,61 @@
+{
+ "$schema": "./_schema.json",
+ "title": "data model 3",
+ "grid": {
+ "x": 329.5,
+ "y": 58.0
+ },
+ "nodes": [
+ {
+ "id": "version-2",
+ "kind": "commit",
+ "label": "\"version 2\"",
+ "col": 1,
+ "row": 1,
+ "dy": 15.0
+ },
+ {
+ "id": "new-file",
+ "kind": "commit",
+ "label": "\"new file\"",
+ "col": 1,
+ "row": 3,
+ "dy": -23.0
+ },
+ {
+ "id": "version-1",
+ "kind": "commit",
+ "label": "\"version 1\"",
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "third-commit",
+ "kind": "blob",
+ "label": "third commit",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "second-commit",
+ "kind": "blob",
+ "label": "second commit",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "first-commit",
+ "kind": "blob",
+ "label": "first commit",
+ "col": 0,
+ "row": 4
+ }
+ ],
+ "edges": [
+ { "from": "third-commit", "to": "second-commit", "arrow": "back" },
+ { "from": "second-commit", "to": "first-commit", "arrow": "back" },
+ { "from": "third-commit", "to": "version-2", "arrow": "forward", "route": "diagonal" },
+ { "from": "second-commit", "to": "new-file", "arrow": "forward", "route": "diagonal" },
+ { "from": "first-commit", "to": "version-1", "arrow": "forward" }
+ ]
+}
diff --git a/figures/data-model-4.json b/figures/data-model-4.json
new file mode 100644
index 0000000..27653ac
--- /dev/null
+++ b/figures/data-model-4.json
@@ -0,0 +1,80 @@
+{
+ "$schema": "./_schema.json",
+ "title": "data model 4",
+ "grid": {
+ "x": 252.0,
+ "y": 58.0
+ },
+ "nodes": [
+ {
+ "id": "version-2",
+ "kind": "commit",
+ "label": "\"version 2\"",
+ "col": 2,
+ "row": 1,
+ "dy": 15.0
+ },
+ {
+ "id": "new-file",
+ "kind": "commit",
+ "label": "\"new file\"",
+ "col": 2,
+ "row": 3,
+ "dy": -23.0
+ },
+ {
+ "id": "version-1",
+ "kind": "commit",
+ "label": "\"version 1\"",
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "third-commit",
+ "kind": "blob",
+ "label": "third commit",
+ "col": 1,
+ "row": 0,
+ "dx": -77.5
+ },
+ {
+ "id": "second-commit",
+ "kind": "blob",
+ "label": "second commit",
+ "col": 1,
+ "row": 2,
+ "dx": -77.5
+ },
+ {
+ "id": "first-commit",
+ "kind": "blob",
+ "label": "first commit",
+ "col": 1,
+ "row": 4,
+ "dx": -77.49
+ },
+ {
+ "id": "refs-heads-test",
+ "kind": "ref",
+ "label": "refs/heads/test",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "refs-heads-master",
+ "kind": "ref",
+ "label": "refs/heads/master",
+ "col": 0,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "third-commit", "to": "second-commit", "arrow": "back" },
+ { "from": "second-commit", "to": "first-commit", "arrow": "back" },
+ { "from": "third-commit", "to": "version-2", "arrow": "forward", "route": "diagonal" },
+ { "from": "second-commit", "to": "new-file", "arrow": "forward", "route": "diagonal" },
+ { "from": "first-commit", "to": "version-1", "arrow": "forward" },
+ { "from": "refs-heads-master", "to": "third-commit", "arrow": "forward" },
+ { "from": "refs-heads-test", "to": "second-commit", "arrow": "forward" }
+ ]
+}
diff --git a/figures/deltas.json b/figures/deltas.json
new file mode 100644
index 0000000..d53d676
--- /dev/null
+++ b/figures/deltas.json
@@ -0,0 +1,130 @@
+{
+ "$schema": "./_schema.json",
+ "title": "deltas",
+ "grid": {
+ "x": 116.0,
+ "y": 52.3
+ },
+ "nodes": [
+ {
+ "id": "version-1",
+ "kind": "blob",
+ "label": "Version 1",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "version-2",
+ "kind": "blob",
+ "label": "Version 2",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "version-3",
+ "kind": "blob",
+ "label": "Version 3",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "version-4",
+ "kind": "blob",
+ "label": "Version 4",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "version-5",
+ "kind": "blob",
+ "label": "Version 5",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "file-a",
+ "kind": "commit",
+ "label": "File A",
+ "col": 0,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "file-a-2",
+ "kind": "commit",
+ "label": "File A",
+ "col": 0,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "node_1",
+ "kind": "commit",
+ "label": "Δ1",
+ "col": 1,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "node_1-2",
+ "kind": "commit",
+ "label": "Δ1",
+ "col": 1,
+ "row": 3
+ },
+ {
+ "id": "node_2",
+ "kind": "commit",
+ "label": "Δ2",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "node_1-3",
+ "kind": "commit",
+ "label": "Δ1",
+ "col": 3,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "node_2-2",
+ "kind": "commit",
+ "label": "Δ2",
+ "col": 3,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "node_2-3",
+ "kind": "commit",
+ "label": "Δ2",
+ "col": 4,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "node_3",
+ "kind": "commit",
+ "label": "Δ3",
+ "col": 4,
+ "row": 3
+ },
+ {
+ "id": "file-b",
+ "kind": "commit",
+ "label": "File B",
+ "col": 0,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "file-c",
+ "kind": "commit",
+ "label": "File C",
+ "col": 0,
+ "row": 3
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/distributed.json b/figures/distributed.json
new file mode 100644
index 0000000..53b281a
--- /dev/null
+++ b/figures/distributed.json
@@ -0,0 +1,97 @@
+{
+ "$schema": "./_schema.json",
+ "title": "distributed",
+ "grid": {
+ "x": 146.4,
+ "y": 71.2
+ },
+ "nodes": [
+ {
+ "id": "version-3",
+ "kind": "commit",
+ "label": "Version 3",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "version-2",
+ "kind": "commit",
+ "label": "Version 2",
+ "col": 1,
+ "row": 1,
+ "dy": -29.2
+ },
+ {
+ "id": "version-1",
+ "kind": "commit",
+ "label": "Version 1",
+ "col": 1,
+ "row": 1,
+ "dy": 12.8
+ },
+ {
+ "id": "version-3-2",
+ "kind": "commit",
+ "label": "Version 3",
+ "col": 0,
+ "row": 5,
+ "dy": -13.0
+ },
+ {
+ "id": "version-2-2",
+ "kind": "commit",
+ "label": "Version 2",
+ "col": 0,
+ "row": 5,
+ "dy": 29.0
+ },
+ {
+ "id": "version-1-2",
+ "kind": "commit",
+ "label": "Version 1",
+ "col": 0,
+ "row": 6
+ },
+ {
+ "id": "file",
+ "kind": "tree",
+ "label": "File",
+ "col": 0,
+ "row": 4,
+ "dy": -29.8
+ },
+ {
+ "id": "version-3-3",
+ "kind": "commit",
+ "label": "Version 3",
+ "col": 2,
+ "row": 5,
+ "dy": -13.0
+ },
+ {
+ "id": "version-2-3",
+ "kind": "commit",
+ "label": "Version 2",
+ "col": 2,
+ "row": 5,
+ "dy": 29.0
+ },
+ {
+ "id": "version-1-3",
+ "kind": "commit",
+ "label": "Version 1",
+ "col": 2,
+ "row": 6
+ },
+ {
+ "id": "file-2",
+ "kind": "tree",
+ "label": "File",
+ "col": 2,
+ "row": 4,
+ "dx": -0.8,
+ "dy": -29.8
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/double-dot.json b/figures/double-dot.json
new file mode 100644
index 0000000..718b824
--- /dev/null
+++ b/figures/double-dot.json
@@ -0,0 +1,80 @@
+{
+ "$schema": "./_schema.json",
+ "title": "double dot",
+ "grid": {
+ "x": 116.1,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "a",
+ "kind": "commit",
+ "label": "A",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "b",
+ "kind": "commit",
+ "label": "B",
+ "col": 1,
+ "row": 0,
+ "dx": 2.9
+ },
+ {
+ "id": "e",
+ "kind": "commit",
+ "label": "E",
+ "col": 2,
+ "row": 0,
+ "dx": 5.8
+ },
+ {
+ "id": "f",
+ "kind": "commit",
+ "label": "F",
+ "col": 3,
+ "row": 0,
+ "dx": 8.7
+ },
+ {
+ "id": "c",
+ "kind": "commit",
+ "label": "C",
+ "col": 2,
+ "row": 1,
+ "dx": 5.8
+ },
+ {
+ "id": "d",
+ "kind": "commit",
+ "label": "D",
+ "col": 3,
+ "row": 1,
+ "dx": 8.7
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "experiment",
+ "kind": "ref",
+ "label": "experiment",
+ "col": 4,
+ "row": 1
+ }
+ ],
+ "edges": [
+ { "from": "b", "to": "a" },
+ { "from": "e", "to": "b" },
+ { "from": "f", "to": "e" },
+ { "from": "c", "to": "b", "route": "diagonal" },
+ { "from": "d", "to": "c" },
+ { "from": "master", "to": "f" },
+ { "from": "experiment", "to": "d" }
+ ]
+}
diff --git a/figures/head-to-master.json b/figures/head-to-master.json
new file mode 100644
index 0000000..de9e61a
--- /dev/null
+++ b/figures/head-to-master.json
@@ -0,0 +1,63 @@
+{
+ "$schema": "./_schema.json",
+ "title": "head to master",
+ "grid": {
+ "x": 145.0,
+ "y": 62.3
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 1,
+ "dy": 2.7
+ },
+ {
+ "id": "testing",
+ "kind": "ref",
+ "label": "testing",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "f30ab",
+ "kind": "commit",
+ "label": "f30ab",
+ "col": 2,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "node_34ac2",
+ "kind": "commit",
+ "label": "34ac2",
+ "col": 1,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "node_98ca9",
+ "kind": "commit",
+ "label": "98ca9",
+ "col": 0,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "head",
+ "kind": "blob",
+ "label": "HEAD",
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_34ac2", "to": "node_98ca9" },
+ { "from": "f30ab", "to": "node_34ac2" },
+ { "from": "master", "to": "f30ab", "arrow": "forward" },
+ { "from": "testing", "to": "f30ab", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" }
+ ]
+}
diff --git a/figures/head-to-testing.json b/figures/head-to-testing.json
new file mode 100644
index 0000000..ca7e606
--- /dev/null
+++ b/figures/head-to-testing.json
@@ -0,0 +1,59 @@
+{
+ "$schema": "./_schema.json",
+ "title": "head to testing",
+ "grid": {
+ "x": 145.0,
+ "y": 61.0
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "testing",
+ "kind": "ref",
+ "label": "testing",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "f30ab",
+ "kind": "commit",
+ "label": "f30ab",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "node_34ac2",
+ "kind": "commit",
+ "label": "34ac2",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "node_98ca9",
+ "kind": "commit",
+ "label": "98ca9",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "head",
+ "kind": "blob",
+ "label": "HEAD",
+ "col": 2,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "node_34ac2", "to": "node_98ca9" },
+ { "from": "f30ab", "to": "node_34ac2" },
+ { "from": "master", "to": "f30ab", "arrow": "forward" },
+ { "from": "testing", "to": "f30ab", "arrow": "forward" },
+ { "from": "head", "to": "testing", "arrow": "forward" }
+ ]
+}
diff --git a/figures/integration-manager.json b/figures/integration-manager.json
new file mode 100644
index 0000000..7703e6e
--- /dev/null
+++ b/figures/integration-manager.json
@@ -0,0 +1,101 @@
+{
+ "$schema": "./_schema.json",
+ "title": "integration manager",
+ "grid": {
+ "x": 137.1,
+ "y": 84.0
+ },
+ "nodes": [
+ {
+ "id": "developer",
+ "kind": "tree",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 1,
+ "row": 0,
+ "dx": 5.11
+ },
+ {
+ "id": "developer-2",
+ "kind": "commit",
+ "label": [
+ "developer",
+ "private"
+ ],
+ "col": 1,
+ "row": 1,
+ "dx": 4.9
+ },
+ {
+ "id": "developer-3",
+ "kind": "commit",
+ "label": [
+ "developer",
+ "private"
+ ],
+ "col": 1,
+ "row": 1,
+ "dx": 4.9
+ },
+ {
+ "id": "developer-4",
+ "kind": "commit",
+ "label": [
+ "developer",
+ "private"
+ ],
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "blessed",
+ "kind": "blob",
+ "label": [
+ "blessed",
+ "repository"
+ ],
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "integration",
+ "kind": "ref",
+ "label": [
+ "integration",
+ "manager"
+ ],
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "developer-5",
+ "kind": "tree",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "developer-6",
+ "kind": "tree",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 1,
+ "row": 0,
+ "dx": 5.11
+ }
+ ],
+ "edges": [
+ { "from": "developer-2", "to": "developer", "arrow": "forward" },
+ { "from": "developer-4", "to": "developer-5", "arrow": "forward" },
+ { "from": "integration", "to": "blessed", "arrow": "forward" },
+ { "from": "integration", "to": "developer", "arrow": "forward", "route": "diagonal" },
+ { "from": "integration", "to": "developer-5", "arrow": "forward", "route": "diagonal" }
+ ]
+}
diff --git a/figures/interesting-rebase-1.json b/figures/interesting-rebase-1.json
new file mode 100644
index 0000000..b06e516
--- /dev/null
+++ b/figures/interesting-rebase-1.json
@@ -0,0 +1,113 @@
+{
+ "$schema": "./_schema.json",
+ "title": "interesting rebase 1",
+ "grid": {
+ "x": 127.2,
+ "y": 60.9
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dy": 2.7
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dy": 2.7
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 3,
+ "row": 4,
+ "dy": -1.1
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9",
+ "col": 4,
+ "row": 4,
+ "dy": -1.1
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 4,
+ "row": 2,
+ "dy": 2.7
+ },
+ {
+ "id": "server",
+ "kind": "ref",
+ "label": "server",
+ "col": 4,
+ "row": 3,
+ "dy": 3.8
+ },
+ {
+ "id": "client",
+ "kind": "ref",
+ "label": "client",
+ "col": 4,
+ "row": 5
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c10", "to": "c4" },
+ { "from": "c8", "to": "c3", "route": "diagonal" },
+ { "from": "c9", "to": "c8" },
+ { "from": "master", "to": "c6", "arrow": "forward" },
+ { "from": "server", "to": "c10", "arrow": "forward" },
+ { "from": "client", "to": "c9", "arrow": "forward" }
+ ]
+}
diff --git a/figures/interesting-rebase-2.json b/figures/interesting-rebase-2.json
new file mode 100644
index 0000000..7d37f55
--- /dev/null
+++ b/figures/interesting-rebase-2.json
@@ -0,0 +1,128 @@
+{
+ "$schema": "./_schema.json",
+ "title": "interesting rebase 2",
+ "grid": {
+ "x": 127.2,
+ "y": 60.6
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8'",
+ "col": 4,
+ "row": 1
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9'",
+ "col": 5,
+ "row": 1
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dy": 3.3
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dy": 3.3
+ },
+ {
+ "id": "c8-2",
+ "kind": "commit",
+ "label": "C8",
+ "col": 3,
+ "row": 4
+ },
+ {
+ "id": "c9-2",
+ "kind": "commit",
+ "label": "C9",
+ "col": 4,
+ "row": 4
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 4,
+ "row": 2,
+ "dy": 3.3
+ },
+ {
+ "id": "server",
+ "kind": "ref",
+ "label": "server",
+ "col": 4,
+ "row": 3,
+ "dy": 4.7
+ },
+ {
+ "id": "client",
+ "kind": "ref",
+ "label": "client",
+ "col": 5,
+ "row": 2,
+ "dy": 1.3
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c8", "to": "c6" },
+ { "from": "c9", "to": "c8" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c10", "to": "c4" },
+ { "from": "c8-2", "to": "c3", "route": "diagonal" },
+ { "from": "c9-2", "to": "c8-2" },
+ { "from": "master", "to": "c6", "arrow": "forward" },
+ { "from": "server", "to": "c10", "arrow": "forward" },
+ { "from": "client", "to": "c9", "arrow": "forward" }
+ ]
+}
diff --git a/figures/interesting-rebase-3.json b/figures/interesting-rebase-3.json
new file mode 100644
index 0000000..25129ae
--- /dev/null
+++ b/figures/interesting-rebase-3.json
@@ -0,0 +1,114 @@
+{
+ "$schema": "./_schema.json",
+ "title": "interesting rebase 3",
+ "grid": {
+ "x": 127.2,
+ "y": 62.2
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": -1.7
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dy": -1.7
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1,
+ "dy": -1.7
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 0
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 1,
+ "dy": -1.7
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8'",
+ "col": 4,
+ "row": 1,
+ "dy": -1.7
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9'",
+ "col": 5,
+ "row": 1,
+ "dy": -1.7
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "server",
+ "kind": "ref",
+ "label": "server",
+ "col": 4,
+ "row": 3
+ },
+ {
+ "id": "client",
+ "kind": "ref",
+ "label": "client",
+ "col": 5,
+ "row": 2,
+ "dy": -1.9
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c8", "to": "c6" },
+ { "from": "c9", "to": "c8" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c10", "to": "c4" },
+ { "from": "master", "to": "c9", "arrow": "forward" },
+ { "from": "server", "to": "c10", "arrow": "forward" },
+ { "from": "client", "to": "c9", "arrow": "forward" }
+ ]
+}
diff --git a/figures/interesting-rebase-4.json b/figures/interesting-rebase-4.json
new file mode 100644
index 0000000..ab3ffd8
--- /dev/null
+++ b/figures/interesting-rebase-4.json
@@ -0,0 +1,154 @@
+{
+ "$schema": "./_schema.json",
+ "title": "interesting rebase 4",
+ "grid": {
+ "x": 127.4,
+ "y": 61.9
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": -1.4
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dy": -1.4
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1,
+ "dx": -0.8,
+ "dy": -1.4
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 0,
+ "dx": -1.2
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 1,
+ "dy": -1.4
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8'",
+ "col": 4,
+ "row": 1,
+ "dx": -0.6,
+ "dy": -1.4
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9'",
+ "col": 5,
+ "row": 1,
+ "dx": -1.0,
+ "dy": -1.4
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3'",
+ "col": 6,
+ "row": 1,
+ "dx": -1.2,
+ "dy": -1.4
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 7,
+ "row": 1,
+ "dx": -0.8,
+ "dy": -1.4
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10'",
+ "col": 8,
+ "row": 1,
+ "dy": -1.4
+ },
+ {
+ "id": "c3-2",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dx": -0.8,
+ "dy": 0.7
+ },
+ {
+ "id": "c4-2",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dy": 0.7
+ },
+ {
+ "id": "c10-2",
+ "kind": "commit",
+ "label": "C10",
+ "col": 4,
+ "row": 2,
+ "dx": -0.6,
+ "dy": 0.7
+ },
+ {
+ "id": "server",
+ "kind": "ref",
+ "label": "server",
+ "col": 8,
+ "row": 2,
+ "dy": -1.3
+ },
+ {
+ "id": "client",
+ "kind": "ref",
+ "label": "client",
+ "col": 5,
+ "row": 2,
+ "dx": -1.2,
+ "dy": -1.3
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c8", "to": "c6" },
+ { "from": "c9", "to": "c8" },
+ { "from": "c3", "to": "c9", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c10", "to": "c4" },
+ { "from": "c3-2", "to": "c2", "route": "diagonal" },
+ { "from": "c4-2", "to": "c3-2" },
+ { "from": "c10-2", "to": "c4-2" },
+ { "from": "master", "to": "c10", "arrow": "forward" },
+ { "from": "server", "to": "c10-2", "arrow": "forward" },
+ { "from": "client", "to": "c9", "arrow": "forward" }
+ ]
+}
diff --git a/figures/interesting-rebase-5.json b/figures/interesting-rebase-5.json
new file mode 100644
index 0000000..5a9902b
--- /dev/null
+++ b/figures/interesting-rebase-5.json
@@ -0,0 +1,97 @@
+{
+ "$schema": "./_schema.json",
+ "title": "interesting rebase 5",
+ "grid": {
+ "x": 127.4,
+ "y": 60.5
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1,
+ "dx": -0.8
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 8,
+ "row": 0,
+ "dx": -0.5
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8'",
+ "col": 4,
+ "row": 1,
+ "dx": -0.6
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9'",
+ "col": 5,
+ "row": 1,
+ "dx": -1.0
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3'",
+ "col": 6,
+ "row": 1,
+ "dx": -1.4
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 7,
+ "row": 1,
+ "dx": -0.8
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10'",
+ "col": 8,
+ "row": 1
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c8", "to": "c6" },
+ { "from": "c9", "to": "c8" },
+ { "from": "c3", "to": "c9", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c10", "to": "c4" },
+ { "from": "master", "to": "c10", "arrow": "forward" }
+ ]
+}
diff --git a/figures/large-merges-1.json b/figures/large-merges-1.json
new file mode 100644
index 0000000..2000e62
--- /dev/null
+++ b/figures/large-merges-1.json
@@ -0,0 +1,167 @@
+{
+ "$schema": "./_schema.json",
+ "title": "large merges 1",
+ "grid": {
+ "x": 123.2,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0,
+ "dx": -4.2
+ },
+ {
+ "id": "tv-rebase-stat",
+ "kind": "ref",
+ "label": "tv/rebase-stat",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "jk-clone-checkout",
+ "kind": "ref",
+ "label": "jk/clone-checkout",
+ "col": 4,
+ "row": 3
+ },
+ {
+ "id": "db-push-cleanup",
+ "kind": "ref",
+ "label": "db/push-cleanup",
+ "col": 4,
+ "row": 4
+ },
+ {
+ "id": "js-notes",
+ "kind": "ref",
+ "label": "js/notes",
+ "col": 4,
+ "row": 5
+ },
+ {
+ "id": "ps-blame",
+ "kind": "ref",
+ "label": "ps/blame",
+ "col": 4,
+ "row": 6
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dx": -4.2
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dx": -8.4
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 3,
+ "dx": -8.4
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 2,
+ "row": 4,
+ "dx": -8.4
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9",
+ "col": 2,
+ "row": 5,
+ "dx": -8.4
+ },
+ {
+ "id": "c11",
+ "kind": "commit",
+ "label": "C11",
+ "col": 2,
+ "row": 6,
+ "dx": -8.4
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dx": -11.6
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 3,
+ "dx": -11.6
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 3,
+ "row": 4,
+ "dx": -11.6
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 3,
+ "row": 5,
+ "dx": -11.6
+ },
+ {
+ "id": "c12",
+ "kind": "commit",
+ "label": "C12",
+ "col": 3,
+ "row": 6,
+ "dx": -11.6
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "master", "to": "c2", "arrow": "forward" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "tv-rebase-stat", "to": "c4", "arrow": "forward" },
+ { "from": "c5", "to": "c2", "route": "diagonal" },
+ { "from": "c6", "to": "c5" },
+ { "from": "jk-clone-checkout", "to": "c6", "arrow": "forward" },
+ { "from": "c7", "to": "c2", "route": "diagonal" },
+ { "from": "c8", "to": "c7" },
+ { "from": "db-push-cleanup", "to": "c8", "arrow": "forward" },
+ { "from": "c9", "to": "c2", "route": "diagonal" },
+ { "from": "c10", "to": "c9" },
+ { "from": "js-notes", "to": "c10", "arrow": "forward" },
+ { "from": "c11", "to": "c2", "route": "diagonal" },
+ { "from": "c12", "to": "c11" },
+ { "from": "ps-blame", "to": "c12", "arrow": "forward" }
+ ]
+}
diff --git a/figures/large-merges-2.json b/figures/large-merges-2.json
new file mode 100644
index 0000000..0538810
--- /dev/null
+++ b/figures/large-merges-2.json
@@ -0,0 +1,252 @@
+{
+ "$schema": "./_schema.json",
+ "title": "large merges 2",
+ "grid": {
+ "x": 87.3,
+ "y": 50.4
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 3,
+ "dx": 1.3,
+ "dy": -18.2
+ },
+ {
+ "id": "tv-rebase-stat",
+ "kind": "ref",
+ "label": "tv/rebase-stat",
+ "col": 6,
+ "row": 6,
+ "dx": -23.9,
+ "dy": -5.4
+ },
+ {
+ "id": "jk-clone-checkout",
+ "kind": "ref",
+ "label": "jk/clone-checkout",
+ "col": 6,
+ "row": 7,
+ "dx": -24.0,
+ "dy": -2.8
+ },
+ {
+ "id": "db-push-cleanup",
+ "kind": "ref",
+ "label": "db/push-cleanup",
+ "col": 6,
+ "row": 8,
+ "dx": -24.0
+ },
+ {
+ "id": "js-notes",
+ "kind": "ref",
+ "label": "js/notes",
+ "col": 6,
+ "row": 0,
+ "dx": -24.0
+ },
+ {
+ "id": "seen",
+ "kind": "ref",
+ "label": "seen",
+ "col": 8,
+ "row": 3,
+ "dy": 9.8
+ },
+ {
+ "id": "next",
+ "kind": "ref",
+ "label": "next",
+ "col": 8,
+ "row": 4,
+ "dy": 4.4
+ },
+ {
+ "id": "ps-blame",
+ "kind": "ref",
+ "label": "ps/blame",
+ "col": 6,
+ "row": 1,
+ "dx": -24.1,
+ "dy": 2.6
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 4,
+ "dy": -17.6
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 4,
+ "dx": 31.7,
+ "dy": -17.6
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 6,
+ "dx": -23.9,
+ "dy": -5.4
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 3,
+ "row": 7,
+ "dx": -23.9,
+ "dy": -2.8
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 3,
+ "row": 8,
+ "dx": -23.9
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9",
+ "col": 3,
+ "row": 0,
+ "dx": -23.9
+ },
+ {
+ "id": "c11",
+ "kind": "commit",
+ "label": "C11",
+ "col": 3,
+ "row": 1,
+ "dx": -23.9,
+ "dy": 2.6
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 4,
+ "row": 6,
+ "dx": 8.8,
+ "dy": -5.4
+ },
+ {
+ "id": "c16",
+ "kind": "commit",
+ "label": "C16",
+ "col": 5,
+ "row": 3,
+ "dx": 40.5,
+ "dy": 9.8
+ },
+ {
+ "id": "c17",
+ "kind": "commit",
+ "label": "C17",
+ "col": 7,
+ "row": 3,
+ "dx": -15.3,
+ "dy": 9.8
+ },
+ {
+ "id": "c13",
+ "kind": "commit",
+ "label": "C13",
+ "col": 4,
+ "row": 4,
+ "dx": 8.8,
+ "dy": 4.4
+ },
+ {
+ "id": "c14",
+ "kind": "commit",
+ "label": "C14",
+ "col": 5,
+ "row": 4,
+ "dx": 40.5,
+ "dy": 4.4
+ },
+ {
+ "id": "c15",
+ "kind": "commit",
+ "label": "C15",
+ "col": 7,
+ "row": 4,
+ "dx": -15.1,
+ "dy": 4.4
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 4,
+ "row": 7,
+ "dx": 8.8,
+ "dy": -2.8
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 4,
+ "row": 8,
+ "dx": 8.8
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 4,
+ "row": 0,
+ "dx": 8.8
+ },
+ {
+ "id": "c12",
+ "kind": "commit",
+ "label": "C12",
+ "col": 4,
+ "row": 1,
+ "dx": 8.8,
+ "dy": 2.6
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "master", "to": "c2", "arrow": "forward" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "tv-rebase-stat", "to": "c4", "arrow": "forward" },
+ { "from": "c5", "to": "c2", "route": "diagonal" },
+ { "from": "c6", "to": "c5" },
+ { "from": "jk-clone-checkout", "to": "c6", "arrow": "forward" },
+ { "from": "c7", "to": "c2", "route": "diagonal" },
+ { "from": "c8", "to": "c7" },
+ { "from": "db-push-cleanup", "to": "c8", "arrow": "forward" },
+ { "from": "c9", "to": "c2", "route": "diagonal" },
+ { "from": "c10", "to": "c9" },
+ { "from": "js-notes", "to": "c10", "arrow": "forward" },
+ { "from": "c11", "to": "c2", "route": "diagonal" },
+ { "from": "c12", "to": "c11" },
+ { "from": "ps-blame", "to": "c12", "arrow": "forward" },
+ { "from": "c16", "to": "c2", "route": "diagonal" },
+ { "from": "c17", "to": "c16" },
+ { "from": "seen", "to": "c17", "arrow": "forward" },
+ { "from": "c13", "to": "c2", "route": "diagonal" },
+ { "from": "c14", "to": "c13" },
+ { "from": "c15", "to": "c14" },
+ { "from": "next", "to": "c15", "arrow": "forward" }
+ ]
+}
diff --git a/figures/lifecycle.json b/figures/lifecycle.json
new file mode 100644
index 0000000..da0f3ed
--- /dev/null
+++ b/figures/lifecycle.json
@@ -0,0 +1,40 @@
+{
+ "$schema": "./_schema.json",
+ "title": "lifecycle",
+ "layout": "absolute",
+ "canvas": {
+ "w": 516.0,
+ "h": 227.0
+ },
+ "nodes": [
+ {
+ "id": "modified",
+ "kind": "goldref",
+ "label": "Modified",
+ "x": 271.4,
+ "y": 0.0
+ },
+ {
+ "id": "untracked",
+ "kind": "commit",
+ "label": "Untracked",
+ "x": 0.0,
+ "y": 0.0
+ },
+ {
+ "id": "unmodified",
+ "kind": "tree",
+ "label": "Unmodified",
+ "x": 135.61,
+ "y": 0.0
+ },
+ {
+ "id": "staged",
+ "kind": "ref",
+ "label": "Staged",
+ "x": 407.05,
+ "y": 0.0
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/local.json b/figures/local.json
new file mode 100644
index 0000000..a1b5b37
--- /dev/null
+++ b/figures/local.json
@@ -0,0 +1,43 @@
+{
+ "$schema": "./_schema.json",
+ "title": "local",
+ "grid": {
+ "x": 165.0,
+ "y": 55.0
+ },
+ "nodes": [
+ {
+ "id": "file",
+ "kind": "tree",
+ "label": "File",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "version-3",
+ "kind": "commit",
+ "label": "Version 3",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "version-2",
+ "kind": "commit",
+ "label": "Version 2",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "version-1",
+ "kind": "commit",
+ "label": "Version 1",
+ "col": 1,
+ "row": 2
+ }
+ ],
+ "edges": [
+ { "from": "version-3", "to": "file", "arrow": "forward" },
+ { "from": "version-2", "to": "version-3", "arrow": "forward" },
+ { "from": "version-1", "to": "version-2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/lr-branches-1.json b/figures/lr-branches-1.json
new file mode 100644
index 0000000..d1e565e
--- /dev/null
+++ b/figures/lr-branches-1.json
@@ -0,0 +1,91 @@
+{
+ "$schema": "./_schema.json",
+ "title": "lr branches 1",
+ "grid": {
+ "x": 118.0,
+ "y": 70.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 4,
+ "row": 1
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 5,
+ "row": 1
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 6,
+ "row": 1
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "develop",
+ "kind": "ref",
+ "label": "develop",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "topic",
+ "kind": "ref",
+ "label": "topic",
+ "col": 6,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c5", "to": "c4" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c7", "to": "c6" },
+ { "from": "master", "to": "c1", "arrow": "forward" },
+ { "from": "develop", "to": "c5", "arrow": "forward" },
+ { "from": "topic", "to": "c7", "arrow": "forward" }
+ ]
+}
diff --git a/figures/lr-branches-2.json b/figures/lr-branches-2.json
new file mode 100644
index 0000000..748a49f
--- /dev/null
+++ b/figures/lr-branches-2.json
@@ -0,0 +1,76 @@
+{
+ "$schema": "./_schema.json",
+ "title": "lr branches 2",
+ "grid": {
+ "x": 138.4,
+ "y": 146.8
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dx": 14.0,
+ "dy": -7.33
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 1,
+ "dx": 11.16,
+ "dy": -7.33
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 1,
+ "dx": 8.46,
+ "dy": -7.33
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 4,
+ "row": 1,
+ "dx": 5.48,
+ "dy": -7.35
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 5,
+ "row": 2,
+ "dx": 2.68
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 6,
+ "row": 2
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1", "route": "diagonal" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c5", "to": "c4" },
+ { "from": "c6", "to": "c5", "route": "diagonal" },
+ { "from": "c7", "to": "c6" }
+ ]
+}
diff --git a/figures/managed-team-1.json b/figures/managed-team-1.json
new file mode 100644
index 0000000..c2bb374
--- /dev/null
+++ b/figures/managed-team-1.json
@@ -0,0 +1,80 @@
+{
+ "$schema": "./_schema.json",
+ "title": "managed team 1",
+ "grid": {
+ "x": 119.0,
+ "y": 54.7
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "node_33009",
+ "kind": "jessica",
+ "label": "33009",
+ "col": 2,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "node_85127",
+ "kind": "jessica",
+ "label": "85127",
+ "col": 3,
+ "row": 2,
+ "dy": -2.9
+ },
+ {
+ "id": "e5b0f",
+ "kind": "jessica",
+ "label": "e5b0f",
+ "col": 2,
+ "row": 2,
+ "dy": -2.9
+ },
+ {
+ "id": "featureb",
+ "kind": "ref",
+ "label": "featureB",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "featurea",
+ "kind": "ref",
+ "label": "featureA",
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_33009", "to": "node_1edee" },
+ { "from": "e5b0f", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_85127", "to": "e5b0f" },
+ { "from": "master", "to": "node_1edee", "arrow": "forward" },
+ { "from": "featurea", "to": "node_33009", "arrow": "forward" },
+ { "from": "featureb", "to": "node_85127", "arrow": "forward" }
+ ]
+}
diff --git a/figures/managed-team-2.json b/figures/managed-team-2.json
new file mode 100644
index 0000000..8b91e1a
--- /dev/null
+++ b/figures/managed-team-2.json
@@ -0,0 +1,147 @@
+{
+ "$schema": "./_schema.json",
+ "title": "managed team 2",
+ "grid": {
+ "x": 95.2,
+ "y": 45.8
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 3,
+ "dy": -20.9
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 3,
+ "dx": 23.8,
+ "dy": -20.9
+ },
+ {
+ "id": "node_33009",
+ "kind": "jessica",
+ "label": "33009",
+ "col": 3,
+ "row": 3,
+ "dx": -47.6,
+ "dy": -20.9
+ },
+ {
+ "id": "node_aad88",
+ "kind": "john",
+ "label": "aad88",
+ "col": 4,
+ "row": 3,
+ "dx": -23.8,
+ "dy": -20.9
+ },
+ {
+ "id": "node_774b3",
+ "kind": "jessica",
+ "label": "774b3",
+ "col": 5,
+ "row": 3,
+ "dy": -20.9
+ },
+ {
+ "id": "node_85127",
+ "kind": "jessica",
+ "label": "85127",
+ "col": 4,
+ "row": 4,
+ "dx": -23.8,
+ "dy": -18.7
+ },
+ {
+ "id": "node_cd685",
+ "kind": "jessica",
+ "label": "cd685",
+ "col": 5,
+ "row": 4,
+ "dy": -18.7
+ },
+ {
+ "id": "node_fba9a",
+ "kind": "josie",
+ "label": "fba9a",
+ "col": 3,
+ "row": 5,
+ "dy": 30
+ },
+ {
+ "id": "node_e5b0f",
+ "kind": "jessica",
+ "label": "e5b0f",
+ "col": 3,
+ "row": 4,
+ "dx": -47.6,
+ "dy": -18.7
+ },
+ {
+ "id": "featureb",
+ "kind": "ref",
+ "label": "featureB",
+ "col": 5,
+ "row": 5,
+ "dy": -7.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 1,
+ "dx": 23.7,
+ "dy": 12.2
+ },
+ {
+ "id": "featurea",
+ "kind": "ref",
+ "label": "featureA",
+ "col": 5,
+ "row": 1,
+ "dy": 12.2
+ },
+ {
+ "id": "origin",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "featureA"
+ ],
+ "col": 5,
+ "row": 0
+ },
+ {
+ "id": "origin-2",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "featureBee"
+ ],
+ "col": 5,
+ "row": 6
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_33009", "to": "node_1edee" },
+ { "from": "node_aad88", "to": "node_33009" },
+ { "from": "node_774b3", "to": "node_aad88" },
+ { "from": "node_e5b0f", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_fba9a", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_85127", "to": "node_e5b0f" },
+ { "from": "node_cd685", "to": "node_85127" },
+ { "from": "node_cd685", "to": "node_fba9a", "route": "diagonal" },
+ { "from": "featurea", "to": "node_774b3", "arrow": "forward" },
+ { "from": "origin", "to": "node_774b3", "arrow": "forward" },
+ { "from": "featureb", "to": "node_cd685", "arrow": "forward" },
+ { "from": "origin-2", "to": "node_cd685", "arrow": "forward" }
+ ]
+}
diff --git a/figures/managed-team-3.json b/figures/managed-team-3.json
new file mode 100644
index 0000000..0bf8da6
--- /dev/null
+++ b/figures/managed-team-3.json
@@ -0,0 +1,176 @@
+{
+ "$schema": "./_schema.json",
+ "title": "managed team 3",
+ "grid": {
+ "x": 99.2,
+ "y": 45.8
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 3,
+ "dy": -20.81
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 3,
+ "dx": 19.8,
+ "dy": -20.81
+ },
+ {
+ "id": "node_33009",
+ "kind": "jessica",
+ "label": "33009",
+ "col": 2,
+ "row": 3,
+ "dx": 39.6,
+ "dy": -20.81
+ },
+ {
+ "id": "node_aad88",
+ "kind": "john",
+ "label": "aad88",
+ "col": 4,
+ "row": 3,
+ "dx": -39.8,
+ "dy": -20.81
+ },
+ {
+ "id": "node_774b3",
+ "kind": "jessica",
+ "label": "774b3",
+ "col": 5,
+ "row": 3,
+ "dx": -20.0,
+ "dy": -20.81
+ },
+ {
+ "id": "node_5399e",
+ "kind": "commit",
+ "label": "5399e",
+ "col": 6,
+ "row": 3,
+ "dy": -20.81
+ },
+ {
+ "id": "node_85127",
+ "kind": "jessica",
+ "label": "85127",
+ "col": 4,
+ "row": 4,
+ "dx": -39.8,
+ "dy": -18.61
+ },
+ {
+ "id": "node_cd685",
+ "kind": "jessica",
+ "label": "cd685",
+ "col": 5,
+ "row": 4,
+ "dx": -20.0,
+ "dy": -18.61
+ },
+ {
+ "id": "node_fba9a",
+ "kind": "josie",
+ "label": "fba9a",
+ "col": 3,
+ "row": 5,
+ "dy": 30
+ },
+ {
+ "id": "node_e5b0f",
+ "kind": "jessica",
+ "label": "e5b0f",
+ "col": 2,
+ "row": 4,
+ "dx": 39.6,
+ "dy": -18.61
+ },
+ {
+ "id": "featureb",
+ "kind": "ref",
+ "label": "featureB",
+ "col": 5,
+ "row": 5,
+ "dx": -20.0,
+ "dy": -6.91
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 1,
+ "dx": 19.8,
+ "dy": 12.29
+ },
+ {
+ "id": "featurea",
+ "kind": "ref",
+ "label": "featureA",
+ "col": 5,
+ "row": 1,
+ "dx": -20.0,
+ "dy": 12.29
+ },
+ {
+ "id": "origin-bee",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "featureBee"
+ ],
+ "col": 5,
+ "row": 6,
+ "dx": -20.0
+ },
+ {
+ "id": "origin-a",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "featureA"
+ ],
+ "col": 5,
+ "row": 0,
+ "dx": -20.0
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 6,
+ "row": 1,
+ "dy": 12.29
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_33009", "to": "node_1edee" },
+ { "from": "node_aad88", "to": "node_33009" },
+ { "from": "node_774b3", "to": "node_aad88" },
+ { "from": "node_5399e", "to": "node_774b3" },
+ { "from": "node_5399e", "to": "node_cd685", "route": "diagonal" },
+ { "from": "node_e5b0f", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_fba9a", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_85127", "to": "node_e5b0f" },
+ { "from": "node_cd685", "to": "node_85127" },
+ { "from": "node_cd685", "to": "node_fba9a", "route": "diagonal" },
+
+ { "from": "featurea", "to": "node_774b3", "arrow": "forward" },
+ { "from": "origin-a", "to": "node_774b3", "arrow": "forward" },
+ { "from": "featureb", "to": "node_cd685", "arrow": "forward" },
+ { "from": "origin-bee", "to": "node_cd685", "arrow": "forward" },
+ { "from": "origin-master", "to": "node_5399e", "arrow": "forward" }
+ ]
+}
diff --git a/figures/managed-team-flow.json b/figures/managed-team-flow.json
new file mode 100644
index 0000000..e34df40
--- /dev/null
+++ b/figures/managed-team-flow.json
@@ -0,0 +1,77 @@
+{
+ "$schema": "./_schema.json",
+ "title": "managed team flow",
+ "grid": {
+ "x": 98.8,
+ "y": 595.0
+ },
+ "nodes": [
+ {
+ "id": "john",
+ "kind": "ref",
+ "label": "John",
+ "col": 1,
+ "row": 0,
+ "dx": -3.8
+ },
+ {
+ "id": "john-2",
+ "kind": "ref",
+ "label": "John",
+ "col": 1,
+ "row": 1,
+ "dx": -3.8
+ },
+ {
+ "id": "josie",
+ "kind": "ref",
+ "label": "Josie",
+ "col": 2,
+ "row": 0,
+ "dx": -7.6
+ },
+ {
+ "id": "josie-2",
+ "kind": "ref",
+ "label": "Josie",
+ "col": 2,
+ "row": 1,
+ "dx": -7.6
+ },
+ {
+ "id": "jessica",
+ "kind": "ref",
+ "label": "Jessica",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "jessica-2",
+ "kind": "ref",
+ "label": "Jessica",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "server",
+ "kind": "ref",
+ "label": [
+ "server:",
+ "featureA"
+ ],
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "server-2",
+ "kind": "ref",
+ "label": [
+ "server:",
+ "featureA"
+ ],
+ "col": 3,
+ "row": 1
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/merging-workflows-1.json b/figures/merging-workflows-1.json
new file mode 100644
index 0000000..49368ae
--- /dev/null
+++ b/figures/merging-workflows-1.json
@@ -0,0 +1,104 @@
+{
+ "$schema": "./_schema.json",
+ "title": "merging workflows 1",
+ "grid": {
+ "x": 118.0,
+ "y": 53.7
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": 3.3
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dx": 1.0,
+ "dy": 3.3
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 2,
+ "row": 1,
+ "dx": 2.0,
+ "dy": 3.3
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dx": 2.0,
+ "dy": 0.6
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dx": 3.0,
+ "dy": 0.6
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 3,
+ "dx": 2.0
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 3,
+ "dx": 3.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0,
+ "dx": 2.1
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 4,
+ "row": 2,
+ "dy": 0.6
+ },
+ {
+ "id": "php-client",
+ "kind": "ref",
+ "label": "php_client",
+ "col": 4,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c7", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c5", "to": "c3", "route": "diagonal" },
+ { "from": "c6", "to": "c5" },
+ { "from": "master", "to": "c7", "arrow": "forward" },
+ { "from": "ruby-client", "to": "c4" },
+ { "from": "php-client", "to": "c6" }
+ ]
+}
diff --git a/figures/merging-workflows-2.json b/figures/merging-workflows-2.json
new file mode 100644
index 0000000..59b82f6
--- /dev/null
+++ b/figures/merging-workflows-2.json
@@ -0,0 +1,127 @@
+{
+ "$schema": "./_schema.json",
+ "title": "merging workflows 2",
+ "grid": {
+ "x": 118.4,
+ "y": 54.3
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": 2.7
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dx": 0.6,
+ "dy": 2.7
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 2,
+ "row": 1,
+ "dx": 1.2,
+ "dy": 2.7
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dx": 1.2,
+ "dy": -0.6
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dx": 1.8,
+ "dy": -0.6
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9",
+ "col": 5,
+ "row": 2,
+ "dy": 2.7
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 4,
+ "row": 1,
+ "dx": -0.6,
+ "dy": 2.7
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 3,
+ "dx": 1.2,
+ "dy": -1.9
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 3,
+ "dx": 1.8,
+ "dy": -1.9
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 1
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 4,
+ "row": 2,
+ "dx": -0.8,
+ "dy": -0.6
+ },
+ {
+ "id": "php-client",
+ "kind": "ref",
+ "label": "php_client",
+ "col": 3,
+ "row": 4,
+ "dx": 1.8
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c7", "to": "c2" },
+ { "from": "c8", "to": "c7" },
+ { "from": "c9", "to": "c8", "route": "diagonal" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c5", "to": "c2", "route": "diagonal" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c9", "to": "c6", "route": "diagonal" },
+ { "from": "master", "to": "c9" },
+ { "from": "ruby-client", "to": "c4" },
+ { "from": "php-client", "to": "c6" }
+ ]
+}
diff --git a/figures/merging-workflows-3.json b/figures/merging-workflows-3.json
new file mode 100644
index 0000000..8fa6c22
--- /dev/null
+++ b/figures/merging-workflows-3.json
@@ -0,0 +1,80 @@
+{
+ "$schema": "./_schema.json",
+ "title": "merging workflows 3",
+ "grid": {
+ "x": 119.0,
+ "y": 55.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dy": -2.0
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dy": -2.0
+ },
+ {
+ "id": "develop",
+ "kind": "ref",
+ "label": "develop",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 3,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "master", "to": "c2", "arrow": "forward" },
+ { "from": "develop", "to": "c5", "arrow": "forward" },
+ { "from": "ruby-client", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/merging-workflows-4.json b/figures/merging-workflows-4.json
new file mode 100644
index 0000000..1ae222a
--- /dev/null
+++ b/figures/merging-workflows-4.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "merging workflows 4",
+ "grid": {
+ "x": 118.7,
+ "y": 55.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 1,
+ "dx": 0.6,
+ "dy": 2.0
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 2,
+ "dx": 0.6,
+ "dy": -2.0
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 2,
+ "dx": 0.9,
+ "dy": -2.0
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 4,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "develop",
+ "kind": "ref",
+ "label": "develop",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 3,
+ "row": 3,
+ "dx": 0.9
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c6", "to": "c4", "route": "diagonal" },
+ { "from": "master", "to": "c2", "arrow": "forward" },
+ { "from": "develop", "to": "c6", "arrow": "forward" },
+ { "from": "ruby-client", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/merging-workflows-5.json b/figures/merging-workflows-5.json
new file mode 100644
index 0000000..0593afa
--- /dev/null
+++ b/figures/merging-workflows-5.json
@@ -0,0 +1,97 @@
+{
+ "$schema": "./_schema.json",
+ "title": "merging workflows 5",
+ "grid": {
+ "x": 87.3,
+ "y": 55.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 1,
+ "dx": 31.7,
+ "dy": 2.0
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 3,
+ "row": 1,
+ "dx": -23.9,
+ "dy": 2.0
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dx": -23.9,
+ "dy": -2.0
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 4,
+ "row": 2,
+ "dx": 7.8,
+ "dy": -2.0
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 5,
+ "row": 1,
+ "dx": 38.5,
+ "dy": 2.0
+ },
+ {
+ "id": "develop",
+ "kind": "ref",
+ "label": "develop",
+ "col": 6,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 0,
+ "dx": -10.5
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 4,
+ "row": 3,
+ "dx": 7.8
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c6", "to": "c4", "route": "diagonal" },
+ { "from": "master", "to": "c6", "route": "diagonal" },
+ { "from": "develop", "to": "c6", "route": "diagonal" },
+ { "from": "ruby-client", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/perils-of-rebasing-1.json b/figures/perils-of-rebasing-1.json
new file mode 100644
index 0000000..bc43880
--- /dev/null
+++ b/figures/perils-of-rebasing-1.json
@@ -0,0 +1,72 @@
+{
+ "$schema": "./_schema.json",
+ "title": "perils of rebasing 1",
+ "grid": {
+ "x": 91.2,
+ "y": 97.5
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0,
+ "dx": 44.15
+ },
+ {
+ "id": "c1-2",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": 46.5
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 2,
+ "dx": 23.85
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 3,
+ "row": 2,
+ "dx": -41.55
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "teamone-master",
+ "kind": "ref",
+ "label": "teamone/master",
+ "col": 1,
+ "row": 1,
+ "dx": 44.35,
+ "dy": 46.5
+ }
+ ],
+ "edges": [
+ { "from": "master", "to": "c1", "arrow": "forward" },
+ { "from": "c2", "to": "c1-2", "route": "diagonal" },
+ { "from": "c3", "to": "c2" },
+ { "from": "master-2", "to": "c3", "arrow": "forward" },
+ { "from": "teamone-master", "to": "c2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/perils-of-rebasing-2.json b/figures/perils-of-rebasing-2.json
new file mode 100644
index 0000000..8e76e92
--- /dev/null
+++ b/figures/perils-of-rebasing-2.json
@@ -0,0 +1,142 @@
+{
+ "$schema": "./_schema.json",
+ "title": "perils of rebasing 2",
+ "grid": {
+ "x": 96.5,
+ "y": 68.8
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": -18.79
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 1,
+ "dx": -21.3,
+ "dy": -18.79
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 1,
+ "dx": 21.6,
+ "dy": -18.79
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 0,
+ "dx": 21.78
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 1,
+ "dx": 43.3,
+ "dy": -18.79
+ },
+ {
+ "id": "c1-2",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 3,
+ "dy": 18.61
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 4,
+ "dx": 43.1
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 4,
+ "row": 4,
+ "dx": -31.9
+ },
+ {
+ "id": "c4-2",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 3,
+ "dx": 21.6,
+ "dy": 18.66
+ },
+ {
+ "id": "c5-2",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 3,
+ "dx": 21.7,
+ "dy": -31.49
+ },
+ {
+ "id": "c6-2",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 3,
+ "dx": 43.1,
+ "dy": 18.61
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 4
+ },
+ {
+ "id": "teamone-master",
+ "kind": "ref",
+ "label": "teamone/master",
+ "col": 4,
+ "row": 3,
+ "dx": -21.4,
+ "dy": 18.61
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 4,
+ "dx": 21.6
+ }
+ ],
+ "edges": [
+ { "from": "c4", "to": "c1" },
+ { "from": "c5", "to": "c4", "route": "diagonal" },
+ { "from": "c6", "to": "c4", "route": "diagonal" },
+ { "from": "master", "to": "c6", "arrow": "forward" },
+ { "from": "c4-2", "to": "c1-2" },
+ { "from": "c5-2", "to": "c4-2", "route": "diagonal" },
+ { "from": "c6-2", "to": "c4-2", "route": "diagonal" },
+ { "from": "teamone-master", "to": "c6-2", "arrow": "forward" },
+ { "from": "c2", "to": "c1-2", "route": "diagonal" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c7", "to": "c3", "route": "diagonal" },
+ { "from": "master-2", "to": "c7", "arrow": "forward" }
+ ]
+}
diff --git a/figures/perils-of-rebasing-3.json b/figures/perils-of-rebasing-3.json
new file mode 100644
index 0000000..bd407cf
--- /dev/null
+++ b/figures/perils-of-rebasing-3.json
@@ -0,0 +1,160 @@
+{
+ "$schema": "./_schema.json",
+ "title": "perils of rebasing 3",
+ "grid": {
+ "x": 96.5,
+ "y": 66.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": -16.01
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 0,
+ "dx": -21.5
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 2,
+ "row": 0,
+ "dx": 43.0
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 0,
+ "dx": 21.51
+ },
+ {
+ "id": "c4-2",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 1,
+ "dx": 21.51,
+ "dy": -16.01
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 1,
+ "dx": 43.01,
+ "dy": -16.01
+ },
+ {
+ "id": "c1-2",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 3,
+ "dy": 15.89
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 4,
+ "dx": 21.51
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 4,
+ "dx": 43.0
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 4,
+ "row": 4,
+ "dx": -32.04
+ },
+ {
+ "id": "c4-3",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 2,
+ "row": 2,
+ "dx": 43.0,
+ "dy": 31.89
+ },
+ {
+ "id": "c4-4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 3,
+ "dx": 21.5,
+ "dy": 15.89
+ },
+ {
+ "id": "c5-2",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 2,
+ "dx": 21.5,
+ "dy": 31.89
+ },
+ {
+ "id": "c6-2",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 3,
+ "dx": 43.0,
+ "dy": 15.89
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 4
+ },
+ {
+ "id": "teamone-master",
+ "kind": "ref",
+ "label": "teamone/master",
+ "col": 4,
+ "row": 2,
+ "dx": -21.54,
+ "dy": 31.9
+ }
+ ],
+ "edges": [
+ { "from": "c4-2", "to": "c1" },
+ { "from": "c5", "to": "c4-2", "route": "diagonal" },
+ { "from": "c6", "to": "c4-2", "route": "diagonal" },
+ { "from": "c4", "to": "c5", "route": "diagonal" },
+ { "from": "master", "to": "c4", "arrow": "forward" },
+ { "from": "c4-4", "to": "c1-2" },
+ { "from": "c5-2", "to": "c4-4", "route": "diagonal" },
+ { "from": "c6-2", "to": "c4-4", "route": "diagonal" },
+ { "from": "c4-3", "to": "c5-2", "route": "diagonal" },
+ { "from": "teamone-master", "to": "c4-3", "arrow": "forward" },
+ { "from": "c2", "to": "c1-2", "route": "diagonal" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c7", "to": "c3", "route": "diagonal" },
+ { "from": "master-2", "to": "c7", "arrow": "forward" }
+ ]
+}
diff --git a/figures/perils-of-rebasing-4.json b/figures/perils-of-rebasing-4.json
new file mode 100644
index 0000000..ce584a4
--- /dev/null
+++ b/figures/perils-of-rebasing-4.json
@@ -0,0 +1,169 @@
+{
+ "$schema": "./_schema.json",
+ "title": "perils of rebasing 4",
+ "grid": {
+ "x": 100.1,
+ "y": 66.7
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": -15.69
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 0,
+ "dx": -35.9
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 2,
+ "row": 0,
+ "dx": 35.82
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 0,
+ "dx": 17.9
+ },
+ {
+ "id": "c4-2",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 1,
+ "dx": 17.92,
+ "dy": -15.69
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 1,
+ "dx": 35.8,
+ "dy": -15.69
+ },
+ {
+ "id": "c1-2",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 3,
+ "dy": 15.81
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 4,
+ "dx": 17.79
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 4,
+ "dx": 35.74
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 4,
+ "row": 4,
+ "dx": -46.49
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 5,
+ "row": 4,
+ "dx": -28.6
+ },
+ {
+ "id": "c4-3",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 2,
+ "row": 2,
+ "dx": 35.74,
+ "dy": 31.49
+ },
+ {
+ "id": "c4-4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 3,
+ "dx": 17.82,
+ "dy": 15.81
+ },
+ {
+ "id": "c5-2",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 2,
+ "dx": 17.79,
+ "dy": 31.49
+ },
+ {
+ "id": "c6-2",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 3,
+ "dx": 35.74,
+ "dy": 15.81
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 4
+ },
+ {
+ "id": "teamone-master",
+ "kind": "ref",
+ "label": "teamone/master",
+ "col": 4,
+ "row": 2,
+ "dx": -35.98,
+ "dy": 31.51
+ }
+ ],
+ "edges": [
+ { "from": "c4-2", "to": "c1" },
+ { "from": "c5", "to": "c4-2", "route": "diagonal" },
+ { "from": "c6", "to": "c4-2", "route": "diagonal" },
+ { "from": "c4", "to": "c5", "route": "diagonal" },
+ { "from": "master", "to": "c4", "arrow": "forward" },
+ { "from": "c4-4", "to": "c1-2" },
+ { "from": "c5-2", "to": "c4-4", "route": "diagonal" },
+ { "from": "c6-2", "to": "c4-4", "route": "diagonal" },
+ { "from": "c4-3", "to": "c5-2", "route": "diagonal" },
+ { "from": "teamone-master", "to": "c4-3", "arrow": "forward" },
+ { "from": "c2", "to": "c1-2", "route": "diagonal" },
+ { "from": "c3", "to": "c2" },
+ { "from": "c7", "to": "c3", "route": "diagonal" },
+ { "from": "c8", "to": "c7" },
+ { "from": "master-2", "to": "c8", "arrow": "forward" }
+ ]
+}
diff --git a/figures/perils-of-rebasing-5.json b/figures/perils-of-rebasing-5.json
new file mode 100644
index 0000000..5b12ee7
--- /dev/null
+++ b/figures/perils-of-rebasing-5.json
@@ -0,0 +1,128 @@
+{
+ "$schema": "./_schema.json",
+ "title": "perils of rebasing 5",
+ "grid": {
+ "x": 100.0,
+ "y": 80.3
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 1,
+ "dy": -29.29
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 0,
+ "dx": -37.75
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 2,
+ "row": 0,
+ "dx": 35.79
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 0,
+ "dx": 17.87
+ },
+ {
+ "id": "c4-2",
+ "kind": "commit",
+ "label": "C4",
+ "col": 1,
+ "row": 1,
+ "dx": 17.87,
+ "dy": -29.29
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 1,
+ "dx": 35.79,
+ "dy": -29.29
+ },
+ {
+ "id": "c1-2",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 3
+ },
+ {
+ "id": "c4-3",
+ "kind": "commit",
+ "label": "C4'",
+ "col": 2,
+ "row": 3,
+ "dx": 35.75
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2'",
+ "col": 4,
+ "row": 3,
+ "dx": -46.29
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3'",
+ "col": 5,
+ "row": 3,
+ "dx": -28.3
+ },
+ {
+ "id": "c5-2",
+ "kind": "commit",
+ "label": "C5",
+ "col": 1,
+ "row": 3,
+ "dx": 17.83
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 3
+ },
+ {
+ "id": "teamone-master",
+ "kind": "ref",
+ "label": "teamone/master",
+ "col": 2,
+ "row": 2,
+ "dx": 35.75,
+ "dy": 29.39
+ }
+ ],
+ "edges": [
+ { "from": "c4-2", "to": "c1" },
+ { "from": "c5", "to": "c4-2", "route": "diagonal" },
+ { "from": "c6", "to": "c4-2", "route": "diagonal" },
+ { "from": "c4", "to": "c5", "route": "diagonal" },
+ { "from": "master", "to": "c4", "arrow": "forward" },
+ { "from": "c5-2", "to": "c1-2" },
+ { "from": "c4-3", "to": "c5-2", "route": "diagonal" },
+ { "from": "teamone-master", "to": "c4-3", "arrow": "forward" },
+ { "from": "c2", "to": "c4-3", "route": "diagonal" },
+ { "from": "c3", "to": "c2" },
+ { "from": "master-2", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/public-small-1.json b/figures/public-small-1.json
new file mode 100644
index 0000000..4eb389e
--- /dev/null
+++ b/figures/public-small-1.json
@@ -0,0 +1,100 @@
+{
+ "$schema": "./_schema.json",
+ "title": "public small 1",
+ "grid": {
+ "x": 119.0,
+ "y": 51.3
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 4.7
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 4.7
+ },
+ {
+ "id": "node_33009",
+ "kind": "commit",
+ "label": "33009",
+ "col": 2,
+ "row": 1,
+ "dy": 4.7
+ },
+ {
+ "id": "node_0d708",
+ "kind": "commit",
+ "label": "0d708",
+ "col": 3,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "node_e5b0f",
+ "kind": "commit",
+ "label": "e5b0f",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "node_a2de3",
+ "kind": "commit",
+ "label": "a2de3",
+ "col": 2,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "featurea",
+ "kind": "ref",
+ "label": "featureA",
+ "col": 4,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "featureb",
+ "kind": "ref",
+ "label": "featureB",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "origin",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_33009", "to": "node_1edee" },
+ { "from": "node_a2de3", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_0d708", "to": "node_a2de3" },
+ { "from": "node_e5b0f", "to": "node_1edee", "route": "diagonal" },
+ { "from": "master", "to": "node_1edee" },
+ { "from": "origin", "to": "node_33009" },
+ { "from": "featurea", "to": "node_0d708" },
+ { "from": "featureb", "to": "node_e5b0f" }
+ ]
+}
diff --git a/figures/public-small-2.json b/figures/public-small-2.json
new file mode 100644
index 0000000..04b5b80
--- /dev/null
+++ b/figures/public-small-2.json
@@ -0,0 +1,100 @@
+{
+ "$schema": "./_schema.json",
+ "title": "public small 2",
+ "grid": {
+ "x": 119.0,
+ "y": 51.3
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 4.72
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 4.72
+ },
+ {
+ "id": "node_33009",
+ "kind": "commit",
+ "label": "33009",
+ "col": 2,
+ "row": 1,
+ "dy": 4.72
+ },
+ {
+ "id": "node_5399e",
+ "kind": "commit",
+ "label": "5399e",
+ "col": 4,
+ "row": 2,
+ "dy": 1.42
+ },
+ {
+ "id": "node_e5b0f",
+ "kind": "commit",
+ "label": "e5b0f",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "node_dee6b",
+ "kind": "commit",
+ "label": "dee6b",
+ "col": 3,
+ "row": 2,
+ "dy": 1.42
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "featurea",
+ "kind": "ref",
+ "label": "featureA",
+ "col": 5,
+ "row": 2,
+ "dy": 1.42
+ },
+ {
+ "id": "featureb",
+ "kind": "ref",
+ "label": "featureB",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "origin",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_33009", "to": "node_1edee" },
+ { "from": "node_dee6b", "to": "node_33009", "route": "diagonal" },
+ { "from": "node_5399e", "to": "node_dee6b" },
+ { "from": "node_e5b0f", "to": "node_1edee", "route": "diagonal" },
+ { "from": "master", "to": "node_1edee" },
+ { "from": "origin", "to": "node_33009" },
+ { "from": "featurea", "to": "node_5399e" },
+ { "from": "featureb", "to": "node_e5b0f" }
+ ]
+}
diff --git a/figures/public-small-3.json b/figures/public-small-3.json
new file mode 100644
index 0000000..f4cab18
--- /dev/null
+++ b/figures/public-small-3.json
@@ -0,0 +1,118 @@
+{
+ "$schema": "./_schema.json",
+ "title": "public small 3",
+ "grid": {
+ "x": 118.9,
+ "y": 34.1
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 2,
+ "dy": -12.18
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 2,
+ "dy": -12.18
+ },
+ {
+ "id": "node_33009",
+ "kind": "commit",
+ "label": "33009",
+ "col": 2,
+ "row": 2,
+ "dy": -12.18
+ },
+ {
+ "id": "node_5399e",
+ "kind": "commit",
+ "label": "5399e",
+ "col": 4,
+ "row": 2,
+ "dy": 12.42
+ },
+ {
+ "id": "node_17f4d",
+ "kind": "commit",
+ "label": "17f4d",
+ "col": 3,
+ "row": 1,
+ "dy": -2.92
+ },
+ {
+ "id": "node_e5b0f",
+ "kind": "commit",
+ "label": "e5b0f",
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "node_dee6b",
+ "kind": "commit",
+ "label": "dee6b",
+ "col": 3,
+ "row": 2,
+ "dy": 12.32
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "featurea",
+ "kind": "ref",
+ "label": "featureA",
+ "col": 5,
+ "row": 2,
+ "dy": 12.35
+ },
+ {
+ "id": "featureb",
+ "kind": "ref",
+ "label": "featureB",
+ "col": 3,
+ "row": 4
+ },
+ {
+ "id": "featurebv2",
+ "kind": "ref",
+ "label": "featureBv2",
+ "col": 4,
+ "row": 1,
+ "dy": -2.99
+ },
+ {
+ "id": "origin",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_33009", "to": "node_1edee" },
+ { "from": "node_17f4d", "to": "node_33009", "route": "diagonal" },
+ { "from": "featurebv2", "to": "node_17f4d" },
+ { "from": "node_dee6b", "to": "node_33009", "route": "diagonal" },
+ { "from": "node_5399e", "to": "node_dee6b" },
+ { "from": "node_e5b0f", "to": "node_1edee", "route": "diagonal" },
+ { "from": "master", "to": "node_1edee" },
+ { "from": "origin", "to": "node_33009" },
+ { "from": "featurea", "to": "node_5399e" },
+ { "from": "featureb", "to": "node_e5b0f" }
+ ]
+}
diff --git a/figures/rebasing-1.json b/figures/rebasing-1.json
new file mode 100644
index 0000000..7aa2c5d
--- /dev/null
+++ b/figures/rebasing-1.json
@@ -0,0 +1,72 @@
+{
+ "$schema": "./_schema.json",
+ "title": "rebasing 1",
+ "grid": {
+ "x": 119.0,
+ "y": 55.0
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "f42c5",
+ "kind": "commit",
+ "label": "f42c5",
+ "col": 2,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "e43a6",
+ "kind": "commit",
+ "label": "e43a6",
+ "col": 2,
+ "row": 2,
+ "dy": -2.0
+ },
+ {
+ "id": "node_5ddae",
+ "kind": "commit",
+ "label": "5ddae",
+ "col": 3,
+ "row": 2,
+ "dy": -2.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 3,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f42c5", "to": "a6b4c" },
+ { "from": "e43a6", "to": "a6b4c", "route": "diagonal" },
+ { "from": "node_5ddae", "to": "e43a6" },
+ { "from": "master", "to": "f42c5", "arrow": "forward" },
+ { "from": "ruby-client", "to": "node_5ddae", "arrow": "forward" }
+ ]
+}
diff --git a/figures/rebasing-2.json b/figures/rebasing-2.json
new file mode 100644
index 0000000..8343dbf
--- /dev/null
+++ b/figures/rebasing-2.json
@@ -0,0 +1,83 @@
+{
+ "$schema": "./_schema.json",
+ "title": "rebasing 2",
+ "grid": {
+ "x": 118.8,
+ "y": 55.0
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "f42c5",
+ "kind": "commit",
+ "label": "f42c5",
+ "col": 2,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "a0a41",
+ "kind": "commit",
+ "label": "a0a41",
+ "col": 3,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "e43a6",
+ "kind": "commit",
+ "label": "e43a6",
+ "col": 2,
+ "row": 2,
+ "dy": -2.0
+ },
+ {
+ "id": "node_5ddae",
+ "kind": "commit",
+ "label": "5ddae",
+ "col": 3,
+ "row": 2,
+ "dx": 0.6,
+ "dy": -2.0
+ },
+ {
+ "id": "ruby-client",
+ "kind": "ref",
+ "label": "ruby_client",
+ "col": 3,
+ "row": 3,
+ "dx": 0.6
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f42c5", "to": "a6b4c" },
+ { "from": "a0a41", "to": "f42c5" },
+ { "from": "e43a6", "to": "a6b4c", "route": "diagonal" },
+ { "from": "node_5ddae", "to": "e43a6" },
+ { "from": "master", "to": "a0a41", "arrow": "forward" },
+ { "from": "ruby-client", "to": "node_5ddae", "arrow": "forward" }
+ ]
+}
diff --git a/figures/remote-branches-1.json b/figures/remote-branches-1.json
new file mode 100644
index 0000000..a854bbc
--- /dev/null
+++ b/figures/remote-branches-1.json
@@ -0,0 +1,89 @@
+{
+ "$schema": "./_schema.json",
+ "title": "remote branches 1",
+ "grid": {
+ "x": 117.0,
+ "y": 73.0
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 1,
+ "dy": -20.0
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 1,
+ "dy": -20.0
+ },
+ {
+ "id": "f4265",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 2,
+ "row": 1,
+ "dy": -20.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "node_0b743-2",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 3,
+ "dy": 20.0
+ },
+ {
+ "id": "a6b4c-2",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 3,
+ "dy": 20.0
+ },
+ {
+ "id": "f4265-2",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 2,
+ "row": 3,
+ "dy": 20.0
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": "origin/master",
+ "col": 2,
+ "row": 3,
+ "dy": -33.0
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f4265", "to": "a6b4c" },
+ { "from": "master", "to": "f4265", "arrow": "forward" },
+ { "from": "a6b4c-2", "to": "node_0b743-2" },
+ { "from": "f4265-2", "to": "a6b4c-2" },
+ { "from": "origin-master", "to": "f4265-2", "arrow": "forward" },
+ { "from": "master-2", "to": "f4265-2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/remote-branches-2.json b/figures/remote-branches-2.json
new file mode 100644
index 0000000..8a53779
--- /dev/null
+++ b/figures/remote-branches-2.json
@@ -0,0 +1,135 @@
+{
+ "$schema": "./_schema.json",
+ "title": "remote branches 2",
+ "grid": {
+ "x": 116.7,
+ "y": 73.0
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 1,
+ "dx": 1.0,
+ "dy": -20.0
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 1,
+ "dx": 1.3,
+ "dy": -20.0
+ },
+ {
+ "id": "f4265",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 2,
+ "row": 1,
+ "dx": 1.6,
+ "dy": -20.0
+ },
+ {
+ "id": "node_31b8e",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 3,
+ "row": 1,
+ "dy": -20.0
+ },
+ {
+ "id": "node_190a3",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 4,
+ "row": 1,
+ "dx": -0.8,
+ "dy": -20.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 0,
+ "dx": -0.8
+ },
+ {
+ "id": "node_0b743-2",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 3,
+ "dx": -1.0,
+ "dy": 20.0
+ },
+ {
+ "id": "a6b4c-2",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 3,
+ "dx": -0.7,
+ "dy": 20.0
+ },
+ {
+ "id": "f4265-2",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 2,
+ "row": 3,
+ "dy": 20.0
+ },
+ {
+ "id": "a38de",
+ "kind": "commit",
+ "label": "a38de",
+ "col": 3,
+ "row": 3,
+ "dx": 0.9,
+ "dy": 20.0
+ },
+ {
+ "id": "node_893cf",
+ "kind": "commit",
+ "label": "893cf",
+ "col": 4,
+ "row": 3,
+ "dx": 1.2,
+ "dy": 20.0
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 4,
+ "dx": 1.2
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": "origin/master",
+ "col": 2,
+ "row": 3,
+ "dy": -33.0
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f4265", "to": "a6b4c" },
+ { "from": "node_31b8e", "to": "f4265" },
+ { "from": "node_190a3", "to": "node_31b8e" },
+ { "from": "master", "to": "node_190a3", "arrow": "forward" },
+ { "from": "a6b4c-2", "to": "node_0b743-2" },
+ { "from": "f4265-2", "to": "a6b4c-2" },
+ { "from": "a38de", "to": "f4265-2" },
+ { "from": "node_893cf", "to": "a38de" },
+ { "from": "origin-master", "to": "f4265-2", "arrow": "forward" },
+ { "from": "master-2", "to": "node_893cf", "arrow": "forward" }
+ ]
+}
diff --git a/figures/remote-branches-3.json b/figures/remote-branches-3.json
new file mode 100644
index 0000000..3abd07e
--- /dev/null
+++ b/figures/remote-branches-3.json
@@ -0,0 +1,145 @@
+{
+ "$schema": "./_schema.json",
+ "title": "remote branches 3",
+ "grid": {
+ "x": 117.3,
+ "y": 69.8
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 3,
+ "dy": 29.6
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 3,
+ "dy": 29.6
+ },
+ {
+ "id": "f4265",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 2,
+ "row": 3,
+ "dx": -0.61,
+ "dy": 29.6
+ },
+ {
+ "id": "node_31b8e",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 3,
+ "row": 3,
+ "dy": 29.6
+ },
+ {
+ "id": "node_190a3",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 4,
+ "row": 3,
+ "dy": 29.6
+ },
+ {
+ "id": "a38de",
+ "kind": "commit",
+ "label": "a38de",
+ "col": 3,
+ "row": 4,
+ "dy": 16.8
+ },
+ {
+ "id": "node_893cf",
+ "kind": "commit",
+ "label": "893cf",
+ "col": 4,
+ "row": 4,
+ "dy": 16.8
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 5
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": "origin/master",
+ "col": 4,
+ "row": 3,
+ "dy": -23.4
+ },
+ {
+ "id": "node_0b743-2",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 1,
+ "dy": -16.8
+ },
+ {
+ "id": "a6b4c-2",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 1,
+ "dy": -16.8
+ },
+ {
+ "id": "f4265-2",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 2,
+ "row": 1,
+ "dx": -0.61,
+ "dy": -16.8
+ },
+ {
+ "id": "node_31b8e-2",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 3,
+ "row": 1,
+ "dy": -16.8
+ },
+ {
+ "id": "node_190a3-2",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 4,
+ "row": 1,
+ "dy": -16.8
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f4265", "to": "a6b4c" },
+ { "from": "node_31b8e", "to": "f4265" },
+ { "from": "node_190a3", "to": "node_31b8e" },
+ { "from": "origin-master", "to": "node_190a3", "arrow": "forward" },
+ { "from": "a38de", "to": "node_190a3" },
+ { "from": "node_893cf", "to": "a38de" },
+ { "from": "master", "to": "node_893cf", "arrow": "forward" },
+ { "from": "a6b4c-2", "to": "node_0b743-2" },
+ { "from": "f4265-2", "to": "a6b4c-2" },
+ { "from": "node_31b8e-2", "to": "f4265-2" },
+ { "from": "node_190a3-2", "to": "node_31b8e-2" },
+ { "from": "master-2", "to": "node_190a3-2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/remote-branches-4.json b/figures/remote-branches-4.json
new file mode 100644
index 0000000..952af7f
--- /dev/null
+++ b/figures/remote-branches-4.json
@@ -0,0 +1,166 @@
+{
+ "$schema": "./_schema.json",
+ "title": "remote branches 4",
+ "grid": {
+ "x": 85.7,
+ "y": 73.1
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 3,
+ "dx": 2.5,
+ "dy": 36.02
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 3,
+ "dx": 33.79,
+ "dy": 36.02
+ },
+ {
+ "id": "f4265",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 3,
+ "row": 3,
+ "dx": -20.61,
+ "dy": 36.02
+ },
+ {
+ "id": "node_31b8e",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 4,
+ "row": 3,
+ "dx": 11.69,
+ "dy": 36.02
+ },
+ {
+ "id": "node_190a3",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 6,
+ "row": 3,
+ "dx": -42.71,
+ "dy": 36.02
+ },
+ {
+ "id": "a38de",
+ "kind": "commit",
+ "label": "a38de",
+ "col": 4,
+ "row": 4,
+ "dx": 11.69,
+ "dy": 19.92
+ },
+ {
+ "id": "node_893cf",
+ "kind": "commit",
+ "label": "893cf",
+ "col": 6,
+ "row": 4,
+ "dx": -42.71,
+ "dy": 19.92
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 5,
+ "dx": -42.7
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": "origin/master",
+ "col": 6,
+ "row": 3,
+ "dx": -42.7,
+ "dy": -16.98
+ },
+ {
+ "id": "f4265-2",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 0,
+ "row": 1,
+ "dx": -2.5,
+ "dy": -19.78
+ },
+ {
+ "id": "node_31b8e-2",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 1,
+ "row": 1,
+ "dx": 26.79,
+ "dy": -19.78
+ },
+ {
+ "id": "node_190a3-2",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 3,
+ "row": 1,
+ "dx": -28.61,
+ "dy": -19.78
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0,
+ "dx": -28.6
+ },
+ {
+ "id": "f4265-3",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 5,
+ "row": 1,
+ "dx": -29.0,
+ "dy": -19.78
+ },
+ {
+ "id": "node_31b8e-3",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 6,
+ "row": 1,
+ "dy": -19.78
+ },
+ {
+ "id": "master-3",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f4265", "to": "a6b4c" },
+ { "from": "node_31b8e", "to": "f4265" },
+ { "from": "node_190a3", "to": "node_31b8e" },
+ { "from": "origin-master", "to": "node_190a3", "arrow": "forward" },
+ { "from": "a38de", "to": "node_190a3" },
+ { "from": "node_893cf", "to": "a38de" },
+ { "from": "master", "to": "node_893cf", "arrow": "forward" },
+ { "from": "f4265-2", "to": "a6b4c" },
+ { "from": "node_31b8e-2", "to": "f4265-2" },
+ { "from": "node_190a3-2", "to": "node_31b8e-2" },
+ { "from": "master-2", "to": "node_190a3-2", "arrow": "forward" },
+ { "from": "f4265-3", "to": "node_31b8e-2" },
+ { "from": "node_31b8e-3", "to": "f4265-3" },
+ { "from": "master-3", "to": "node_31b8e-3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/remote-branches-5.json b/figures/remote-branches-5.json
new file mode 100644
index 0000000..a35bd26
--- /dev/null
+++ b/figures/remote-branches-5.json
@@ -0,0 +1,176 @@
+{
+ "$schema": "./_schema.json",
+ "title": "remote branches 5",
+ "grid": {
+ "x": 85.9,
+ "y": 73.0
+ },
+ "nodes": [
+ {
+ "id": "node_0b743",
+ "kind": "commit",
+ "label": "0b743",
+ "col": 0,
+ "row": 3,
+ "dx": 1.6,
+ "dy": 36.0
+ },
+ {
+ "id": "a6b4c",
+ "kind": "commit",
+ "label": "a6b4c",
+ "col": 1,
+ "row": 3,
+ "dx": 32.7,
+ "dy": 36.0
+ },
+ {
+ "id": "f4265",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 3,
+ "row": 3,
+ "dx": -22.1,
+ "dy": 36.0
+ },
+ {
+ "id": "node_31b8e",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 4,
+ "row": 3,
+ "dx": 10.0,
+ "dy": 36.0
+ },
+ {
+ "id": "node_190a3",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 5,
+ "row": 3,
+ "dx": 41.1,
+ "dy": 36.0
+ },
+ {
+ "id": "a38de",
+ "kind": "commit",
+ "label": "a38de",
+ "col": 4,
+ "row": 4,
+ "dx": 10.0,
+ "dy": 20.0
+ },
+ {
+ "id": "node_893cf",
+ "kind": "commit",
+ "label": "893cf",
+ "col": 5,
+ "row": 4,
+ "dx": 41.1,
+ "dy": 20.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 5,
+ "dx": 41.1
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": "origin/master",
+ "col": 5,
+ "row": 3,
+ "dx": 41.1,
+ "dy": -18.0
+ },
+ {
+ "id": "teamone-master",
+ "kind": "ref",
+ "label": "teamone/master",
+ "col": 4,
+ "row": 3,
+ "dx": 10.0,
+ "dy": -18.0
+ },
+ {
+ "id": "f4265-2",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 0,
+ "row": 1,
+ "dx": -1.6,
+ "dy": -20.0
+ },
+ {
+ "id": "node_31b8e-2",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 1,
+ "row": 1,
+ "dx": 27.5,
+ "dy": -20.0
+ },
+ {
+ "id": "node_190a3-2",
+ "kind": "commit",
+ "label": "190a3",
+ "col": 3,
+ "row": 1,
+ "dx": -28.3,
+ "dy": -20.0
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0,
+ "dx": -28.3
+ },
+ {
+ "id": "f4265-3",
+ "kind": "commit",
+ "label": "f4265",
+ "col": 5,
+ "row": 1,
+ "dx": -29.11,
+ "dy": -20.0
+ },
+ {
+ "id": "node_31b8e-3",
+ "kind": "commit",
+ "label": "31b8e",
+ "col": 6,
+ "row": 1,
+ "dy": -20.0
+ },
+ {
+ "id": "master-3",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "a6b4c", "to": "node_0b743" },
+ { "from": "f4265", "to": "a6b4c" },
+ { "from": "node_31b8e", "to": "f4265" },
+ { "from": "node_190a3", "to": "node_31b8e" },
+ { "from": "origin-master", "to": "node_190a3", "arrow": "forward" },
+ { "from": "teamone-master", "to": "node_190a3", "arrow": "forward" },
+ { "from": "a38de", "to": "node_190a3" },
+ { "from": "node_893cf", "to": "a38de" },
+ { "from": "master", "to": "node_893cf", "arrow": "forward" },
+ { "from": "f4265-2", "to": "a6b4c" },
+ { "from": "node_31b8e-2", "to": "f4265-2" },
+ { "from": "node_190a3-2", "to": "node_31b8e-2" },
+ { "from": "master-2", "to": "node_190a3-2", "arrow": "forward" },
+ { "from": "f4265-3", "to": "node_31b8e-2" },
+ { "from": "node_31b8e-3", "to": "f4265-3" },
+ { "from": "master-3", "to": "node_31b8e-3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/replace1.json b/figures/replace1.json
new file mode 100644
index 0000000..9028324
--- /dev/null
+++ b/figures/replace1.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "./_schema.json",
+ "title": "replace1",
+ "grid": {
+ "x": 135.4,
+ "y": 92.0
+ },
+ "nodes": [
+ {
+ "id": "ef989",
+ "kind": "commit",
+ "label": "ef989",
+ "col": 0,
+ "row": 0,
+ "dx": -0.8
+ },
+ {
+ "id": "c6e1e",
+ "kind": "commit",
+ "label": "c6e1e",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "node_9c68f",
+ "kind": "commit",
+ "label": "9c68f",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "node_94570",
+ "kind": "commit",
+ "label": "94570",
+ "col": 0,
+ "row": 3
+ },
+ {
+ "id": "c1822",
+ "kind": "commit",
+ "label": "c1822",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "c6e1e", "to": "ef989" },
+ { "from": "node_9c68f", "to": "c6e1e" },
+ { "from": "node_94570", "to": "node_9c68f" },
+ { "from": "c1822", "to": "node_94570" },
+ { "from": "master", "to": "ef989", "arrow": "forward" }
+ ]
+}
diff --git a/figures/replace2.json b/figures/replace2.json
new file mode 100644
index 0000000..17c3cbb
--- /dev/null
+++ b/figures/replace2.json
@@ -0,0 +1,68 @@
+{
+ "$schema": "./_schema.json",
+ "title": "replace2",
+ "grid": {
+ "x": 137.2,
+ "y": 92.0
+ },
+ "nodes": [
+ {
+ "id": "ef989",
+ "kind": "commit",
+ "label": "ef989",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "c6e1e",
+ "kind": "commit",
+ "label": "c6e1e",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "node_9c68f",
+ "kind": "commit",
+ "label": "9c68f",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "node_94570",
+ "kind": "commit",
+ "label": "94570",
+ "col": 0,
+ "row": 3
+ },
+ {
+ "id": "c1822",
+ "kind": "commit",
+ "label": "c1822",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "history",
+ "kind": "ref",
+ "label": "history",
+ "col": 1,
+ "row": 1,
+ "dy": 1.0
+ }
+ ],
+ "edges": [
+ { "from": "c6e1e", "to": "ef989" },
+ { "from": "node_9c68f", "to": "c6e1e" },
+ { "from": "node_94570", "to": "node_9c68f" },
+ { "from": "c1822", "to": "node_94570" },
+ { "from": "master", "to": "ef989", "arrow": "forward" },
+ { "from": "history", "to": "c6e1e", "arrow": "forward" }
+ ]
+}
diff --git a/figures/replace3.json b/figures/replace3.json
new file mode 100644
index 0000000..346447f
--- /dev/null
+++ b/figures/replace3.json
@@ -0,0 +1,82 @@
+{
+ "$schema": "./_schema.json",
+ "title": "replace3",
+ "grid": {
+ "x": 142.0,
+ "y": 92.0
+ },
+ "nodes": [
+ {
+ "id": "node_622e8",
+ "kind": "commit",
+ "label": "622e8",
+ "col": 0,
+ "row": 2,
+ "dy": -0.94
+ },
+ {
+ "id": "c6e1e",
+ "kind": "commit",
+ "label": "c6e1e",
+ "col": 1,
+ "row": 1,
+ "dx": 4.97
+ },
+ {
+ "id": "node_9c68f",
+ "kind": "commit",
+ "label": "9c68f",
+ "col": 1,
+ "row": 2,
+ "dx": 4.97
+ },
+ {
+ "id": "node_94570",
+ "kind": "commit",
+ "label": "94570",
+ "col": 1,
+ "row": 3,
+ "dx": 4.97
+ },
+ {
+ "id": "c1822",
+ "kind": "commit",
+ "label": "c1822",
+ "col": 1,
+ "row": 4,
+ "dx": 4.97
+ },
+ {
+ "id": "history",
+ "kind": "ref",
+ "label": "history",
+ "col": 2,
+ "row": 1,
+ "dy": 1.05
+ },
+ {
+ "id": "ef989",
+ "kind": "commit",
+ "label": "ef989",
+ "col": 1,
+ "row": 0,
+ "dx": 4.97
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "c6e1e", "to": "ef989" },
+ { "from": "node_9c68f", "to": "c6e1e" },
+ { "from": "node_94570", "to": "node_9c68f" },
+ { "from": "c1822", "to": "node_94570" },
+ { "from": "master", "to": "ef989", "arrow": "forward" },
+ { "from": "history", "to": "c6e1e", "arrow": "forward" },
+ { "from": "node_622e8", "to": "c6e1e", "route": "diagonal" }
+ ]
+}
diff --git a/figures/replace4.json b/figures/replace4.json
new file mode 100644
index 0000000..43735d8
--- /dev/null
+++ b/figures/replace4.json
@@ -0,0 +1,101 @@
+{
+ "$schema": "./_schema.json",
+ "title": "replace4",
+ "grid": {
+ "x": 140.2,
+ "y": 92.0
+ },
+ "nodes": [
+ {
+ "id": "ef989",
+ "kind": "commit",
+ "label": "ef989",
+ "col": 2,
+ "row": 0,
+ "dx": 2.91
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "e146b",
+ "kind": "commit",
+ "label": "e146b",
+ "col": 1,
+ "row": 0,
+ "dx": -3.92
+ },
+ {
+ "id": "c6e1e",
+ "kind": "commit",
+ "label": "c6e1e",
+ "col": 2,
+ "row": 1,
+ "dx": 2.91
+ },
+ {
+ "id": "history",
+ "kind": "ref",
+ "label": "history",
+ "col": 3,
+ "row": 1,
+ "dy": 0.99
+ },
+ {
+ "id": "node_81a70",
+ "kind": "commit",
+ "label": "81a70",
+ "col": 1,
+ "row": 1,
+ "dx": -3.94
+ },
+ {
+ "id": "node_622e8",
+ "kind": "commit",
+ "label": "622e8",
+ "col": 1,
+ "row": 2,
+ "dx": -3.86,
+ "dy": -1.0
+ },
+ {
+ "id": "node_9c68f",
+ "kind": "commit",
+ "label": "9c68f",
+ "col": 2,
+ "row": 2,
+ "dx": 2.91
+ },
+ {
+ "id": "node_94570",
+ "kind": "commit",
+ "label": "94570",
+ "col": 2,
+ "row": 3,
+ "dx": 2.91
+ },
+ {
+ "id": "c1822",
+ "kind": "commit",
+ "label": "c1822",
+ "col": 2,
+ "row": 4,
+ "dx": 2.91
+ }
+ ],
+ "edges": [
+ { "from": "e146b", "to": "master", "arrow": "forward" },
+ { "from": "ef989", "to": "e146b" },
+ { "from": "c6e1e", "to": "ef989" },
+ { "from": "node_9c68f", "to": "c6e1e" },
+ { "from": "node_94570", "to": "node_9c68f" },
+ { "from": "c1822", "to": "node_94570" },
+ { "from": "history", "to": "c6e1e", "arrow": "forward" },
+ { "from": "node_81a70", "to": "e146b", "route": "diagonal" },
+ { "from": "node_622e8", "to": "node_81a70" }
+ ]
+}
diff --git a/figures/replace5.json b/figures/replace5.json
new file mode 100644
index 0000000..7d52a07
--- /dev/null
+++ b/figures/replace5.json
@@ -0,0 +1,96 @@
+{
+ "$schema": "./_schema.json",
+ "title": "replace5",
+ "grid": {
+ "x": 112.0,
+ "y": 62.9
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "e146b",
+ "kind": "commit",
+ "label": "e146b",
+ "col": 1,
+ "row": 0,
+ "dx": 24.28
+ },
+ {
+ "id": "node_81a70",
+ "kind": "commit",
+ "label": "81a70",
+ "col": 1,
+ "row": 1,
+ "dx": 24.26,
+ "dy": 29.05
+ },
+ {
+ "id": "node_622e8",
+ "kind": "commit",
+ "label": "622e8",
+ "col": 1,
+ "row": 3,
+ "dx": 24.34,
+ "dy": -5.7
+ },
+ {
+ "id": "c6e1e",
+ "kind": "commit",
+ "label": "c6e1e",
+ "col": 2,
+ "row": 2,
+ "dx": -25.09,
+ "dy": -24.16
+ },
+ {
+ "id": "history",
+ "kind": "ref",
+ "label": "history",
+ "col": 3,
+ "row": 2,
+ "dy": -23.16
+ },
+ {
+ "id": "node_9c68f",
+ "kind": "commit",
+ "label": "9c68f",
+ "col": 2,
+ "row": 3,
+ "dx": -25.09,
+ "dy": 4.94
+ },
+ {
+ "id": "node_94570",
+ "kind": "commit",
+ "label": "94570",
+ "col": 2,
+ "row": 5,
+ "dx": -25.09,
+ "dy": -28.86
+ },
+ {
+ "id": "c1822",
+ "kind": "commit",
+ "label": "c1822",
+ "col": 2,
+ "row": 6,
+ "dx": -25.09
+ }
+ ],
+ "edges": [
+ { "from": "e146b", "to": "master", "arrow": "forward" },
+ { "from": "node_81a70", "to": "e146b" },
+ { "from": "node_622e8", "to": "node_81a70" },
+ { "from": "c6e1e", "to": "node_622e8", "route": "diagonal" },
+ { "from": "node_9c68f", "to": "c6e1e" },
+ { "from": "node_94570", "to": "node_9c68f" },
+ { "from": "c1822", "to": "node_94570" },
+ { "from": "history", "to": "c6e1e", "arrow": "forward" }
+ ]
+}
diff --git a/figures/rerere1.json b/figures/rerere1.json
new file mode 100644
index 0000000..2b49823
--- /dev/null
+++ b/figures/rerere1.json
@@ -0,0 +1,54 @@
+{
+ "$schema": "./_schema.json",
+ "title": "rerere1",
+ "grid": {
+ "x": 152.9,
+ "y": 66.0
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "hello-world",
+ "kind": "commit",
+ "label": "hello world",
+ "col": 0,
+ "row": 1,
+ "dy": 1.9
+ },
+ {
+ "id": "world",
+ "kind": "commit",
+ "label": "world",
+ "col": 1,
+ "row": 1,
+ "dy": 1.9
+ },
+ {
+ "id": "hello",
+ "kind": "commit",
+ "label": "hello",
+ "col": 1,
+ "row": 2,
+ "dy": -2.1
+ },
+ {
+ "id": "i18n-world",
+ "kind": "ref",
+ "label": "i18n-world",
+ "col": 1,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "world", "to": "hello-world" },
+ { "from": "hello", "to": "hello-world" },
+ { "from": "master", "to": "world", "arrow": "forward" },
+ { "from": "i18n-world", "to": "hello", "arrow": "forward" }
+ ]
+}
diff --git a/figures/rerere2.json b/figures/rerere2.json
new file mode 100644
index 0000000..dcbf08a
--- /dev/null
+++ b/figures/rerere2.json
@@ -0,0 +1,63 @@
+{
+ "$schema": "./_schema.json",
+ "title": "rerere2",
+ "grid": {
+ "x": 152.9,
+ "y": 66.0
+ },
+ "nodes": [
+ {
+ "id": "hello-world",
+ "kind": "commit",
+ "label": "hello world",
+ "col": 0,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "hola-world",
+ "kind": "commit",
+ "label": "hola world",
+ "col": 1,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "hola-mundo",
+ "kind": "commit",
+ "label": "hola mundo",
+ "col": 2,
+ "row": 1,
+ "dy": 2.0
+ },
+ {
+ "id": "hello-mundo",
+ "kind": "commit",
+ "label": "hello mundo",
+ "col": 1,
+ "row": 2,
+ "dy": -2.0
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "i18-world",
+ "kind": "ref",
+ "label": "i18-world",
+ "col": 1,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "hola-world", "to": "hello-world" },
+ { "from": "hola-mundo", "to": "hola-world" },
+ { "from": "hello-mundo", "to": "hola-world" },
+ { "from": "master", "to": "hola-mundo", "arrow": "forward" },
+ { "from": "i18-world", "to": "hello-mundo", "arrow": "forward" }
+ ]
+}
diff --git a/figures/rerere3.json b/figures/rerere3.json
new file mode 100644
index 0000000..6561485
--- /dev/null
+++ b/figures/rerere3.json
@@ -0,0 +1,116 @@
+{
+ "$schema": "./_schema.json",
+ "title": "rerere3",
+ "grid": {
+ "x": 79.5,
+ "y": 67.1
+ },
+ "nodes": [
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0,
+ "dx": 4.0,
+ "dy": -3.0
+ },
+ {
+ "id": "hello-world",
+ "kind": "commit",
+ "label": "hello world",
+ "col": 0,
+ "row": 1,
+ "dy": 4.9
+ },
+ {
+ "id": "hola-world",
+ "kind": "commit",
+ "label": "hola world",
+ "col": 2,
+ "row": 1,
+ "dx": -5.0,
+ "dy": 4.9
+ },
+ {
+ "id": "hola-mundo",
+ "kind": "commit",
+ "label": "hola mundo",
+ "col": 4,
+ "row": 1,
+ "dx": -12.0,
+ "dy": 4.9
+ },
+ {
+ "id": "i18-world",
+ "kind": "ref",
+ "label": "i18-world",
+ "col": 4,
+ "row": 0,
+ "dy": -5.0
+ },
+ {
+ "id": "hello-world-2",
+ "kind": "commit",
+ "label": "hello world",
+ "col": 0,
+ "row": 1,
+ "dy": 5.03
+ },
+ {
+ "id": "hola-world-2",
+ "kind": "commit",
+ "label": "hola world",
+ "col": 2,
+ "row": 1,
+ "dx": -6.13,
+ "dy": 5.02
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0,
+ "dx": -6.13,
+ "dy": 4.0
+ },
+ {
+ "id": "hello-mundo",
+ "kind": "commit",
+ "label": "hello mundo",
+ "col": 2,
+ "row": 2,
+ "dx": -6.1
+ },
+ {
+ "id": "hola-mundo-2",
+ "kind": "commit",
+ "label": "hola mundo",
+ "col": 4,
+ "row": 1,
+ "dx": -12.0,
+ "dy": 5.02
+ },
+ {
+ "id": "i18-world-2",
+ "kind": "ref",
+ "label": "i18-world",
+ "col": 4,
+ "row": 0,
+ "dx": -12.0,
+ "dy": 3.99
+ }
+ ],
+ "edges": [
+ { "from": "hola-world", "to": "hello-world" },
+ { "from": "hola-mundo", "to": "hola-world" },
+ { "from": "master", "to": "hola-mundo", "arrow": "forward" },
+ { "from": "i18-world", "to": "hola-mundo", "arrow": "forward" },
+ { "from": "hola-world-2", "to": "hello-world-2" },
+ { "from": "hello-mundo", "to": "hola-world-2" },
+ { "from": "hola-mundo-2", "to": "hello-mundo" },
+ { "from": "master-2", "to": "hola-mundo-2", "arrow": "forward" },
+ { "from": "i18-world-2", "to": "hola-mundo-2", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-checkout.json b/figures/reset-checkout.json
new file mode 100644
index 0000000..f713046
--- /dev/null
+++ b/figures/reset-checkout.json
@@ -0,0 +1,153 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset checkout",
+ "grid": {
+ "x": 86.2,
+ "y": 53.3
+ },
+ "nodes": [
+ {
+ "id": "commit-a",
+ "kind": "commit",
+ "label": "commit A",
+ "col": 4,
+ "row": 2,
+ "dx": -30.9,
+ "dy": 1.4
+ },
+ {
+ "id": "commit-b",
+ "kind": "commit",
+ "label": "commit B",
+ "col": 5,
+ "row": 2,
+ "dy": 1.4
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 1,
+ "dx": 12.35,
+ "dy": 0.7
+ },
+ {
+ "id": "develop",
+ "kind": "ref",
+ "label": "develop",
+ "col": 5,
+ "row": 1,
+ "dx": -43.05,
+ "dy": 0.7
+ },
+ {
+ "id": "head",
+ "kind": "tree",
+ "label": "HEAD",
+ "col": 5,
+ "row": 0,
+ "dx": -43.04
+ },
+ {
+ "id": "commit-a-2",
+ "kind": "commit",
+ "label": "commit A",
+ "col": 0,
+ "row": 4,
+ "dy": -1.2
+ },
+ {
+ "id": "commit-b-2",
+ "kind": "commit",
+ "label": "commit B",
+ "col": 1,
+ "row": 4,
+ "dx": 30.8,
+ "dy": -1.2
+ },
+ {
+ "id": "head-2",
+ "kind": "tree",
+ "label": "HEAD",
+ "col": 1,
+ "row": 2,
+ "dx": 30.8,
+ "dy": -2.6
+ },
+ {
+ "id": "master-2",
+ "kind": "ref",
+ "label": "master",
+ "col": 0,
+ "row": 3,
+ "dy": -1.89
+ },
+ {
+ "id": "develop-2",
+ "kind": "ref",
+ "label": "develop",
+ "col": 1,
+ "row": 3,
+ "dx": 30.81,
+ "dy": -1.89
+ },
+ {
+ "id": "commit-a-3",
+ "kind": "commit",
+ "label": "commit A",
+ "col": 3,
+ "row": 6,
+ "dx": 12.36
+ },
+ {
+ "id": "commit-b-3",
+ "kind": "commit",
+ "label": "commit B",
+ "col": 5,
+ "row": 6,
+ "dx": -43.03
+ },
+ {
+ "id": "master-3",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 5,
+ "dx": 12.35,
+ "dy": -0.59
+ },
+ {
+ "id": "develop-3",
+ "kind": "ref",
+ "label": "develop",
+ "col": 5,
+ "row": 5,
+ "dx": -43.04,
+ "dy": -0.51
+ },
+ {
+ "id": "head-3",
+ "kind": "tree",
+ "label": "HEAD",
+ "col": 3,
+ "row": 4,
+ "dx": 12.35,
+ "dy": -1.21
+ }
+ ],
+ "edges": [
+ { "from": "commit-b", "to": "commit-a" },
+ { "from": "master", "to": "commit-a", "arrow": "forward" },
+ { "from": "develop", "to": "commit-b", "arrow": "forward" },
+ { "from": "head", "to": "develop", "arrow": "forward" },
+ { "from": "commit-b-2", "to": "commit-a-2" },
+ { "from": "master-2", "to": "commit-a-2", "arrow": "forward" },
+ { "from": "develop-2", "to": "commit-b-2", "arrow": "forward" },
+ { "from": "head-2", "to": "commit-b-2", "arrow": "forward" },
+ { "from": "commit-b-3", "to": "commit-a-3" },
+ { "from": "master-3", "to": "commit-a-3", "arrow": "forward" },
+ { "from": "develop-3", "to": "commit-b-3", "arrow": "forward" },
+ { "from": "head-3", "to": "master-3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-ex1.json b/figures/reset-ex1.json
new file mode 100644
index 0000000..b13ef84
--- /dev/null
+++ b/figures/reset-ex1.json
@@ -0,0 +1,66 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset ex1",
+ "grid": {
+ "x": 141.0,
+ "y": 86.6
+ },
+ "nodes": [
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 2,
+ "dy": 34.29
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 2,
+ "dx": 0.57,
+ "dy": 34.29
+ },
+ {
+ "id": "file-txt",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 2,
+ "dy": 34.25
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -41.6
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-ex2.json b/figures/reset-ex2.json
new file mode 100644
index 0000000..8235153
--- /dev/null
+++ b/figures/reset-ex2.json
@@ -0,0 +1,75 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset ex2",
+ "grid": {
+ "x": 140.9,
+ "y": 86.3
+ },
+ "nodes": [
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 2,
+ "dy": 34.82
+ },
+ {
+ "id": "file-txt",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 2,
+ "dy": 34.85
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 2,
+ "dy": 34.82
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 1,
+ "row": 3
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -41.3
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-ex3.json b/figures/reset-ex3.json
new file mode 100644
index 0000000..b767119
--- /dev/null
+++ b/figures/reset-ex3.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset ex3",
+ "grid": {
+ "x": 140.9,
+ "y": 70.1
+ },
+ "nodes": [
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -25.1
+ },
+ {
+ "id": "file-txt",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 14.03
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 14.2
+ },
+ {
+ "id": "eb43bf8",
+ "kind": "tree",
+ "label": "eb43bf8",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 14.2
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -17.35
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "head-2", "to": "eb43bf8", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-ex4.json b/figures/reset-ex4.json
new file mode 100644
index 0000000..2fee3a7
--- /dev/null
+++ b/figures/reset-ex4.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset ex4",
+ "grid": {
+ "x": 141.0,
+ "y": 70.2
+ },
+ "nodes": [
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 14.01
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 14.01
+ },
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 14.06
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "eb43bf8",
+ "kind": "tree",
+ "label": "eb43bf8",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -18.29
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -25.2
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" },
+ { "from": "head", "to": "eb43bf8", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-ex5.json b/figures/reset-ex5.json
new file mode 100644
index 0000000..f543046
--- /dev/null
+++ b/figures/reset-ex5.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset ex5",
+ "grid": {
+ "x": 141.0,
+ "y": 70.2
+ },
+ "nodes": [
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 13.75
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 13.75
+ },
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 13.8
+ },
+ {
+ "id": "eb43bf8",
+ "kind": "tree",
+ "label": "eb43bf8",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -18.55
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -25.2
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" },
+ { "from": "head", "to": "eb43bf8", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-ex6.json b/figures/reset-ex6.json
new file mode 100644
index 0000000..09920e6
--- /dev/null
+++ b/figures/reset-ex6.json
@@ -0,0 +1,107 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset ex6",
+ "grid": {
+ "x": 140.9,
+ "y": 70.0
+ },
+ "nodes": [
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 13.67
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 13.62
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 13.62
+ },
+ {
+ "id": "node_9e5e6a4",
+ "kind": "ref",
+ "label": "9e5e6a4",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -19.01
+ },
+ {
+ "id": "file-txt-4",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 2,
+ "dy": -19.01
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 1,
+ "row": 0,
+ "dx": -0.7
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 1,
+ "row": 1,
+ "dx": -0.7,
+ "dy": -25.0
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" },
+ { "from": "file-txt-4", "to": "file-txt-3" }
+ ]
+}
diff --git a/figures/reset-hard.json b/figures/reset-hard.json
new file mode 100644
index 0000000..3747905
--- /dev/null
+++ b/figures/reset-hard.json
@@ -0,0 +1,107 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset hard",
+ "grid": {
+ "x": 140.9,
+ "y": 69.9
+ },
+ "nodes": [
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 1,
+ "row": 1,
+ "dy": -25.11
+ },
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 13.58
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 13.52
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 13.52
+ },
+ {
+ "id": "node_9e5e6a4",
+ "kind": "ref",
+ "label": "9e5e6a4",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -19.28
+ },
+ {
+ "id": "file-txt-4",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 2,
+ "dx": -0.76,
+ "dy": -19.29
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-txt-4", "to": "file-txt-3" },
+ { "from": "master", "to": "node_9e5e6a4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-mixed.json b/figures/reset-mixed.json
new file mode 100644
index 0000000..3ad763e
--- /dev/null
+++ b/figures/reset-mixed.json
@@ -0,0 +1,97 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset mixed",
+ "grid": {
+ "x": 140.9,
+ "y": 69.9
+ },
+ "nodes": [
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 1,
+ "row": 1,
+ "dy": -25.11
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 13.58
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 13.52
+ },
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 13.52
+ },
+ {
+ "id": "node_9e5e6a4",
+ "kind": "ref",
+ "label": "9e5e6a4",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -19.28
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 2,
+ "dx": -0.76,
+ "dy": -19.29
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-txt-3", "to": "file-txt-2" },
+ { "from": "master", "to": "node_9e5e6a4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-path1.json b/figures/reset-path1.json
new file mode 100644
index 0000000..a4378cb
--- /dev/null
+++ b/figures/reset-path1.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset path1",
+ "grid": {
+ "x": 141.0,
+ "y": 70.2
+ },
+ "nodes": [
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 14.01
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 14.01
+ },
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 14.06
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 1,
+ "row": 4
+ },
+ {
+ "id": "eb43bf8",
+ "kind": "tree",
+ "label": "eb43bf8",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -18.29
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -25.2
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" },
+ { "from": "head", "to": "eb43bf8", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-path2.json b/figures/reset-path2.json
new file mode 100644
index 0000000..fd88ff4
--- /dev/null
+++ b/figures/reset-path2.json
@@ -0,0 +1,84 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset path2",
+ "grid": {
+ "x": 141.0,
+ "y": 70.3
+ },
+ "nodes": [
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 13.71
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 13.71
+ },
+ {
+ "id": "file-txt",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 2,
+ "row": 4
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 13.76
+ },
+ {
+ "id": "eb43bf8",
+ "kind": "tree",
+ "label": "eb43bf8",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -18.49
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 0,
+ "row": 1,
+ "dy": -25.3
+ }
+ ],
+ "edges": [
+ { "from": "head-2", "to": "master", "arrow": "forward" },
+ { "from": "head", "to": "eb43bf8", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-path3.json b/figures/reset-path3.json
new file mode 100644
index 0000000..1c9d804
--- /dev/null
+++ b/figures/reset-path3.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset path3",
+ "grid": {
+ "x": 141.5,
+ "y": 69.7
+ },
+ "nodes": [
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 2,
+ "row": 0,
+ "dx": 0.76
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 2,
+ "row": 1,
+ "dx": 0.76,
+ "dy": -24.91
+ },
+ {
+ "id": "file-txt",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -18.69
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 2,
+ "dx": -0.85,
+ "dy": -18.69
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dx": -1.59,
+ "dy": 14.06
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dx": -0.89,
+ "dy": 14.01
+ },
+ {
+ "id": "file-txt-3",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 1,
+ "row": 4,
+ "dx": -0.89
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 14.01
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-txt-2", "to": "file-txt" }
+ ]
+}
diff --git a/figures/reset-soft.json b/figures/reset-soft.json
new file mode 100644
index 0000000..78c9a62
--- /dev/null
+++ b/figures/reset-soft.json
@@ -0,0 +1,89 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset soft",
+ "grid": {
+ "x": 140.8,
+ "y": 69.8
+ },
+ "nodes": [
+ {
+ "id": "file-txt",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -18.89
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 2,
+ "dx": -0.5,
+ "dy": -18.89
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 1,
+ "row": 0,
+ "dx": 0.7
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 1,
+ "row": 1,
+ "dx": 0.7,
+ "dy": -25.01
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dy": 14.06
+ },
+ {
+ "id": "node_9e5e6a4",
+ "kind": "ref",
+ "label": "9e5e6a4",
+ "col": 0,
+ "row": 4
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dy": 14.01
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dy": 14.01
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-txt-2", "to": "file-txt" },
+ { "from": "master", "to": "node_9e5e6a4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-squash-r1.json b/figures/reset-squash-r1.json
new file mode 100644
index 0000000..c1f788c
--- /dev/null
+++ b/figures/reset-squash-r1.json
@@ -0,0 +1,141 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset squash r1",
+ "grid": {
+ "x": 141.4,
+ "y": 50.5
+ },
+ "nodes": [
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 3,
+ "row": 0,
+ "dx": 0.73
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 3,
+ "row": 1,
+ "dx": 0.73,
+ "dy": -5.71
+ },
+ {
+ "id": "file-a-txt-v1",
+ "kind": "tree",
+ "label": "file-a.txt v1",
+ "col": 1,
+ "row": 2,
+ "dy": 12.72
+ },
+ {
+ "id": "file-b-txt-v1",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 3,
+ "dy": -5.78
+ },
+ {
+ "id": "file-a-txt-v3",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 3,
+ "row": 2,
+ "dy": 12.57
+ },
+ {
+ "id": "file-b-txt-v1-2",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 3,
+ "dy": -5.78
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 3,
+ "row": 4,
+ "dx": -0.78,
+ "dy": 21.67
+ },
+ {
+ "id": "file-a-txt-v3-2",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 3,
+ "row": 5,
+ "dx": -0.77,
+ "dy": 18.06
+ },
+ {
+ "id": "file-b-txt-v1-3",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 6,
+ "dx": -1.37
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 2,
+ "row": 4,
+ "dy": 21.67
+ },
+ {
+ "id": "file-a-txt-v3-3",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 2,
+ "row": 5,
+ "dy": 18.06
+ },
+ {
+ "id": "file-b-txt-v1-4",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 1,
+ "row": 6,
+ "dx": -0.58
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 1,
+ "row": 4,
+ "dx": 0.6,
+ "dy": 21.67
+ },
+ {
+ "id": "file-a-txt-v3-4",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 1,
+ "row": 5,
+ "dx": 0.6,
+ "dy": 18.06
+ },
+ {
+ "id": "file-b-txt-v1-5",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 0,
+ "row": 6
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-a-txt-v1", "to": "file-a-txt-v3" }
+ ]
+}
diff --git a/figures/reset-squash-r2.json b/figures/reset-squash-r2.json
new file mode 100644
index 0000000..ef366d1
--- /dev/null
+++ b/figures/reset-squash-r2.json
@@ -0,0 +1,157 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset squash r2",
+ "grid": {
+ "x": 224.9,
+ "y": 39.3
+ },
+ "nodes": [
+ {
+ "id": "node_38eb946",
+ "kind": "blob",
+ "label": "38eb946",
+ "col": 1,
+ "row": 7,
+ "dx": -82.9,
+ "dy": 7.25
+ },
+ {
+ "id": "file-b-txt-v1",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 0,
+ "row": 8
+ },
+ {
+ "id": "file-a-txt-v1",
+ "kind": "tree",
+ "label": "file-a.txt v1",
+ "col": 3,
+ "row": 3,
+ "dx": -57.45,
+ "dy": -4.54
+ },
+ {
+ "id": "file-b-txt-v1-2",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 3,
+ "row": 4,
+ "dx": 83.55,
+ "dy": -11.84
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 3,
+ "row": 0,
+ "dx": -57.46
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 3,
+ "row": 1,
+ "dx": -57.46,
+ "dy": 5.49
+ },
+ {
+ "id": "file-a-txt-v3",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 4,
+ "row": 3,
+ "dx": 0.65,
+ "dy": -4.69
+ },
+ {
+ "id": "file-b-txt-v1-3",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 3,
+ "row": 4,
+ "dx": 83.55,
+ "dy": -11.84
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 4,
+ "row": 6,
+ "dy": -12.46
+ },
+ {
+ "id": "file-a-txt-v3-2",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 4,
+ "row": 7,
+ "dy": -4.87
+ },
+ {
+ "id": "file-b-txt-v1-4",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 3,
+ "row": 8,
+ "dx": 82.68,
+ "dy": -12.02
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 3,
+ "row": 6,
+ "dx": 83.75,
+ "dy": -12.46
+ },
+ {
+ "id": "file-a-txt-v3-3",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 3,
+ "row": 7,
+ "dx": 83.76,
+ "dy": -4.87
+ },
+ {
+ "id": "file-b-txt-v1-5",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 3,
+ "row": 8,
+ "dx": -58.24,
+ "dy": -12.02
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 3,
+ "row": 6,
+ "dx": -57.2,
+ "dy": -12.46
+ },
+ {
+ "id": "eb43bf8",
+ "kind": "tree",
+ "label": "eb43bf8",
+ "col": 3,
+ "row": 7,
+ "dx": -57.2,
+ "dy": -4.74
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-a-txt-v1", "to": "file-a-txt-v3" },
+ { "from": "master", "to": "node_38eb946", "arrow": "forward" }
+ ]
+}
diff --git a/figures/reset-squash-r3.json b/figures/reset-squash-r3.json
new file mode 100644
index 0000000..f35f026
--- /dev/null
+++ b/figures/reset-squash-r3.json
@@ -0,0 +1,156 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset squash r3",
+ "grid": {
+ "x": 141.4,
+ "y": 49.1
+ },
+ "nodes": [
+ {
+ "id": "file-a-txt-v1",
+ "kind": "tree",
+ "label": "file-a.txt v1",
+ "col": 1,
+ "row": 4,
+ "dy": 7.3
+ },
+ {
+ "id": "file-b-txt-v1",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 5,
+ "dy": -9.76
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 3,
+ "row": 1,
+ "dy": -4.31
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 3,
+ "row": 6,
+ "dx": -0.7,
+ "dy": 19.15
+ },
+ {
+ "id": "file-a-txt-v3",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 3,
+ "row": 7,
+ "dx": -0.7,
+ "dy": 16.94
+ },
+ {
+ "id": "file-b-txt-v1-2",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 8,
+ "dx": -1.3
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 2,
+ "row": 6,
+ "dy": 19.15
+ },
+ {
+ "id": "file-a-txt-v3-2",
+ "kind": "blob",
+ "label": "file-a.txt v3",
+ "col": 2,
+ "row": 7,
+ "dy": 16.94
+ },
+ {
+ "id": "file-b-txt-v1-3",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 1,
+ "row": 8,
+ "dx": -0.68
+ },
+ {
+ "id": "node_68aef35",
+ "kind": "blob",
+ "label": "68aef35",
+ "col": 1,
+ "row": 7,
+ "dx": 0.6,
+ "dy": 17.11
+ },
+ {
+ "id": "file-b-txt-v1-4",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 0,
+ "row": 8
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 1,
+ "row": 6,
+ "dx": 0.6,
+ "dy": 19.14
+ },
+ {
+ "id": "node_68aef35-2",
+ "kind": "blob",
+ "label": "68aef35",
+ "col": 3,
+ "row": 2,
+ "dy": 11.42
+ },
+ {
+ "id": "file-b-txt-v1-5",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 3,
+ "dy": -5.53
+ },
+ {
+ "id": "node_68aef35-3",
+ "kind": "blob",
+ "label": "68aef35",
+ "col": 3,
+ "row": 2,
+ "dy": 11.42
+ },
+ {
+ "id": "file-b-txt-v1-6",
+ "kind": "commit",
+ "label": "file-b.txt v1",
+ "col": 2,
+ "row": 3,
+ "dy": -5.53
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "master", "to": "node_68aef35-2", "arrow": "forward" },
+ { "from": "file-a-txt-v1", "to": "node_68aef35", "route": "diagonal" }
+ ]
+}
diff --git a/figures/reset-start.json b/figures/reset-start.json
new file mode 100644
index 0000000..14f85a0
--- /dev/null
+++ b/figures/reset-start.json
@@ -0,0 +1,80 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset start",
+ "grid": {
+ "x": 141.4,
+ "y": 74.6
+ },
+ "nodes": [
+ {
+ "id": "file-txt",
+ "kind": "tree",
+ "label": [
+ "file.txt",
+ "v1"
+ ],
+ "col": 0,
+ "row": 2,
+ "dy": -28.14
+ },
+ {
+ "id": "file-txt-2",
+ "kind": "ref",
+ "label": [
+ "file.txt",
+ "v2"
+ ],
+ "col": 1,
+ "row": 2,
+ "dx": -1.16,
+ "dy": -28.14
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 2,
+ "row": 0,
+ "dx": 0.54
+ },
+ {
+ "id": "master",
+ "kind": "blob",
+ "label": "master",
+ "col": 2,
+ "row": 1,
+ "dx": 0.54,
+ "dy": -29.81
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dx": -1.3
+ },
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "col": 1,
+ "row": 3,
+ "dx": -0.57
+ },
+ {
+ "id": "head-2",
+ "kind": "commit",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3
+ }
+ ],
+ "edges": [
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "file-txt-2", "to": "file-txt" }
+ ]
+}
diff --git a/figures/reset-workflow.json b/figures/reset-workflow.json
new file mode 100644
index 0000000..2254fa6
--- /dev/null
+++ b/figures/reset-workflow.json
@@ -0,0 +1,38 @@
+{
+ "$schema": "./_schema.json",
+ "title": "reset workflow",
+ "layout": "absolute",
+ "canvas": {
+ "w": 401.0,
+ "h": 221.0
+ },
+ "nodes": [
+ {
+ "id": "index",
+ "kind": "tree",
+ "label": "Index",
+ "x": 144.21,
+ "y": 4.0
+ },
+ {
+ "id": "head",
+ "kind": "commit",
+ "label": "HEAD",
+ "x": 284.95,
+ "y": 4.0
+ },
+ {
+ "id": "working",
+ "kind": "ref",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "x": 3.47,
+ "y": 4.0
+ }
+ ],
+ "edges": [
+ { "from": "index", "to": "head", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-1.json b/figures/small-team-1.json
new file mode 100644
index 0000000..268d413
--- /dev/null
+++ b/figures/small-team-1.json
@@ -0,0 +1,66 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 1",
+ "grid": {
+ "x": 118.9,
+ "y": 54.3
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 3.2
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 3.2
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 2,
+ "row": 1,
+ "dy": 3.2
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 2,
+ "dy": -3.1
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_738ee", "to": "node_1edee" },
+ { "from": "fbff5", "to": "node_1edee", "route": "diagonal" },
+ { "from": "master", "to": "node_738ee", "arrow": "forward" },
+ { "from": "origin-master", "to": "fbff5", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-2.json b/figures/small-team-2.json
new file mode 100644
index 0000000..593fb0a
--- /dev/null
+++ b/figures/small-team-2.json
@@ -0,0 +1,76 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 2",
+ "grid": {
+ "x": 119.0,
+ "y": 54.7
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 2,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "node_72bbc",
+ "kind": "john",
+ "label": "72bbc",
+ "col": 3,
+ "row": 1,
+ "dy": 3.8
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 2,
+ "dy": -2.9
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_738ee", "to": "node_1edee" },
+ { "from": "node_72bbc", "to": "node_738ee" },
+ { "from": "node_72bbc", "to": "fbff5", "route": "diagonal" },
+ { "from": "fbff5", "to": "node_1edee", "route": "diagonal" },
+ { "from": "master", "to": "node_72bbc", "arrow": "forward" },
+ { "from": "origin-master", "to": "fbff5", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-3.json b/figures/small-team-3.json
new file mode 100644
index 0000000..fa06a16
--- /dev/null
+++ b/figures/small-team-3.json
@@ -0,0 +1,77 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 3",
+ "grid": {
+ "x": 119.0,
+ "y": 37.3
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 2,
+ "dy": -18.6
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 2,
+ "dy": -18.6
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 2,
+ "row": 2,
+ "dy": -18.6
+ },
+ {
+ "id": "node_72bbc",
+ "kind": "john",
+ "label": "72bbc",
+ "col": 3,
+ "row": 2,
+ "dy": -18.6
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 3,
+ "dy": -7.9
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "node_738ee", "to": "node_1edee" },
+ { "from": "node_72bbc", "to": "fbff5", "route": "diagonal" },
+ { "from": "node_72bbc", "to": "node_738ee" },
+ { "from": "fbff5", "to": "node_1edee", "route": "diagonal" },
+ { "from": "fbff5", "to": "node_1edee" },
+ { "from": "master", "to": "node_72bbc", "arrow": "forward" },
+ { "from": "origin-master", "to": "node_72bbc", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-4.json b/figures/small-team-4.json
new file mode 100644
index 0000000..2862755
--- /dev/null
+++ b/figures/small-team-4.json
@@ -0,0 +1,86 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 4",
+ "grid": {
+ "x": 119.0,
+ "y": 56.0
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "node_8149a",
+ "kind": "jessica",
+ "label": "8149a",
+ "col": 3,
+ "row": 1
+ },
+ {
+ "id": "node_23ac6",
+ "kind": "jessica",
+ "label": "23ac6",
+ "col": 4,
+ "row": 1
+ },
+ {
+ "id": "node_4af42",
+ "kind": "jessica",
+ "label": "4af42",
+ "col": 5,
+ "row": 1
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "issue54",
+ "kind": "ref",
+ "label": "issue54",
+ "col": 5,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "fbff5", "to": "node_1edee" },
+ { "from": "node_8149a", "to": "fbff5" },
+ { "from": "node_23ac6", "to": "node_8149a" },
+ { "from": "node_4af42", "to": "node_23ac6" },
+ { "from": "master", "to": "fbff5", "arrow": "forward" },
+ { "from": "origin-master", "to": "fbff5", "arrow": "forward" },
+ { "from": "issue54", "to": "node_4af42", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-5.json b/figures/small-team-5.json
new file mode 100644
index 0000000..4ef6d25
--- /dev/null
+++ b/figures/small-team-5.json
@@ -0,0 +1,111 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 5",
+ "grid": {
+ "x": 119.0,
+ "y": 54.3
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 1.71
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 1.71
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 1,
+ "dy": 1.71
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 2,
+ "row": 2,
+ "dy": -1.59
+ },
+ {
+ "id": "node_72bbc",
+ "kind": "john",
+ "label": "72bbc",
+ "col": 3,
+ "row": 2,
+ "dy": -1.59
+ },
+ {
+ "id": "node_8149a",
+ "kind": "jessica",
+ "label": "8149a",
+ "col": 3,
+ "row": 1,
+ "dy": 1.61
+ },
+ {
+ "id": "node_23ac6",
+ "kind": "jessica",
+ "label": "23ac6",
+ "col": 4,
+ "row": 1,
+ "dy": 1.61
+ },
+ {
+ "id": "node_4af42",
+ "kind": "jessica",
+ "label": "4af42",
+ "col": 5,
+ "row": 1,
+ "dy": 1.61
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "issue54",
+ "kind": "ref",
+ "label": "issue54",
+ "col": 5,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "fbff5", "to": "node_1edee" },
+ { "from": "node_738ee", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_72bbc", "to": "node_738ee" },
+ { "from": "node_72bbc", "to": "fbff5", "route": "diagonal" },
+ { "from": "node_8149a", "to": "fbff5" },
+ { "from": "node_23ac6", "to": "node_8149a" },
+ { "from": "node_4af42", "to": "node_23ac6" },
+ { "from": "master", "to": "fbff5", "arrow": "forward" },
+ { "from": "origin-master", "to": "node_72bbc", "arrow": "forward" },
+ { "from": "issue54", "to": "node_4af42", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-6.json b/figures/small-team-6.json
new file mode 100644
index 0000000..6beaaf6
--- /dev/null
+++ b/figures/small-team-6.json
@@ -0,0 +1,127 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 6",
+ "grid": {
+ "x": 119.2,
+ "y": 54.7
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 1.3
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 1,
+ "dy": 1.29
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 2,
+ "row": 2,
+ "dy": -1.4
+ },
+ {
+ "id": "node_72bbc",
+ "kind": "john",
+ "label": "72bbc",
+ "col": 3,
+ "row": 2,
+ "dx": -0.6,
+ "dy": -1.4
+ },
+ {
+ "id": "node_8059c",
+ "kind": "jessica",
+ "label": "8059c",
+ "col": 6,
+ "row": 2,
+ "dy": -1.4
+ },
+ {
+ "id": "node_8149a",
+ "kind": "jessica",
+ "label": "8149a",
+ "col": 3,
+ "row": 1,
+ "dx": -0.6,
+ "dy": 1.3
+ },
+ {
+ "id": "node_23ac6",
+ "kind": "jessica",
+ "label": "23ac6",
+ "col": 4,
+ "row": 1,
+ "dx": -0.8,
+ "dy": 1.3
+ },
+ {
+ "id": "node_4af42",
+ "kind": "jessica",
+ "label": "4af42",
+ "col": 5,
+ "row": 1,
+ "dx": -1.0,
+ "dy": 1.3
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 3,
+ "row": 3,
+ "dx": -0.6
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 0
+ },
+ {
+ "id": "issue54",
+ "kind": "ref",
+ "label": "issue54",
+ "col": 5,
+ "row": 0,
+ "dx": -1.0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "fbff5", "to": "node_1edee" },
+ { "from": "node_738ee", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_72bbc", "to": "node_738ee" },
+ { "from": "node_72bbc", "to": "fbff5", "route": "diagonal" },
+ { "from": "node_8149a", "to": "fbff5" },
+ { "from": "node_23ac6", "to": "node_8149a" },
+ { "from": "node_4af42", "to": "node_23ac6" },
+ { "from": "node_8059c", "to": "node_72bbc" },
+ { "from": "node_8059c", "to": "node_4af42", "route": "diagonal" },
+ { "from": "master", "to": "node_8059c", "arrow": "forward" },
+ { "from": "origin-master", "to": "node_72bbc", "arrow": "forward" },
+ { "from": "issue54", "to": "node_4af42", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-7.json b/figures/small-team-7.json
new file mode 100644
index 0000000..597b1ef
--- /dev/null
+++ b/figures/small-team-7.json
@@ -0,0 +1,126 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team 7",
+ "grid": {
+ "x": 119.2,
+ "y": 54.7
+ },
+ "nodes": [
+ {
+ "id": "node_4b078",
+ "kind": "commit",
+ "label": "4b078",
+ "col": 0,
+ "row": 1,
+ "dy": 1.35
+ },
+ {
+ "id": "node_1edee",
+ "kind": "commit",
+ "label": "1edee",
+ "col": 1,
+ "row": 1,
+ "dy": 1.35
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 1,
+ "dy": 1.34
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 2,
+ "row": 2,
+ "dy": -1.35
+ },
+ {
+ "id": "node_72bbc",
+ "kind": "john",
+ "label": "72bbc",
+ "col": 3,
+ "row": 2,
+ "dx": -0.6,
+ "dy": -1.35
+ },
+ {
+ "id": "node_8059c",
+ "kind": "jessica",
+ "label": "8059c",
+ "col": 6,
+ "row": 2,
+ "dy": -1.35
+ },
+ {
+ "id": "node_8149a",
+ "kind": "jessica",
+ "label": "8149a",
+ "col": 3,
+ "row": 1,
+ "dx": -0.6,
+ "dy": 1.35
+ },
+ {
+ "id": "node_23ac6",
+ "kind": "jessica",
+ "label": "23ac6",
+ "col": 4,
+ "row": 1,
+ "dx": -0.8,
+ "dy": 1.35
+ },
+ {
+ "id": "node_4af42",
+ "kind": "jessica",
+ "label": "4af42",
+ "col": 5,
+ "row": 1,
+ "dx": -1.0,
+ "dy": 1.35
+ },
+ {
+ "id": "origin-master",
+ "kind": "ref",
+ "label": [
+ "origin/",
+ "master"
+ ],
+ "col": 6,
+ "row": 3
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 0
+ },
+ {
+ "id": "issue54",
+ "kind": "ref",
+ "label": "issue54",
+ "col": 5,
+ "row": 0,
+ "dx": -1.0
+ }
+ ],
+ "edges": [
+ { "from": "node_1edee", "to": "node_4b078" },
+ { "from": "fbff5", "to": "node_1edee" },
+ { "from": "node_738ee", "to": "node_1edee", "route": "diagonal" },
+ { "from": "node_72bbc", "to": "node_738ee" },
+ { "from": "node_72bbc", "to": "fbff5", "route": "diagonal" },
+ { "from": "node_8149a", "to": "fbff5" },
+ { "from": "node_23ac6", "to": "node_8149a" },
+ { "from": "node_4af42", "to": "node_23ac6" },
+ { "from": "node_8059c", "to": "node_72bbc" },
+ { "from": "node_8059c", "to": "node_4af42", "route": "diagonal" },
+ { "from": "master", "to": "node_8059c", "arrow": "forward" },
+ { "from": "origin-master", "to": "node_8059c", "arrow": "forward" },
+ { "from": "issue54", "to": "node_4af42", "arrow": "forward" }
+ ]
+}
diff --git a/figures/small-team-flow.json b/figures/small-team-flow.json
new file mode 100644
index 0000000..6e81ee0
--- /dev/null
+++ b/figures/small-team-flow.json
@@ -0,0 +1,57 @@
+{
+ "$schema": "./_schema.json",
+ "title": "small team flow",
+ "grid": {
+ "x": 114.5,
+ "y": 505.0
+ },
+ "nodes": [
+ {
+ "id": "server",
+ "kind": "ref",
+ "label": "Server",
+ "col": 1,
+ "row": 0,
+ "dx": 0.5
+ },
+ {
+ "id": "john",
+ "kind": "ref",
+ "label": "John",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "jessica",
+ "kind": "ref",
+ "label": "Jessica",
+ "col": 0,
+ "row": 0,
+ "dx": -1.0
+ },
+ {
+ "id": "server-2",
+ "kind": "ref",
+ "label": "Server",
+ "col": 1,
+ "row": 1,
+ "dx": 0.5
+ },
+ {
+ "id": "john-2",
+ "kind": "ref",
+ "label": "John",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "jessica-2",
+ "kind": "ref",
+ "label": "Jessica",
+ "col": 0,
+ "row": 1,
+ "dx": 1.0
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/smudge.json b/figures/smudge.json
new file mode 100644
index 0000000..a6d4f5b
--- /dev/null
+++ b/figures/smudge.json
@@ -0,0 +1,72 @@
+{
+ "$schema": "./_schema.json",
+ "title": "smudge",
+ "grid": {
+ "x": 150.2,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "filea-txt",
+ "kind": "commit",
+ "label": "fileA.txt",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "filea-txt-2",
+ "kind": "blob",
+ "label": "fileA.txt'",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "fileb-txt",
+ "kind": "blob",
+ "label": "fileB.txt'",
+ "col": 2,
+ "row": 1
+ },
+ {
+ "id": "fileb-txt-2",
+ "kind": "commit",
+ "label": "fileB.txt",
+ "col": 0,
+ "row": 1
+ },
+ {
+ "id": "filec-rb",
+ "kind": "commit",
+ "label": "fileC.rb",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "filec-rb-2",
+ "kind": "commit",
+ "label": "fileC.rb",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "smudge",
+ "kind": "ref",
+ "label": "smudge",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "clean",
+ "kind": "ref",
+ "label": "clean",
+ "col": 1,
+ "row": 1
+ }
+ ],
+ "edges": [
+ { "from": "filea-txt-2", "to": "smudge" },
+ { "from": "smudge", "to": "filea-txt" },
+ { "from": "fileb-txt-2", "to": "fileb-txt" },
+ { "from": "filec-rb-2", "to": "filec-rb" }
+ ]
+}
diff --git a/figures/snapshots.json b/figures/snapshots.json
new file mode 100644
index 0000000..5aaf2de
--- /dev/null
+++ b/figures/snapshots.json
@@ -0,0 +1,175 @@
+{
+ "$schema": "./_schema.json",
+ "title": "snapshots",
+ "grid": {
+ "x": 116.0,
+ "y": 52.3
+ },
+ "nodes": [
+ {
+ "id": "version-1",
+ "kind": "blob",
+ "label": "Version 1",
+ "col": 0,
+ "row": 0
+ },
+ {
+ "id": "version-2",
+ "kind": "blob",
+ "label": "Version 2",
+ "col": 1,
+ "row": 0
+ },
+ {
+ "id": "version-3",
+ "kind": "blob",
+ "label": "Version 3",
+ "col": 2,
+ "row": 0
+ },
+ {
+ "id": "version-4",
+ "kind": "blob",
+ "label": "Version 4",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "version-5",
+ "kind": "blob",
+ "label": "Version 5",
+ "col": 4,
+ "row": 0
+ },
+ {
+ "id": "file-a",
+ "kind": "commit",
+ "label": "File A",
+ "col": 0,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "file-a-2",
+ "kind": "commit",
+ "label": "File A",
+ "col": 0,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "a1",
+ "kind": "commit",
+ "label": "A1",
+ "col": 1,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 3
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "b1",
+ "kind": "commit",
+ "label": "B1",
+ "col": 3,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "a2",
+ "kind": "commit",
+ "label": "A2",
+ "col": 3,
+ "row": 1,
+ "dy": 6.7
+ },
+ {
+ "id": "a2-2",
+ "kind": "commit",
+ "label": "A2",
+ "col": 4,
+ "row": 1,
+ "dy": 6.71
+ },
+ {
+ "id": "b2",
+ "kind": "commit",
+ "label": "B2",
+ "col": 4,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 4,
+ "row": 3
+ },
+ {
+ "id": "file-b",
+ "kind": "commit",
+ "label": "File B",
+ "col": 0,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "file-c",
+ "kind": "commit",
+ "label": "File C",
+ "col": 0,
+ "row": 3
+ },
+ {
+ "id": "b",
+ "kind": "commit",
+ "label": "B",
+ "col": 2,
+ "row": 2,
+ "dy": 3.4
+ },
+ {
+ "id": "a1-2",
+ "kind": "commit",
+ "label": "A1",
+ "col": 2,
+ "row": 1,
+ "dy": 6.71
+ },
+ {
+ "id": "c2-2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "b-2",
+ "kind": "commit",
+ "label": "B",
+ "col": 1,
+ "row": 2,
+ "dy": 3.4
+ }
+ ],
+ "edges": [
+ { "from": "version-1", "to": "file-c", "arrow": "none" },
+ { "from": "version-2", "to": "c1", "arrow": "none" },
+ { "from": "version-3", "to": "c2", "arrow": "none" },
+ { "from": "version-4", "to": "c2-2", "arrow": "none" },
+ { "from": "version-5", "to": "c3", "arrow": "none" }
+ ]
+}
diff --git a/figures/symbols.json b/figures/symbols.json
new file mode 100644
index 0000000..57d07f3
--- /dev/null
+++ b/figures/symbols.json
@@ -0,0 +1,197 @@
+{
+ "$schema": "./_schema.json",
+ "title": "symbols",
+ "grid": {
+ "x": 119.0,
+ "y": 89.2
+ },
+ "nodes": [
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 0,
+ "row": 0,
+ "dx": -6.7
+ },
+ {
+ "id": "v1-0",
+ "kind": "ref",
+ "label": "v1.0",
+ "col": 2,
+ "row": 0,
+ "dx": 18.8
+ },
+ {
+ "id": "head",
+ "kind": "symref",
+ "label": "HEAD",
+ "col": 4,
+ "row": 0,
+ "dx": 25.8
+ },
+ {
+ "id": "tree",
+ "kind": "tree",
+ "label": "tree",
+ "col": 6,
+ "row": 0,
+ "dx": 44.3
+ },
+ {
+ "id": "version-5",
+ "kind": "blob",
+ "label": "Version 5",
+ "col": 9,
+ "row": 0,
+ "dx": -57.7
+ },
+ {
+ "id": "integration",
+ "kind": "ref",
+ "label": [
+ "integration",
+ "manager"
+ ],
+ "col": 0,
+ "row": 2,
+ "dx": 4.09,
+ "dy": -27.9
+ },
+ {
+ "id": "developer",
+ "kind": "tree",
+ "label": [
+ "developer",
+ "public"
+ ],
+ "col": 2,
+ "row": 2,
+ "dx": 21.09,
+ "dy": -27.9
+ },
+ {
+ "id": "staging",
+ "kind": "commit",
+ "label": [
+ "Staging",
+ "Area"
+ ],
+ "col": 4,
+ "row": 2,
+ "dx": 32.3,
+ "dy": -27.9
+ },
+ {
+ "id": "node_9c68f",
+ "kind": "commit",
+ "label": "9c68f",
+ "col": 6,
+ "row": 2,
+ "dx": 55.09,
+ "dy": -18.4
+ },
+ {
+ "id": "c2b9e",
+ "kind": "commit",
+ "label": "c2b9e",
+ "col": 9,
+ "row": 2,
+ "dx": -59.2,
+ "dy": -30.4
+ },
+ {
+ "id": "head-2",
+ "kind": "goldref",
+ "label": "HEAD",
+ "col": 0,
+ "row": 3,
+ "dx": 2.8,
+ "dy": 28.4
+ },
+ {
+ "id": "working",
+ "kind": "blob",
+ "label": [
+ "Working",
+ "Directory"
+ ],
+ "col": 2,
+ "row": 3,
+ "dx": 21.09,
+ "dy": 30.9
+ },
+ {
+ "id": "size",
+ "kind": "commit",
+ "label": [
+ "size",
+ "92ec2",
+ "Scott",
+ "Scott"
+ ],
+ "col": 5,
+ "row": 4,
+ "dx": -33.7,
+ "dy": -28.3
+ },
+ {
+ "id": "size-2",
+ "kind": "tree",
+ "label": [
+ "size",
+ "5b1d3 README",
+ "911e7 LICENSE",
+ "cba0a test.rb"
+ ],
+ "col": 7,
+ "row": 4,
+ "dx": -16.7,
+ "dy": -28.3
+ },
+ {
+ "id": "size-3",
+ "kind": "blob",
+ "label": "size",
+ "col": 9,
+ "row": 4,
+ "dy": -28.3
+ },
+ {
+ "id": "hello-mundo",
+ "kind": "commit",
+ "label": "hello mundo",
+ "col": 0,
+ "row": 5,
+ "dy": 5.0
+ },
+ {
+ "id": "fbff5",
+ "kind": "jessica",
+ "label": "fbff5",
+ "col": 2,
+ "row": 5,
+ "dx": 10.3,
+ "dy": -2.0
+ },
+ {
+ "id": "node_738ee",
+ "kind": "john",
+ "label": "738ee",
+ "col": 4,
+ "row": 5,
+ "dx": 27.3,
+ "dy": -2.0
+ },
+ {
+ "id": "fba9a",
+ "kind": "josie",
+ "label": "fba9a",
+ "col": 6,
+ "row": 5,
+ "dx": 44.3,
+ "dy": -2.0
+ }
+ ],
+ "edges": []
+}
diff --git a/figures/topic-branches-1.json b/figures/topic-branches-1.json
new file mode 100644
index 0000000..41c1fa4
--- /dev/null
+++ b/figures/topic-branches-1.json
@@ -0,0 +1,166 @@
+{
+ "$schema": "./_schema.json",
+ "title": "topic branches 1",
+ "grid": {
+ "x": 136.0,
+ "y": 40.1
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 1,
+ "row": 9
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 8,
+ "dy": -12.79
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 1,
+ "row": 6,
+ "dy": 14.41
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9",
+ "col": 1,
+ "row": 5,
+ "dy": 1.51
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 1,
+ "row": 4,
+ "dy": -11.39
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 6,
+ "dy": 14.41
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 2,
+ "row": 5,
+ "dy": 1.51
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 4,
+ "dy": -11.39
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 2,
+ "row": 2,
+ "dy": 15.81
+ },
+ {
+ "id": "c12",
+ "kind": "commit",
+ "label": "C12",
+ "col": 0,
+ "row": 2,
+ "dy": 15.81
+ },
+ {
+ "id": "c13",
+ "kind": "commit",
+ "label": "C13",
+ "col": 0,
+ "row": 1,
+ "dy": 2.91
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 3,
+ "row": 4,
+ "dy": -11.39
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 3,
+ "row": 2,
+ "dy": 15.81
+ },
+ {
+ "id": "c11",
+ "kind": "commit",
+ "label": "C11",
+ "col": 3,
+ "row": 1,
+ "dy": 2.91
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 3,
+ "dy": -14.39
+ },
+ {
+ "id": "iss91",
+ "kind": "ref",
+ "label": "iss91",
+ "col": 2,
+ "row": 1,
+ "dy": 12.91
+ },
+ {
+ "id": "iss91v2",
+ "kind": "ref",
+ "label": "iss91v2",
+ "col": 3,
+ "row": 0
+ },
+ {
+ "id": "dumbidea",
+ "kind": "ref",
+ "label": "dumbidea",
+ "col": 0,
+ "row": 0
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c3", "to": "c1" },
+ { "from": "c9", "to": "c3" },
+ { "from": "c10", "to": "c9" },
+ { "from": "c2", "to": "c1", "route": "diagonal" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c5", "to": "c4" },
+ { "from": "c6", "to": "c5" },
+ { "from": "c7", "to": "c4", "route": "diagonal" },
+ { "from": "c8", "to": "c7" },
+ { "from": "c11", "to": "c8" },
+ { "from": "c12", "to": "c10", "route": "diagonal" },
+ { "from": "c13", "to": "c12" }
+ ]
+}
diff --git a/figures/topic-branches-2.json b/figures/topic-branches-2.json
new file mode 100644
index 0000000..e61dfc7
--- /dev/null
+++ b/figures/topic-branches-2.json
@@ -0,0 +1,165 @@
+{
+ "$schema": "./_schema.json",
+ "title": "topic branches 2",
+ "grid": {
+ "x": 111.7,
+ "y": 51.7
+ },
+ "nodes": [
+ {
+ "id": "c0",
+ "kind": "commit",
+ "label": "C0",
+ "col": 1,
+ "row": 8,
+ "dx": -12.68
+ },
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 1,
+ "row": 7,
+ "dx": -12.68,
+ "dy": -0.92
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 1,
+ "row": 6,
+ "dx": -12.68,
+ "dy": -2.22
+ },
+ {
+ "id": "c9",
+ "kind": "commit",
+ "label": "C9",
+ "col": 1,
+ "row": 5,
+ "dx": -12.68,
+ "dy": -3.52
+ },
+ {
+ "id": "c10",
+ "kind": "commit",
+ "label": "C10",
+ "col": 1,
+ "row": 4,
+ "dx": -12.68,
+ "dy": -4.82
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 2,
+ "row": 6,
+ "dx": 12.62,
+ "dy": -2.22
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 2,
+ "row": 5,
+ "dx": 12.62,
+ "dy": -3.52
+ },
+ {
+ "id": "c13",
+ "kind": "commit",
+ "label": "C13",
+ "col": 1,
+ "row": 2,
+ "dx": -12.68,
+ "dy": -7.42
+ },
+ {
+ "id": "c14",
+ "kind": "commit",
+ "label": "C14",
+ "col": 1,
+ "row": 1,
+ "dx": -12.68,
+ "dy": -8.72
+ },
+ {
+ "id": "c12",
+ "kind": "commit",
+ "label": "C12",
+ "col": 1,
+ "row": 3,
+ "dx": -12.68,
+ "dy": -6.12
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 1,
+ "row": 0,
+ "dx": -12.68
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 2,
+ "row": 4,
+ "dx": 12.62,
+ "dy": -4.82
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 2,
+ "row": 3,
+ "dx": 12.62,
+ "dy": -6.12
+ },
+ {
+ "id": "c11",
+ "kind": "commit",
+ "label": "C11",
+ "col": 2,
+ "row": 2,
+ "dx": 12.62,
+ "dy": -7.42
+ },
+ {
+ "id": "iss91v2",
+ "kind": "ref",
+ "label": "iss91v2",
+ "col": 3,
+ "row": 2,
+ "dy": -7.42
+ },
+ {
+ "id": "dumbidea",
+ "kind": "ref",
+ "label": "dumbidea",
+ "col": 0,
+ "row": 2,
+ "dy": -7.42
+ }
+ ],
+ "edges": [
+ { "from": "c1", "to": "c0" },
+ { "from": "c3", "to": "c1" },
+ { "from": "c9", "to": "c3" },
+ { "from": "c10", "to": "c9" },
+ { "from": "c12", "to": "c10" },
+ { "from": "c13", "to": "c12" },
+ { "from": "c14", "to": "c13" },
+ { "from": "c2", "to": "c1", "route": "diagonal" },
+ { "from": "c4", "to": "c2" },
+ { "from": "c7", "to": "c4" },
+ { "from": "c8", "to": "c7" },
+ { "from": "c11", "to": "c8" },
+ { "from": "c14", "to": "c11", "route": "diagonal" }
+ ]
+}
diff --git a/figures/two-branches.json b/figures/two-branches.json
new file mode 100644
index 0000000..06871e8
--- /dev/null
+++ b/figures/two-branches.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "./_schema.json",
+ "title": "Two branch pointers into the commit history",
+ "grid": { "x": 145, "y": 61 },
+ "nodes": [
+ { "id": "c1", "kind": "other", "label": "98ca9", "col": 0, "row": 1 },
+ { "id": "c2", "kind": "other", "label": "34ac2", "col": 1, "row": 1 },
+ { "id": "c3", "kind": "other", "label": "f30ab", "col": 2, "row": 1 },
+ { "id": "main", "kind": "ref", "label": "main", "col": 2, "row": 0 },
+ { "id": "testing","kind": "ref", "label": "testing","col": 2, "row": 2 }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c3", "to": "c2" },
+ { "from": "main", "to": "c3", "arrow": "forward" },
+ { "from": "testing", "to": "c3", "arrow": "forward" }
+ ]
+}
diff --git a/figures/undomerge-reset.json b/figures/undomerge-reset.json
new file mode 100644
index 0000000..b5f293c
--- /dev/null
+++ b/figures/undomerge-reset.json
@@ -0,0 +1,95 @@
+{
+ "$schema": "./_schema.json",
+ "title": "undomerge reset",
+ "grid": {
+ "x": 119.0,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 2
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 2
+ },
+ {
+ "id": "m",
+ "kind": "commit",
+ "label": "M",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 3,
+ "row": 1,
+ "dx": -0.5
+ },
+ {
+ "id": "head",
+ "kind": "symref",
+ "label": "HEAD",
+ "col": 3,
+ "row": 0,
+ "dx": -0.5
+ },
+ {
+ "id": "topic",
+ "kind": "ref",
+ "label": "topic",
+ "col": 3,
+ "row": 4,
+ "dx": -0.5
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "m", "to": "c6" },
+ { "from": "m", "to": "c4", "route": "diagonal" },
+ { "from": "master", "to": "c6", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "topic", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/undomerge-revert.json b/figures/undomerge-revert.json
new file mode 100644
index 0000000..f203077
--- /dev/null
+++ b/figures/undomerge-revert.json
@@ -0,0 +1,106 @@
+{
+ "$schema": "./_schema.json",
+ "title": "undomerge revert",
+ "grid": {
+ "x": 118.7,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 2
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 2,
+ "dx": 0.6
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 3,
+ "dx": 0.6
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 3,
+ "dx": 0.9
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 2,
+ "dx": 0.9
+ },
+ {
+ "id": "m",
+ "kind": "commit",
+ "label": "M",
+ "col": 4,
+ "row": 2,
+ "dx": 1.2
+ },
+ {
+ "id": "m-2",
+ "kind": "commit",
+ "label": "^M",
+ "col": 5,
+ "row": 2,
+ "dx": 0.5
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 5,
+ "row": 1
+ },
+ {
+ "id": "head",
+ "kind": "symref",
+ "label": "HEAD",
+ "col": 5,
+ "row": 0
+ },
+ {
+ "id": "topic",
+ "kind": "ref",
+ "label": "topic",
+ "col": 3,
+ "row": 4
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "m", "to": "c6" },
+ { "from": "m", "to": "c4", "route": "diagonal" },
+ { "from": "m-2", "to": "m" },
+ { "from": "master", "to": "m-2", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "topic", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/figures/undomerge-revert2.json b/figures/undomerge-revert2.json
new file mode 100644
index 0000000..e867ec6
--- /dev/null
+++ b/figures/undomerge-revert2.json
@@ -0,0 +1,120 @@
+{
+ "$schema": "./_schema.json",
+ "title": "undomerge revert2",
+ "grid": {
+ "x": 118.9,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 2
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 2
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 4,
+ "row": 3,
+ "dx": -1.6
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 6,
+ "row": 2
+ },
+ {
+ "id": "m",
+ "kind": "commit",
+ "label": "M",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "m-2",
+ "kind": "commit",
+ "label": "^M",
+ "col": 5,
+ "row": 2,
+ "dx": -0.5
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 6,
+ "row": 1
+ },
+ {
+ "id": "head",
+ "kind": "symref",
+ "label": "HEAD",
+ "col": 6,
+ "row": 0
+ },
+ {
+ "id": "topic",
+ "kind": "ref",
+ "label": "topic",
+ "col": 4,
+ "row": 4,
+ "dx": -1.7
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "m", "to": "c6" },
+ { "from": "m", "to": "c4", "route": "diagonal" },
+ { "from": "m-2", "to": "m" },
+ { "from": "c7", "to": "c4" },
+ { "from": "c8", "to": "m-2" },
+ { "from": "c8", "to": "c7", "route": "diagonal" },
+ { "from": "master", "to": "c8", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "topic", "to": "c7", "arrow": "forward" }
+ ]
+}
diff --git a/figures/undomerge-revert3.json b/figures/undomerge-revert3.json
new file mode 100644
index 0000000..8338cc9
--- /dev/null
+++ b/figures/undomerge-revert3.json
@@ -0,0 +1,128 @@
+{
+ "$schema": "./_schema.json",
+ "title": "undomerge revert3",
+ "grid": {
+ "x": 118.9,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 2
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 2
+ },
+ {
+ "id": "c7",
+ "kind": "commit",
+ "label": "C7",
+ "col": 4,
+ "row": 3,
+ "dx": -1.6
+ },
+ {
+ "id": "m",
+ "kind": "commit",
+ "label": "^^M",
+ "col": 6,
+ "row": 2
+ },
+ {
+ "id": "c8",
+ "kind": "commit",
+ "label": "C8",
+ "col": 7,
+ "row": 2
+ },
+ {
+ "id": "m-2",
+ "kind": "commit",
+ "label": "M",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "m-3",
+ "kind": "commit",
+ "label": "^M",
+ "col": 5,
+ "row": 2,
+ "dx": -0.5
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 7,
+ "row": 1
+ },
+ {
+ "id": "head",
+ "kind": "symref",
+ "label": "HEAD",
+ "col": 7,
+ "row": 0
+ },
+ {
+ "id": "topic",
+ "kind": "ref",
+ "label": "topic",
+ "col": 4,
+ "row": 4,
+ "dx": -1.6
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "m-2", "to": "c6" },
+ { "from": "m-2", "to": "c4", "route": "diagonal" },
+ { "from": "m-3", "to": "m-2" },
+ { "from": "c7", "to": "c4" },
+ { "from": "m", "to": "m-3" },
+ { "from": "m", "to": "c7", "route": "diagonal" },
+ { "from": "c8", "to": "m" },
+ { "from": "master", "to": "c8", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "topic", "to": "c7", "arrow": "forward" }
+ ]
+}
diff --git a/figures/undomerge-start.json b/figures/undomerge-start.json
new file mode 100644
index 0000000..99ca881
--- /dev/null
+++ b/figures/undomerge-start.json
@@ -0,0 +1,93 @@
+{
+ "$schema": "./_schema.json",
+ "title": "undomerge start",
+ "grid": {
+ "x": 119.0,
+ "y": 53.0
+ },
+ "nodes": [
+ {
+ "id": "c1",
+ "kind": "commit",
+ "label": "C1",
+ "col": 0,
+ "row": 2
+ },
+ {
+ "id": "c2",
+ "kind": "commit",
+ "label": "C2",
+ "col": 1,
+ "row": 2
+ },
+ {
+ "id": "c5",
+ "kind": "commit",
+ "label": "C5",
+ "col": 2,
+ "row": 2
+ },
+ {
+ "id": "c3",
+ "kind": "commit",
+ "label": "C3",
+ "col": 2,
+ "row": 3
+ },
+ {
+ "id": "c4",
+ "kind": "commit",
+ "label": "C4",
+ "col": 3,
+ "row": 3
+ },
+ {
+ "id": "c6",
+ "kind": "commit",
+ "label": "C6",
+ "col": 3,
+ "row": 2
+ },
+ {
+ "id": "m",
+ "kind": "commit",
+ "label": "M",
+ "col": 4,
+ "row": 2
+ },
+ {
+ "id": "master",
+ "kind": "ref",
+ "label": "master",
+ "col": 4,
+ "row": 1
+ },
+ {
+ "id": "head",
+ "kind": "symref",
+ "label": "HEAD",
+ "col": 4,
+ "row": 0,
+ "dx": 0.5
+ },
+ {
+ "id": "topic",
+ "kind": "ref",
+ "label": "topic",
+ "col": 3,
+ "row": 4
+ }
+ ],
+ "edges": [
+ { "from": "c2", "to": "c1" },
+ { "from": "c5", "to": "c2" },
+ { "from": "c3", "to": "c2", "route": "diagonal" },
+ { "from": "c4", "to": "c3" },
+ { "from": "c6", "to": "c5" },
+ { "from": "m", "to": "c6" },
+ { "from": "m", "to": "c4", "route": "diagonal" },
+ { "from": "master", "to": "m", "arrow": "forward" },
+ { "from": "head", "to": "master", "arrow": "forward" },
+ { "from": "topic", "to": "c4", "arrow": "forward" }
+ ]
+}
diff --git a/images/advance-master.svg b/images/advance-master.svg
index 4fede8f..b1d2b17 100644
--- a/images/advance-master.svg
+++ b/images/advance-master.svg
@@ -1,61 +1,43 @@
-