Add a documentation website (#262)

We need more documentation than we can cover in a reasonably sized
README.
Replace stitchmd setup with a mkdocs-material based website.

Resolves #43
This commit is contained in:
Abhinav Gupta
2024-07-20 22:43:45 -07:00
committed by GitHub
parent 9b830d662a
commit d23c3dd2c9
59 changed files with 12135 additions and 200 deletions
+61
View File
@@ -0,0 +1,61 @@
name: GitHub Pages
on:
# TODO: uncomment closer to release
# push:
# branches: [main]
workflow_dispatch:
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.22
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pipenv
cache-dependency-path: doc/Pipfile.lock
- name: Install Python dependencies
run: pipenv install --deploy
working-directory: doc
- name: Build
run: make build
working-directory: doc
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: doc/_site/
deploy:
needs: build
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
-40
View File
@@ -1,40 +0,0 @@
name: Stitch README.md
on:
push:
branches: [main]
# Change the event to pull_request_target
# so that it runs in the context of the base repository.
pull_request_target:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
stitchmd:
name: ${{ github.event_name == 'pull_request_target' && 'Update' || 'Check' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
- name: Check or update README
uses: abhinav/stitchmd-action@v1
with:
mode: ${{ github.event_name == 'pull_request_target' && 'write' || 'check' }}
summary: doc/SUMMARY.md
preface: doc/preface.txt
output: README.md
- uses: stefanzweifel/git-auto-commit-action@v5
if: ${{ github.event_name == 'pull_request_target' }}
with:
file_pattern: README.md
commit_message: 'Update README.md'
+5 -5
View File
@@ -13,9 +13,9 @@ The following tools are needed to work on this project:
- [Changie](https://changie.dev/):
We use Changie to manage the changelog.
You'll need this if you make user-facing changes.
- [stitchmd](https://github.com/abhinav/stitchmd):
We use stitchmd to generate the README from files inside the doc/ directory.
You'll need this to edit the README.
- [pipenv](https://pipenv.pypa.io/en/latest/):
We use pipenv to manage Python dependencies for documentation generation.
You'll need this to preview changes to the doc/ directory.
## Making contributions
@@ -30,8 +30,8 @@ with the following notes:
[skip changelog]: reason why no changelog entry is needed
```
- If you edit documentation in doc/, run `make README.md` to update the README.
This requires stitchmd to be installed.
- If you edit documentation in doc/,
run `pipenv install && pipenv run mkdocs serve` to preview changes.
- All commits must include meaningful commit messages.
- Test new features and bug fixes.
+1 -19
View File
@@ -12,7 +12,6 @@ export PATH := $(GOBIN):$(PATH)
export GOEXPERIMENT = rangefunc
TEST_FLAGS ?=
STITCHMD_FLAGS ?= -o README.md -preface doc/preface.txt doc/SUMMARY.md
GS = bin/gs
MOCKGEN = bin/mockgen
@@ -24,8 +23,6 @@ GO_SRC_FILES = $(shell find . \
-path '*/.*' -prune -o \
'(' -type f -a -name '*.go' -a -not -name '*_test.go' ')' -print)
DOC_MD_FILES = $(shell find doc -name '*.md')
.PHONY: all
all: build lint test
@@ -38,6 +35,7 @@ lint: golangci-lint requiredfield-lint tidy-lint generate-lint
.PHONY: generate
generate: $(TOOLS)
go generate -x ./...
make -C doc generate
.PHONY: test
test:
@@ -52,9 +50,6 @@ cover:
tidy:
go mod tidy
README.md: $(DOC_MD_FILES)
stitchmd $(STITCHMD_FLAGS)
.PHONY: golangci-lint
golangci-lint:
golangci-lint run
@@ -78,19 +73,6 @@ generate-lint: $(TOOLS)
git diff --exit-code || \
(echo "'go generate' changed files" && false)
# readme-lint depends on generate-lint
# because that updates doc/reference.md.
# In the future, that can be make-managed.
.PHONE: readme-lint
readme-lint: generate-lint
@echo "[lint] readme"
@DIFF=$$(stitchmd -diff $(STITCHMD_FLAGS)); \
if [[ -n "$$DIFF" ]]; then \
echo "stitchmd would change README:"; \
echo "$$DIFF"; \
false; \
fi \
$(GS): $(GO_SRC_FILES) go.mod
go install go.abhg.dev/gs
+1 -6
View File
@@ -1,14 +1,9 @@
<!-- DO NOT EDIT. This file is generated from doc/*.md. Edit those instead. -->
# git-spice
- [Introduction](#introduction)
- [License](#license)
## Introduction
<div align="center">
<img src="doc/img/logo.png" width="300"/>
<img src="doc/src/img/logo.png" width="300"/>
</div>
[![CI](https://github.com/abhinav/git-spice/actions/workflows/ci.yml/badge.svg)](https://github.com/abhinav/git-spice/actions/workflows/ci.yml)
+1 -1
View File
@@ -19,7 +19,7 @@ func (*branchUntrackCmd) Help() string {
The current branch is deleted from git-spice's data store
but not deleted from the repository.
Branches upstack from it are not affected,
and will to the next branch downstack.
and will use the next branch downstack as their new base.
Provide a branch name as an argument to target
a different branch.
+3
View File
@@ -0,0 +1,3 @@
** linguist-documentation
*.svg -diff
*.drawio -diff
+3
View File
@@ -0,0 +1,3 @@
/_site
/.cache
/bin
+49
View File
@@ -0,0 +1,49 @@
SHELL = /bin/bash
MAKEFILE_DIR = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
export GOBIN = $(MAKEFILE_DIR)/bin
export PATH := $(GOBIN):$(PATH)
GS = bin/gs
FREEZE = bin/freeze
# Non-test Go files.
GO_SRC_FILES = $(shell find .. \
-path '*/.*' -prune -o \
'(' -type f -a -name '*.go' -a -not -name '*_test.go' ')' -print)
TOOLS = bin/pikchr bin/freeze
CC ?= gcc
.PHONY: build
build: install generate
pipenv run mkdocs build
.PHONY: serve
serve: install generate
pipenv run mkdocs serve
.PHONY: install
install: .cache/.install $(TOOLS)
.PHONY: generate
generate: $(GS)
$(GS) dumpmd \
--ref includes/cli-reference.md \
--shorthands includes/cli-shorthands.md
# Marker file for having run 'pipenv install'.
.cache/.install: Pipfile.lock
pipenv install
mkdir -p .cache
touch .cache/.install
bin/pikchr: cmd/pikchr/pikchr.c
gcc -DPIKCHR_SHELL -o $@ $< -lm
bin/gs: $(GO_SRC_FILES)
go install -tags dumpmd go.abhg.dev/gs
bin/freeze: go.mod
go install github.com/charmbracelet/freeze
+13
View File
@@ -0,0 +1,13 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
mkdocs-material = {extras = ["imaging"], version = "*"}
markdown-exec = {extras = ["ansi"], version = "*"}
[dev-packages]
[requires]
python_version = "3.12"
+770
View File
@@ -0,0 +1,770 @@
{
"_meta": {
"hash": {
"sha256": "9163ac195dc2c2d6a084be93e43235ba89fc8c99eed4e319556a92438aebd959"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.12"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"babel": {
"hashes": [
"sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb",
"sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"
],
"markers": "python_version >= '3.8'",
"version": "==2.15.0"
},
"cairocffi": {
"hashes": [
"sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b",
"sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f"
],
"markers": "python_version >= '3.8'",
"version": "==1.7.1"
},
"cairosvg": {
"hashes": [
"sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0",
"sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b"
],
"version": "==2.7.1"
},
"certifi": {
"hashes": [
"sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b",
"sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"
],
"markers": "python_version >= '3.6'",
"version": "==2024.7.4"
},
"cffi": {
"hashes": [
"sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc",
"sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a",
"sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417",
"sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab",
"sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520",
"sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36",
"sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743",
"sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8",
"sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed",
"sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684",
"sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56",
"sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324",
"sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d",
"sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235",
"sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e",
"sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088",
"sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000",
"sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7",
"sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e",
"sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673",
"sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c",
"sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe",
"sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2",
"sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098",
"sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8",
"sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a",
"sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0",
"sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b",
"sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896",
"sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e",
"sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9",
"sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2",
"sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b",
"sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6",
"sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404",
"sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f",
"sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0",
"sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4",
"sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc",
"sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936",
"sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba",
"sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872",
"sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb",
"sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614",
"sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1",
"sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d",
"sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969",
"sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b",
"sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4",
"sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627",
"sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956",
"sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"
],
"markers": "python_version >= '3.8'",
"version": "==1.16.0"
},
"charset-normalizer": {
"hashes": [
"sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027",
"sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087",
"sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786",
"sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8",
"sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09",
"sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185",
"sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574",
"sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e",
"sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519",
"sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898",
"sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269",
"sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3",
"sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f",
"sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6",
"sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8",
"sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a",
"sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73",
"sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc",
"sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714",
"sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2",
"sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc",
"sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce",
"sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d",
"sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e",
"sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6",
"sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269",
"sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96",
"sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d",
"sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a",
"sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4",
"sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77",
"sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d",
"sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0",
"sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed",
"sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068",
"sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac",
"sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25",
"sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8",
"sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab",
"sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26",
"sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2",
"sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db",
"sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f",
"sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5",
"sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99",
"sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c",
"sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d",
"sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811",
"sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa",
"sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a",
"sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03",
"sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b",
"sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04",
"sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c",
"sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001",
"sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458",
"sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389",
"sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99",
"sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985",
"sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537",
"sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238",
"sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f",
"sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d",
"sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796",
"sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a",
"sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143",
"sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8",
"sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c",
"sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5",
"sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5",
"sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711",
"sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4",
"sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6",
"sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c",
"sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7",
"sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4",
"sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b",
"sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae",
"sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12",
"sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c",
"sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae",
"sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8",
"sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887",
"sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b",
"sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4",
"sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f",
"sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5",
"sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33",
"sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519",
"sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"
],
"markers": "python_full_version >= '3.7.0'",
"version": "==3.3.2"
},
"click": {
"hashes": [
"sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
"sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.7"
},
"colorama": {
"hashes": [
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
"version": "==0.4.6"
},
"cssselect2": {
"hashes": [
"sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a",
"sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"
],
"markers": "python_version >= '3.7'",
"version": "==0.7.0"
},
"defusedxml": {
"hashes": [
"sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69",
"sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.7.1"
},
"ghp-import": {
"hashes": [
"sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619",
"sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"
],
"version": "==2.1.0"
},
"idna": {
"hashes": [
"sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc",
"sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"
],
"markers": "python_version >= '3.5'",
"version": "==3.7"
},
"jinja2": {
"hashes": [
"sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369",
"sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.4"
},
"markdown": {
"hashes": [
"sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f",
"sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"
],
"markers": "python_version >= '3.8'",
"version": "==3.6"
},
"markdown-exec": {
"extras": [
"ansi"
],
"hashes": [
"sha256:6ad206b75306020779144835f44de115235903755c46a7ccd3b754aad74661e1",
"sha256:6b3f5f35d9c74e3c7ba9011376ad1732aa3ed3806cadefb8522dee3afe02b1ec"
],
"markers": "python_version >= '3.8'",
"version": "==1.9.3"
},
"markupsafe": {
"hashes": [
"sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf",
"sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff",
"sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f",
"sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3",
"sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532",
"sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f",
"sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617",
"sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df",
"sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4",
"sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906",
"sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f",
"sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4",
"sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8",
"sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371",
"sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2",
"sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465",
"sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52",
"sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6",
"sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169",
"sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad",
"sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2",
"sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0",
"sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029",
"sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f",
"sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a",
"sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced",
"sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5",
"sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c",
"sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf",
"sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9",
"sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb",
"sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad",
"sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3",
"sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1",
"sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46",
"sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc",
"sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a",
"sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee",
"sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900",
"sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5",
"sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea",
"sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f",
"sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5",
"sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e",
"sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a",
"sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f",
"sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50",
"sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a",
"sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b",
"sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4",
"sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff",
"sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2",
"sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46",
"sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b",
"sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf",
"sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5",
"sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5",
"sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab",
"sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd",
"sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.5"
},
"mergedeep": {
"hashes": [
"sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8",
"sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"
],
"markers": "python_version >= '3.6'",
"version": "==1.3.4"
},
"mkdocs": {
"hashes": [
"sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7",
"sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"
],
"markers": "python_version >= '3.8'",
"version": "==1.6.0"
},
"mkdocs-get-deps": {
"hashes": [
"sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c",
"sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"
],
"markers": "python_version >= '3.8'",
"version": "==0.2.0"
},
"mkdocs-material": {
"extras": [
"imaging"
],
"hashes": [
"sha256:3e977598ec15a4ddad5c4dfc9e08edab6023edb51e88f0729bd27be77e3d322a",
"sha256:afc1f508e2662ded95f0a35a329e8a5acd73ee88ca07ba73836eb6fcdae5d8b4"
],
"markers": "python_version >= '3.8'",
"version": "==9.5.29"
},
"mkdocs-material-extensions": {
"hashes": [
"sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443",
"sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"
],
"markers": "python_version >= '3.8'",
"version": "==1.3.1"
},
"packaging": {
"hashes": [
"sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002",
"sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"
],
"markers": "python_version >= '3.8'",
"version": "==24.1"
},
"paginate": {
"hashes": [
"sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"
],
"version": "==0.5.6"
},
"pathspec": {
"hashes": [
"sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08",
"sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"
],
"markers": "python_version >= '3.8'",
"version": "==0.12.1"
},
"pillow": {
"hashes": [
"sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885",
"sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea",
"sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df",
"sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5",
"sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c",
"sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d",
"sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd",
"sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06",
"sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908",
"sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a",
"sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be",
"sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0",
"sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b",
"sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80",
"sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a",
"sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e",
"sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9",
"sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696",
"sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b",
"sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309",
"sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e",
"sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab",
"sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d",
"sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060",
"sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d",
"sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d",
"sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4",
"sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3",
"sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6",
"sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb",
"sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94",
"sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b",
"sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496",
"sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0",
"sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319",
"sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b",
"sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856",
"sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef",
"sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680",
"sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b",
"sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42",
"sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e",
"sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597",
"sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a",
"sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8",
"sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3",
"sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736",
"sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da",
"sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126",
"sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd",
"sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5",
"sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b",
"sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026",
"sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b",
"sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc",
"sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46",
"sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2",
"sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c",
"sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe",
"sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984",
"sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a",
"sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70",
"sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca",
"sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b",
"sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91",
"sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3",
"sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84",
"sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1",
"sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5",
"sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be",
"sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f",
"sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc",
"sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9",
"sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e",
"sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141",
"sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef",
"sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22",
"sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27",
"sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e",
"sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"
],
"version": "==10.4.0"
},
"platformdirs": {
"hashes": [
"sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee",
"sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"
],
"markers": "python_version >= '3.8'",
"version": "==4.2.2"
},
"pycparser": {
"hashes": [
"sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
"sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
],
"markers": "python_version >= '3.8'",
"version": "==2.22"
},
"pygments": {
"hashes": [
"sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199",
"sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"
],
"markers": "python_version >= '3.8'",
"version": "==2.18.0"
},
"pygments-ansi-color": {
"hashes": [
"sha256:7018954cf5b11d1e734383a1bafab5af613213f246109417fee3f76da26d5431",
"sha256:7eb063feaecadad9d4d1fd3474cbfeadf3486b64f760a8f2a00fc25392180aba"
],
"version": "==0.3.0"
},
"pymdown-extensions": {
"hashes": [
"sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940",
"sha256:f938326115884f48c6059c67377c46cf631c733ef3629b6eed1349989d1b30cb"
],
"markers": "python_version >= '3.8'",
"version": "==10.8.1"
},
"python-dateutil": {
"hashes": [
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
"sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.0.post0"
},
"pyyaml": {
"hashes": [
"sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",
"sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc",
"sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df",
"sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741",
"sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206",
"sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27",
"sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595",
"sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62",
"sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98",
"sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696",
"sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290",
"sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9",
"sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d",
"sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6",
"sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867",
"sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47",
"sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486",
"sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6",
"sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3",
"sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007",
"sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938",
"sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0",
"sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c",
"sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735",
"sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d",
"sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28",
"sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4",
"sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba",
"sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
"sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef",
"sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5",
"sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd",
"sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3",
"sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0",
"sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515",
"sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c",
"sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c",
"sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924",
"sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34",
"sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43",
"sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859",
"sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673",
"sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54",
"sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a",
"sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b",
"sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab",
"sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa",
"sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c",
"sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585",
"sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d",
"sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"
],
"markers": "python_version >= '3.6'",
"version": "==6.0.1"
},
"pyyaml-env-tag": {
"hashes": [
"sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb",
"sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"
],
"markers": "python_version >= '3.6'",
"version": "==0.1"
},
"regex": {
"hashes": [
"sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649",
"sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35",
"sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb",
"sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68",
"sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5",
"sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133",
"sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0",
"sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d",
"sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da",
"sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f",
"sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d",
"sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53",
"sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa",
"sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a",
"sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890",
"sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67",
"sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c",
"sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2",
"sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced",
"sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741",
"sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f",
"sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa",
"sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf",
"sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4",
"sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5",
"sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2",
"sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384",
"sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7",
"sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014",
"sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704",
"sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5",
"sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2",
"sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49",
"sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1",
"sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694",
"sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629",
"sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6",
"sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435",
"sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c",
"sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835",
"sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e",
"sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201",
"sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62",
"sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5",
"sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16",
"sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f",
"sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1",
"sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f",
"sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f",
"sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145",
"sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3",
"sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed",
"sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143",
"sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca",
"sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9",
"sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa",
"sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850",
"sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80",
"sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe",
"sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656",
"sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388",
"sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1",
"sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294",
"sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3",
"sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d",
"sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b",
"sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40",
"sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600",
"sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c",
"sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569",
"sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456",
"sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9",
"sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb",
"sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e",
"sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f",
"sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d",
"sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a",
"sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a",
"sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"
],
"markers": "python_version >= '3.8'",
"version": "==2024.5.15"
},
"requests": {
"hashes": [
"sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
"sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
],
"markers": "python_version >= '3.8'",
"version": "==2.32.3"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"tinycss2": {
"hashes": [
"sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d",
"sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"
],
"markers": "python_version >= '3.8'",
"version": "==1.3.0"
},
"urllib3": {
"hashes": [
"sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472",
"sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"
],
"markers": "python_version >= '3.8'",
"version": "==2.2.2"
},
"watchdog": {
"hashes": [
"sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7",
"sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767",
"sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175",
"sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459",
"sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5",
"sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429",
"sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6",
"sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d",
"sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7",
"sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28",
"sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235",
"sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57",
"sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a",
"sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5",
"sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709",
"sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee",
"sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84",
"sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd",
"sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba",
"sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db",
"sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682",
"sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35",
"sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d",
"sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645",
"sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253",
"sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193",
"sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b",
"sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44",
"sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b",
"sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625",
"sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e",
"sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"
],
"markers": "python_version >= '3.8'",
"version": "==4.0.1"
},
"webencodings": {
"hashes": [
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
],
"version": "==0.5.1"
}
},
"develop": {}
}
-4
View File
@@ -1,4 +0,0 @@
# git-spice
- [Introduction](intro.md)
- [License](license.md)
+1
View File
@@ -0,0 +1 @@
pikchr.c linguist-vendored
+8246
View File
File diff suppressed because it is too large Load Diff
-4
View File
@@ -1,4 +0,0 @@
// Package doc is a placeholder for documentation.
package doc
//go:generate gs dump-md --out reference.md
+66
View File
@@ -0,0 +1,66 @@
module go.abhg.dev/gs/doc
go 1.22.5
replace go.abhg.dev/gs => ../
require (
github.com/charmbracelet/freeze v0.1.7-0.20240607113933-578e9d05c319
go.abhg.dev/gs v0.1.0
)
require (
github.com/adrg/xdg v0.4.0 // indirect
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
github.com/alecthomas/kong v0.9.0 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beevik/etree v1.3.0 // indirect
github.com/buildkite/shellwords v0.0.0-20180315110454-59467a9b8e10 // indirect
github.com/caarlos0/go-shellwords v1.0.12 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/charmbracelet/bubbles v0.18.0 // indirect
github.com/charmbracelet/bubbletea v0.26.6 // indirect
github.com/charmbracelet/huh v0.3.1-0.20240327025511-ec643317aa10 // indirect
github.com/charmbracelet/lipgloss v0.12.1 // indirect
github.com/charmbracelet/log v0.4.0 // indirect
github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240327191656-1e1cd98f30d4 // indirect
github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4 // indirect
github.com/charmbracelet/x/input v0.1.1 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.2 // indirect
github.com/creack/pty v1.1.21 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064 // indirect
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
github.com/tetratelabs/wazero v1.7.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/zalando/go-keyring v0.2.5 // indirect
go.abhg.dev/requiredfield v0.3.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.20.0 // indirect
)
+149
View File
@@ -0,0 +1,149 @@
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA=
github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
github.com/buildkite/shellwords v0.0.0-20180315110454-59467a9b8e10 h1:XwHQ5xDtYPdtBbVPyRO6UZoWZe8/mbKUb076f8x7RvI=
github.com/buildkite/shellwords v0.0.0-20180315110454-59467a9b8e10/go.mod h1:gv0DYOzHEsKgo31lTCDGauIg4DTTGn41Bzp+t3wSOlk=
github.com/caarlos0/go-shellwords v1.0.12 h1:HWrUnu6lGbWfrDcFiHcZiwOLzHWjjrPVehULaTFgPp8=
github.com/caarlos0/go-shellwords v1.0.12/go.mod h1:bYeeX1GrTLPl5cAMYEzdm272qdsQAZiaHgeF0KTk1Gw=
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
github.com/charmbracelet/freeze v0.1.7-0.20240607113933-578e9d05c319 h1:t9ALQwGQH/Xmh267X1QYKdXwBpwCmWEYBlg76ajFAr0=
github.com/charmbracelet/freeze v0.1.7-0.20240607113933-578e9d05c319/go.mod h1:gQ/e9NCWaG1E/tuLF0u7xn8XFJVsU4hm0O9pYR6Ii8U=
github.com/charmbracelet/huh v0.3.1-0.20240327025511-ec643317aa10 h1:779PmXc9Zt/Hxa0yg4I8sQk/1Nfa5TQisXaHZEW60Yk=
github.com/charmbracelet/huh v0.3.1-0.20240327025511-ec643317aa10/go.mod h1:x0rYoA1kpsaefXhRJZuxLM+qP4CYyEFE67T3ZGl7zPU=
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI=
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
github.com/charmbracelet/x/exp/strings v0.0.0-20240327191656-1e1cd98f30d4 h1:E3qdydPjQJIGnT6t6lY0P70sou+Vmh+VcQHAYR4qTtA=
github.com/charmbracelet/x/exp/strings v0.0.0-20240327191656-1e1cd98f30d4/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4 h1:LewLBFkff+bCxgMZn1m8xNYQbUksWaY71d1QARHA11s=
github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE=
github.com/charmbracelet/x/input v0.1.1 h1:YDOJaTUKCqtGnq9PHzx3pkkl4pXDOANUHmhH3DqMtM4=
github.com/charmbracelet/x/input v0.1.1/go.mod h1:jvdTVUnNWj/RD6hjC4FsoB0SeZCJ2ZBkiuFP9zXvZI0=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg=
github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty/v2 v2.0.1 h1:RDY1VY5b+7m2mfPsugucOYPIxMp+xal5ZheSyVzUA+k=
github.com/creack/pty/v2 v2.0.1/go.mod h1:2dSssKp3b86qYEMwA/FPwc3ff+kYpDdQI8osU8J7gxQ=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064 h1:RCQBSFx5JrsbHltqTtJ+kN3U0Y3a/N/GlVdmRSoxzyE=
github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ=
github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
github.com/vito/midterm v0.1.5-0.20240307214207-d0271a7ca452 h1:I5FdiUvkD++87hOiZYuDu0BqsaJXAnpOCed3kqkjCEE=
github.com/vito/midterm v0.1.5-0.20240307214207-d0271a7ca452/go.mod h1:2ujYuyOObdWrQtnXAzwSBcPRKhC5Q96ex0nsf2Dmfzk=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
go.abhg.dev/requiredfield v0.3.0 h1:/fOqPt7t1SktSZVGUNXMg5djO8GasUh+G1ez111UEcs=
go.abhg.dev/requiredfield v0.3.0/go.mod h1:ML+QbQ96X7jzJK68sDI4BafOEIZNAB4DHLXl78TrPZI=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/dnaeon/go-vcr.v3 v3.2.0 h1:Rltp0Vf+Aq0u4rQXgmXgtgoRDStTnFN83cWgSGSoRzM=
gopkg.in/dnaeon/go-vcr.v3 v3.2.0/go.mod h1:2IMOnnlx9I6u9x+YBsM3tAMx6AlOxnJ0pWxQAzZ79Ag=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
+32
View File
@@ -0,0 +1,32 @@
# Introduces a $$...$$ syntax for references to CLI commands in the reference.
# $$command|text$$ will use {text} as the link text.
import re
from mkdocs.structure.pages import Page
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.files import Files
_CLI_PAGE = "cli/index.md"
_cmd_re = re.compile(r"\$\$([^$]+)\$\$")
def on_page_markdown(
markdown: str,
page: Page,
config: MkDocsConfig,
files: Files,
) -> str:
# Don't process the target page itself.
if page.file.src_uri == _CLI_PAGE:
return markdown
def _replace(match):
cmd = match.group(1)
text = cmd
if "|" in cmd:
cmd, text = cmd.split("|", 1)
id = cmd.replace(" ", "-")
return f'[:material-console:{{ .middle }} {text}](/{_CLI_PAGE}#{id})'
return _cmd_re.sub(_replace, markdown)
+60
View File
@@ -0,0 +1,60 @@
import logging
from os.path import normpath
from mkdocs.structure.pages import Page
from mkdocs.utils.templates import TemplateContext
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.nav import Navigation
log = logging.getLogger("mkdocs.plugins.footer")
# A variant of the footer functionality in mkdocs-material
# but with support for pages to define their own next_page or prev_page
# in front matter.
#
# Useful for pages that are not part of the main nav,
# but still want to have a next/prev page.
def on_page_context(
ctx: TemplateContext,
page: Page,
config: MkDocsConfig,
nav: Navigation,
) -> TemplateContext:
next_page_name = page.meta.get('next_page', None)
prev_page_name = page.meta.get('prev_page', None)
if not next_page_name and not prev_page_name:
return ctx
# foo/bar/baz.md -> foo/bar
uri = page.file.src_uri
dir = '/'.join(uri.split('/')[:-1])
next_page_uri, prev_page_uri = None, None
if next_page_name:
next_page_uri = normpath(f'{dir}/{next_page_name}')
if prev_page_name:
prev_page_uri = normpath(f'{dir}/{prev_page_name}')
next_page, prev_page = None, None
for file in ctx['pages']:
uri = file.src_uri
if next_page_uri and uri == next_page_uri:
next_page = file.page
if prev_page_uri and uri == prev_page_uri:
prev_page = file.page
if next_page_name and next_page is None:
log.warn(f'page {next_page_uri} not found')
if prev_page_name and prev_page is None:
log.warn(f'page {prev_page_uri} not found')
if not hasattr(page, 'overrides'):
page.overrides = {}
if next_page:
page.next_page = next_page
if prev_page:
page.previous_page = prev_page
return ctx
+137
View File
@@ -0,0 +1,137 @@
import re
import subprocess
import tempfile
from typing import Any
from mkdocs.config.defaults import MkDocsConfig
def on_config(config: MkDocsConfig) -> MkDocsConfig | None:
mdx_configs = config.setdefault("mdx_configs", {})
superfences = mdx_configs.setdefault("pymdownx.superfences", {})
custom_fences = superfences.setdefault("custom_fences", [])
custom_fences.append({
"name": "freeze",
"class": "freeze",
"validator": _validator,
"format": _formatter
})
def _validator(
language: str,
inputs: dict[str, str],
options: dict[str, Any],
attrs: dict[str, Any], # noqa: ARG001
*args, **kwargs,
) -> bool:
default_center = "true"
if "float" in inputs:
options["float"] = inputs.pop("float")
default_center = "false"
if "width" in inputs:
options["width"] = inputs.pop("width")
if "language" in inputs:
options["language"] = inputs.pop("language")
options["center"] = inputs.pop("center", default_center) == "true"
if options["center"]:
options.pop("float", None)
return "language" in options
# freeze generates svgs with a width and height parameter.
# We can use that to generate a viewBox attribute,
# allowing the svg to scale to the width of the container.
_widthRe = re.compile(r'width="(?P<width>[\d\.]+)"')
_heightRe = re.compile(r'height="(?P<height>[\d\.]+)"')
_terminalReplacements = [
('\\x1b', '\x1b'),
('{red}', '\x1b[0;31m'),
('{green}', '\x1b[0;32m'),
('{yellow}', '\x1b[0;33m'),
('{blue}', '\x1b[0;34m'),
('{mag}', '\x1b[0;35m'),
('{cyan}', '\x1b[0;36m'),
('{gray}', '\x1b[0;90m'),
('{reset}', '\x1b[0;0m'),
]
# run freeze --window --language=$language in a subprocess,
# writing to a temporary file.
def _formatter(
source: str,
language: str,
css_class: str,
options: dict[str, Any],
*args, **kwargs,
):
# Convenience:
# If language is terminal, we actually want "ansi",
# but we want to transform escapes in the source.
if options["language"] == "terminal":
options["language"] = "ansi"
for pattern, replacement in _terminalReplacements:
source = source.replace(pattern, replacement)
with tempfile.TemporaryDirectory() as tmpdir:
outfile = f"{tmpdir}/output.svg"
try:
args = [
"freeze",
"--language", options["language"],
"--output", outfile,
# same as -c full, but with no shadow.
# auto-width calculation is broken with shadow.
"--window", "--theme=charm",
"--border.radius=8", "--border.width=1",
"--border.color=#515151",
"--padding=10,10,10,10",
"--margin=10,10,10,10",
"--background=#171717",
]
subprocess.run(
args, input=source.encode("utf-8"), check=True,
)
except subprocess.CalledProcessError as e:
return f'<code>{e.output.decode("utf-8")}</code>'
except Exception as e:
return f'<code>{e}</code>'
with open(outfile, "r") as f:
svg = f.read()
width, height = None, None
if m := _widthRe.search(svg):
width = m.group("width")
if m := _heightRe.search(svg):
height = m.group("height")
if not width or not height:
return '<code>Could not find width and height in svg</code>'
# insert viewBox="0 0 width height" into the svg,
# and drop the width and height attributes.
svg = svg.replace(
'<svg', f'<svg viewBox="0 0 {width} {height}"', 1,
)
svg = svg.replace(f' width="{width}"', "", 1)
svg = svg.replace(f' height="{height}"', "", 1)
if "width" in options:
width = options["width"]
height = "auto"
else:
width = f"{width}px"
height = f"{height}px"
style = f'width:{width};height:{height};max-width:100%;'
if "float" in options:
style += f'float:{options["float"]};'
if options["center"]:
style += 'margin:0 auto;'
return f'<div style="{style}">{svg}</div>'
+64
View File
@@ -0,0 +1,64 @@
import logging
from os.path import normpath
from mkdocs.structure.pages import Page
from mkdocs.utils.templates import TemplateContext
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.nav import Navigation
log = logging.getLogger("mkdocs.plugins.listing")
# Given a page with meta:
#
# listing:
# - foo.md
# - bar.md
#
# Will add a listing attribute to that page
# with the pages for foo.md and bar.md.
#
# Use with template: listing.html.
def on_page_context(
ctx: TemplateContext,
page: Page,
config: MkDocsConfig,
nav: Navigation,
) -> TemplateContext:
# cur_page = page
children = page.meta.get('listing', [])
if not children:
return ctx
# foo/bar/baz.md -> foo/bar
uri = page.file.src_uri
dir = '/'.join(uri.split('/')[:-1])
want_uris = [normpath(f'{dir}/{child}') for child in children]
got_pages = [None] * len(want_uris)
# mapping from requested page URI
# to index of that page in got_pages.
# Will be used to fill in got_pages
# as we iterate over all pages.
page_idxes = {
uri: idx for idx, uri in enumerate(want_uris)
}
for file in ctx['pages']:
uri = file.src_uri
idx = page_idxes.get(uri)
if idx is not None:
got_pages[idx] = file.page
# If there are any missing pages, fail.
result = []
for idx, page in enumerate(got_pages):
if page is None:
log.warn(f'page {want_uris[idx]} not found')
result.append(page)
ctx['page_listing'] = result
return ctx
+81
View File
@@ -0,0 +1,81 @@
import re
import subprocess
from typing import Any
from mkdocs.config.defaults import MkDocsConfig
def on_config(config: MkDocsConfig) -> MkDocsConfig | None:
mdx_configs = config.setdefault("mdx_configs", {})
superfences = mdx_configs.setdefault("pymdownx.superfences", {})
custom_fences = superfences.setdefault("custom_fences", [])
custom_fences.append({
"name": "pikchr",
"class": "pikchr",
"validator": _validator,
"format": _formatter
})
def _validator(
language: str,
inputs: dict[str, str],
options: dict[str, Any],
attrs: dict[str, Any], # noqa: ARG001
*args, **kwargs,
) -> bool:
options["width"] = inputs.pop("width", "100%")
default_center = "true"
if "float" in inputs:
options["float"] = inputs.pop("float")
default_center = "false"
options["center"] = inputs.pop("center", default_center) == "true"
if options["center"]:
options.pop("float", None)
return True
_viewboxRe = re.compile(
r'viewBox="\d+ +\d+ +(?P<w>[\d\.]+) +(?P<h>[\d\.]+)"',
)
# Run 'pikchr -' with the source code as input
# and return the SVG output as a string.
def _formatter(
source: str,
language: str,
css_class: str,
options: dict[str, Any],
*args, **kwargs,
):
try:
svg = subprocess.run(
["pikchr", "--dark-mode", "--svg-only", "-"],
input=source.encode("utf-8"),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True
).stdout.decode("utf-8")
width = options["width"]
height = "auto"
match = _viewboxRe.search(svg)
if match:
w, h = map(float, match.groups())
width = f"{w}px"
height = f"{h}px"
style = f'width:{width};height:{height};max-width:100%;'
if "float" in options:
style += f'float:{options["float"]};'
if options["center"]:
style += 'margin:0 auto;'
return f'<div style="{style}">{svg}</div>'
except subprocess.CalledProcessError as e:
return f'<code>{e.output.decode("utf-8")}</code>'
except Exception as e:
return f'<code>{e}</code>'
+5
View File
@@ -0,0 +1,5 @@
$ gs branch submit
Title: branch submit: Fix directory+ref conflict
Body: Press [e] to open nvim or [enter/tab] to skip
Draft: [y/N]
INF Created #261: https://github.com/abhinav/git-spice/pull/261
+1
View File
@@ -0,0 +1 @@
../../CHANGELOG.md
@@ -1,5 +1,3 @@
# gs command reference
```
gs <command> [flags]
```
@@ -411,7 +409,7 @@ Forget a tracked branch
The current branch is deleted from git-spice's data store
but not deleted from the repository.
Branches upstack from it are not affected,
and will to the next branch downstack.
and will use the next branch downstack as their new base.
Provide a branch name as an argument to target
a different branch.
+31
View File
@@ -0,0 +1,31 @@
| **Shorthand** | **Long form** |
| --- | --- |
| gs bc | [gs branch create](/cli/index.md#gs-branch-create) |
| gs bco | [gs branch checkout](/cli/index.md#gs-branch-checkout) |
| gs bd | [gs branch delete](/cli/index.md#gs-branch-delete) |
| gs be | [gs branch edit](/cli/index.md#gs-branch-edit) |
| gs bfo | [gs branch fold](/cli/index.md#gs-branch-fold) |
| gs bon | [gs branch onto](/cli/index.md#gs-branch-onto) |
| gs br | [gs branch restack](/cli/index.md#gs-branch-restack) |
| gs brn | [gs branch rename](/cli/index.md#gs-branch-rename) |
| gs bs | [gs branch submit](/cli/index.md#gs-branch-submit) |
| gs bsp | [gs branch split](/cli/index.md#gs-branch-split) |
| gs btr | [gs branch track](/cli/index.md#gs-branch-track) |
| gs buntr | [gs branch untrack](/cli/index.md#gs-branch-untrack) |
| gs ca | [gs commit amend](/cli/index.md#gs-commit-amend) |
| gs cc | [gs commit create](/cli/index.md#gs-commit-create) |
| gs csp | [gs commit split](/cli/index.md#gs-commit-split) |
| gs dse | [gs downstack edit](/cli/index.md#gs-downstack-edit) |
| gs dss | [gs downstack submit](/cli/index.md#gs-downstack-submit) |
| gs ll | [gs log long](/cli/index.md#gs-log-long) |
| gs ls | [gs log short](/cli/index.md#gs-log-short) |
| gs rba | [gs rebase abort](/cli/index.md#gs-rebase-abort) |
| gs rbc | [gs rebase continue](/cli/index.md#gs-rebase-continue) |
| gs ri | [gs repo init](/cli/index.md#gs-repo-init) |
| gs rs | [gs repo sync](/cli/index.md#gs-repo-sync) |
| gs se | [gs stack edit](/cli/index.md#gs-stack-edit) |
| gs sr | [gs stack restack](/cli/index.md#gs-stack-restack) |
| gs ss | [gs stack submit](/cli/index.md#gs-stack-submit) |
| gs uso | [gs upstack onto](/cli/index.md#gs-upstack-onto) |
| gs usr | [gs upstack restack](/cli/index.md#gs-upstack-restack) |
| gs uss | [gs upstack submit](/cli/index.md#gs-upstack-submit) |
-13
View File
@@ -1,13 +0,0 @@
# Introduction
<div align="center">
<img src="img/logo.png" width=300>
[![CI](https://github.com/abhinav/git-spice/actions/workflows/ci.yml/badge.svg)](https://github.com/abhinav/git-spice/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/abhinav/git-spice/graph/badge.svg?token=FE4S370I4A)](https://codecov.io/gh/abhinav/git-spice)
</div>
git-spice is a tool for stacking Git branches.
TODO: more details
-29
View File
@@ -1,29 +0,0 @@
# License
This software is distributed under the GPL-3.0 License:
```
git-spice: Stacked Pull Requests
Copyright (C) 2024 Abhinav Gupta
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
```
See LICENSE for details.
As an exception to the above, the following files are distributed
under a different license. See the file headers for details:
internal/komplete/komplete.go
internal/komplete/komplete_test.go
+126
View File
@@ -0,0 +1,126 @@
site_name: git-spice
site_url: https://abhinav.github.io/git-spice
site_description: >-
git-spice is a tool for stacking Git branches.
It helps you manage and navigate a stack of branches
that build on top of each other.
copyright: '&copy; 2024 Abhinav Gupta'
repo_url: https://github.com/abhinav/git-spice
repo_name: abhinav/git-spice
edit_uri: edit/main/doc/src/
docs_dir: src
site_dir: _site
extra:
analytics:
provider: custom
token: !ENV [CLOUDFLARE_ANALYTICS_TOKEN, '']
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/abhinav
- icon: fontawesome/brands/mastodon
link: https://hachyderm.io/@abhinav
- icon: fontawesome/brands/bluesky
link: https://bsky.app/profile/abhg.dev
validation:
omitted_files: warn
absolute_links: relative_to_docs
unrecognized_links: warn
anchors: warn # New in MkDocs 1.6
strict: true
theme:
name: material
custom_dir: overrides
palette:
scheme: slate
logo: img/logo.png
favicon: img/logo.png
icon:
repo: fontawesome/brands/github-alt
features:
- content.action.edit
- content.code.copy
- header.autohide
- navigation.expand
- navigation.indexes
- search.suggest
- toc.follow
exclude_docs: |
img/README.md
plugins:
- markdown-exec
- privacy:
enabled: !ENV [CI, false]
- search
- social:
enabled: !ENV [CI, false]
hooks:
- hooks/cliref.py
- hooks/footer.py
- hooks/freeze.py
- hooks/listing.py
- hooks/pikchr.py
markdown_extensions:
- admonition
- attr_list
- def_list
- md_in_html
- pymdownx.details
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.snippets:
base_path: [includes]
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
slugify: !!python/object/apply:pymdownx.slugs.slugify
kwds:
case: lower
- pymdownx.tasklist:
custom_checkbox: true
- toc: {permalink: true}
nav:
- Home: index.md
- Get started:
- start/index.md
- start/install.md
- User Guide:
- guide/index.md
- guide/concepts.md
- guide/branch.md
- guide/pr.md
- guide/limits.md
- guide/internals.md
- Setting up:
- setup/index.md
- setup/auth.md
- setup/completion.md
- CLI:
- cli/index.md
- cli/shorthand.md
- how-to.md
- FAQ: faq.md
- Changelog: changelog.md
not_in_nav: |
# Listed manually
start/stack.md
start/submit.md
watch:
- includes
- overrides
+74
View File
@@ -0,0 +1,74 @@
<!-- Footer -->
<footer class="md-footer">
<!-- Link to previous and/or next page -->
{% if page.previous_page or page.next_page %}
{% if page.meta and page.meta.hide %}
{% set hidden = "hidden" if "footer" in page.meta.hide %}
{% endif %}
<nav
class="md-footer__inner md-grid"
aria-label="{{ lang.t('footer') }}"
{{ hidden }}
>
<!-- Link to previous page -->
{% if page.previous_page %}
{% set direction = lang.t("footer.previous") %}
<a
href="{{ page.previous_page.url | url }}"
class="md-footer__link md-footer__link--prev"
aria-label="{{ direction }}: {{ page.previous_page.title | e }}"
>
<div class="md-footer__button md-icon">
{% set icon = config.theme.icon.previous or "material/arrow-left" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
{{ direction }}
</span>
<div class="md-ellipsis">
{{ page.previous_page.title }}
</div>
</div>
</a>
{% endif %}
<!-- Link to next page -->
{% if page.next_page %}
{% set direction = lang.t("footer.next") %}
<a
href="{{ page.next_page.url | url }}"
class="md-footer__link md-footer__link--next"
aria-label="{{ direction }}: {{ page.next_page.title | e }}"
>
<div class="md-footer__title">
<span class="md-footer__direction">
{{ direction }}
</span>
<div class="md-ellipsis">
{{ page.next_page.title }}
</div>
</div>
<div class="md-footer__button md-icon">
{% set icon = config.theme.icon.next or "material/arrow-right" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</div>
</a>
{% endif %}
</nav>
{% endif %}
<!-- Further information -->
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
{% include "partials/copyright.html" %}
<!-- Social links -->
{% if config.extra.social %}
{% include "partials/social.html" %}
{% endif %}
</div>
</div>
</footer>
@@ -0,0 +1,3 @@
{% if config.extra.analytics.token -%}
<script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "{{ config.extra.analytics.token }}"}'></script>
{%- endif %}
+58
View File
@@ -0,0 +1,58 @@
{% extends "base.html" %}
{% macro render_content(nav_item, ref = nav_item) %}
{% if nav_item.is_page and nav_item.meta.icon %}
<span class="twemoji lg middle">
{% include ".icons/" ~ nav_item.meta.icon ~ ".svg" %}
</span>
{% elif nav_item.is_section and nav_item.children %}
{% set first = nav_item.children | first %}
{% if first.is_index and first.meta.icon %}
<span class="twemoji lg middle">
{% include ".icons/" ~ first.meta.icon ~ ".svg" %}
</span>
{% endif %}
{% endif %}
<span class="md-ellipsis">
{{ ref.title }}
</span>
{% endmacro %}
{% macro render_item(item) %}
<li>
<a href="{{ item.url | url }}" class="md-nav__link"
{{ render_content(item) }}
</a>
{% if item.meta.description %}
<hr/>
<p>{{ item.meta.description }}</p>
{% endif %}
</li>
{% endmacro %}
{% macro render_index_children(index_page) %}
{% set parent = page.parent %}
{% if parent and (parent.children | length > 1) %}
<div class="grid cards">
<ul>
{% for item in parent.children %}
{% if item.is_page and item != index_page %}
{{ render_item(item) }}
{% elif item.is_section and item.children and (item.children|first).is_index %}
{{ render_item(item.children|first) }}
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{% endmacro %}
{% block content %}
{{ super() }}
{% if page.is_index %}
<hr/>
{{ render_index_children(page) }}
{% endif %}
{% endblock %}
+47
View File
@@ -0,0 +1,47 @@
{% extends "base.html" %}
{% macro render_content(nav_item, ref = nav_item) %}
{% if nav_item.is_page and nav_item.meta.icon %}
<span class="twemoji lg middle">
{% include ".icons/" ~ nav_item.meta.icon ~ ".svg" %}
</span>
{% elif nav_item.is_section and nav_item.children %}
{% set first = nav_item.children | first %}
{% if first.is_index and first.meta.icon %}
<span class="twemoji lg middle">
{% include ".icons/" ~ first.meta.icon ~ ".svg" %}
</span>
{% endif %}
{% endif %}
<span class="md-ellipsis">
{{ ref.title }}
</span>
{% endmacro %}
{% macro render_item(item) %}
<li>
<a href="{{ item.url | url }}" class="md-nav__link"
{{ render_content(item) }}
</a>
{% if item.meta.description %}
<hr/>
<p>{{ item.meta.description }}</p>
{% endif %}
</li>
{% endmacro %}
{% block content %}
{{ super() }}
{% if page_listing %}
<hr/>
<div class="grid cards">
<ul>
{% for item in page_listing %}
{{ render_item(item) }}
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
-2
View File
@@ -1,2 +0,0 @@
<!-- DO NOT EDIT. This file is generated from doc/*.md. Edit those instead. -->
+7
View File
@@ -0,0 +1,7 @@
---
icon: material/format-list-group
description: >-
Release history of the project.
---
--8<-- "changelog.md"
+10
View File
@@ -0,0 +1,10 @@
---
icon: material/console
description: >-
Comprehensive list of all available commands and options
for the git-spice CLI.
---
# CLI Reference
--8<-- "cli-reference.md"
+49
View File
@@ -0,0 +1,49 @@
---
icon: material/scissors-cutting
title: Shorthands
description: >-
Most commands come with intuitive shorthand variants
that are easy to remember and type.
---
# CLI shorthands
git-spice offers short versions of most commands
to make them easier to remember and type.
To determine the shorthand for a command,
run the command with the `--help` flag.
```freeze language="terminal"
{green}${reset} gs branch create --help
Usage: gs branch (b) create (c) [<name>] [flags]
{gray}# ...{reset}
```
The shorthand is built by joining the bits in parentheses.
For example, the shorthand for $$gs branch create$$ above is `gs bc`.
Another example:
```freeze language="terminal"
{green}${reset} gs branch checkout --help
Usage: gs branch (b) checkout (co) [<branch>] [flags]
{gray}# ...{reset}
```
The shorthand for $$gs branch checkout$$ is `gs bco`.
## When to use shorthands?
We encourage adopting the command shorthands
after you are comfortable with the corresponding full command names.
The shorthands are designed to be easy to remember and type,
but they may not be obvious if you don't know the full command name.
The shorthands act as a mnemonic aid:
invoke the full command name in your head while typing the shorthand.
## Available shorthands
--8<-- "cli-shorthands.md"
+37
View File
@@ -0,0 +1,37 @@
---
icon: material/frequently-asked-questions
description: >-
Frequently asked questions about git-spice,
a tool to manage stacked branches in Git.
---
# Frequently Asked Questions
## What is stacking?
Stacking refers to the practice of creating interdependent branches
on top of each other.
Each branch in the stack builds on top of the previous one.
For example, you might have a branch `feature-a` that adds a new feature,
and a branch `feature-b` that builds on top of `feature-a`.
Stacking your changes lets you:
- **Unblock yourself**:
While one branch is under review, you can start working on the next one.
- **Be kinder to your team**:
By keeping changes small and focused, you make it easier for your team
to review, test, and understand your work.
A 100 line PR gets a more meaningful review than a 1000 line PR.
git-spice helps you manage your stack of branches,
keeping them up-to-date and in sync with each other.
**Related**:
- [The stacking workflow](https://www.stacking.dev/)
## Where is the GitHub authentication token stored?
git-spice stores the GitHub authentication in a system-specific secure storage.
See [Secret storage](guide/internals.md#secret-storage) for more details.
+558
View File
@@ -0,0 +1,558 @@
---
title: Branch stacks
icon: octicons/git-branch-16
description: >-
Manage, navigate, and manipulate stacks of branches with git-spice.
---
# Branch stacks
Starting at the trunk branch, any number of branches may be stacked on top.
You may stack branches using one of the following methods:
- [**Automatic stacking**](#automatic-stacking):
Use git-spice to create a branch, commit to it,
and stack it on top of the current branch in one go.
- [**Manual stacking**](#manual-stacking):
Create a branch with Git as usual, and then track it with git-spice.
## Automatic stacking
```freeze language="terminal" float="right"
{green}${reset} $EDITOR file.txt
{gray}# make your changes{reset}
{green}${reset} git add file.txt
{green}${reset} gs branch create my-feature
```
The preferred way to create a stacked branch is:
make your changes, prepare them to be committed with `git add`,
and then use $$gs branch create$$ to commit these changes to a new branch
stacked on top of the current branch.
This creates a new branch `my-feature` stacked on top of the current branch,
and commits the staged changes to it.
An editor will open to let you write a commit message
if one was not provided with the `-m`/`--message` flag.
!!! tip "But I use `git commit -a`"
If you prefer to use `git commit -a` to automatically stage files
before committing, use `gs branch create -a` to do the same with git-spice.
Explore the full list of options at $$gs branch create$$.
## Manual stacking
git-spice does not require to change your workflow too drastically.
If you prefer to use your usual workflow to create branches and commit changes,
use $$gs branch track$$ to inform git-spice of the branch after creating it.
```freeze language="terminal"
{green}${reset} git checkout -b my-feature
{gray}# make your changes{reset}
{green}${reset} git commit
{green}${reset} gs branch track
{green}INF{reset} my-feature: tracking with base main
```
$$gs branch track$$ automatically detects the base branch.
Use the `--base` option to specify it manually.
## Navigating the stack
git-spice offers the following commands to navigate within a stack of branches:
<div class="grid" markdown>
<div markdown>
* $$gs down$$ checks out the branch below the current branch
* $$gs up$$ checks out a branch stacked on top of the current branch,
prompting to pick one if there are multiple
* $$gs bottom$$ moves to the bottommost branch in the stack,
right above the trunk
* $$gs top$$ moves to the topmost branch in the stack,
prompting to pick one if there are multiple
* $$gs trunk$$ checks out the trunk branch
* $$gs branch checkout$$ checks out any branch in the repository
</div>
```pikchr float="right"
lineht = 0.3in
linewid = 0.4in
up
Main: box "main" fit ht 0.25in fill 0xffffff
dot; line
box "A" same
dot; line
box "B" same fill 0xd0cee0
dot; line lineht/2
ADot: dot invis
left; line
up; line ht lineht/2
box "C" same fill A.fill
move to ADot
right; line
up; line ht lineht/2
box "D" same
dot; line
box "E" same
color = gray
linerad = 0.05in
arrow chop thin \
from B go left 0.3in \
then down until even with Main \
then to Main
text "gs trunk " mono rjust \
at 0.3in west of 1/2 way between B and Main
arrow chop thin \
from B go right 0.3in \
then down until even with A \
then to A
text " gs down" mono ljust \
at 0.3in east of 1/2 way between A and B
arrow behind Main chop thin \
from B up until even with C \
then to C
arrow behind Main chop thin \
from B up until even with D \
then to D
text "gs up" mono at 0.1in north of 1/2 way between C and D
arrow chop thin \
from B go right until even with 0.1in east of E.e \
then up until even with E \
then to E
text " gs top" mono ljust \
at (0.1in east of E.e.x, B.y+(E.y-B.y)/2)
arrow chop thin \
from B go left until even with 0.1in west of C.w \
then up until even with C \
then to C
text "gs top " mono rjust \
at (0.1in west of C.w.x, B.y+(C.y-B.y)/2)
```
</div>
These commands allow for relative movement within the stack,
so that you don't have to remember exact branch names
or their positions in the stack.
!!! question "What's the value of `gs branch checkout`?"
```freeze language="terminal" float="right"
{green}${reset} gs branch checkout my-feature
```
You may be wondering about $$gs branch checkout$$
when `git checkout` exists.
The command behaves not much differently than `git checkout`
when provided with a branch name as an argument.
```freeze language="terminal" float="right"
{green}${reset} gs branch checkout {gray}# or gs bco{reset}
{green}Select a branch to checkout{reset}:
┏━■ {yellow}docs ◀{reset}
┃ ┏━□ github-optimization
┣━┻□ github-support
main
```
Its real value lies in the interactive mode.
Invoke it without arguments to get a fuzzy-searchable list of branches,
visualized as a tree-like structure to help you navigate the stack.
## Committing and restacking
With a stacked branch checked out,
you can commit to it as usual with `git commit`, `git commit --amend`, etc.
However, after committing, the branches upstack from the current branch
need to be restacked to maintain a linear history.
Use $$gs upstack restack$$ to do this.
<div class="grid" markdown>
```freeze language="terminal"
{green}${reset} gs branch checkout feat1
{green}${reset} $EDITOR file.txt
{gray}# prepare your changes{reset}
{green}${reset} git commit
{green}${reset} gs upstack restack
```
```pikchr
linewid = 0.25in
X: [
right
text "A" small; line; text "B" small;
line go linewid heading 45; right; text "C" small
text "feat1" with e at A.w
text "feat2" with e at (last text.e.x, C.y)
line thin chop dotted from last to C
]
Y: [
right
text "A" small; line; text "B" small; line; text "D" small
move to B.ne;
line go linewid heading 45; right;
text "C" small
text "feat1" with e at A.w
text "feat2" with e at (last text.e.x, C.y)
line thin chop dotted from last to C
] with nw at 0.5in south of last.sw
Z: [
right
text "A" small; line; text "B" small; line; text "D" small
line go linewid heading 45; right; text "C" small
text "feat1" with e at A.w
text "feat2" with e at (last text.e.x, C.y)
line thin chop dotted from last to C
] with nw at 0.5in south of last.sw
arrow color gray from X.s down until even with Y.n
text "git commit" mono with w at last.e
arrow color gray from Y.s down until even with Z.n
text "gs upstack restack" mono with w at last.e
```
</div>
!!! tip
git-spice also offers
$$gs branch restack$$ to restack just the current branch onto its base,
and $$gs stack restack$$ to restack all branches in the current stack.
### Automatic restacking
git-spice provides a handful of convenience commands
for common commit-related tasks
that will automatically restack upstack branches for you:
- $$gs commit create$$ (or $$gs commit create|gs cc$$)
commits changes to the current branch and restacks upstack branches
- $$gs commit amend$$ (or $$gs commit amend|gs ca$$)
amends the last commit and restacks upstack branches
- $$gs commit split$$ (or $$gs commit split|gs csp$$)
interactively splits the last commit into two and restacks upstack branches
- $$gs branch edit$$ (or $$gs branch edit|gs be$$)
opens an interactive rebase of the commits in the current branch
and restacks upstack branches after the rebase completes successfully
For example, the interaction above can be shortened to:
```freeze language="terminal"
{green}${reset} gs branch checkout feat1
{green}${reset} $EDITOR file.txt
{gray}# prepare your changes{reset}
{green}${reset} gs commit create {gray}# or gs cc{reset}
```
## Inserting into the stack
By default, $$gs branch create$$ creates a branch
stacked on top of the current branch.
It does not manipulate the existing stack in any way.
If you're in the middle of the stack, use the `--insert` option
to stack the new branch between the current branch and its upstack branches.
=== "With `--insert`"
<div class="grid" markdown>
```freeze language="terminal"
{green}${reset} gs branch checkout feat1
{green}${reset} gs branch create --insert feat4
```
```pikchr
linerad = 0.05in
X: [
up; text "feat1"; F1: dot invis
line go up 0.15in then go right 0.15in
text "feat2"
line from F1 go up 0.3in then go right 0.15in
text "feat3"
]
Y: [
up; text "feat1"; F1: dot invis
line go up 0.15in then go right 0.15in
text "feat4"; up; F2: dot invis
line from F2 go up 0.15in then go right 0.15in
text "feat2"
line from F2 go up 0.30in then go right 0.15in
text "feat3"
] with sw at 0.5in east of last.se
arrow color gray from X.e go right until even with Y.w
```
</div>
=== "Without `--insert`"
<div class="grid" markdown>
```freeze language="terminal"
{green}${reset} gs branch checkout feat1
{green}${reset} gs branch create feat4
```
```pikchr
linerad = 0.05in
X: [
up; text "feat1"; F1: dot invis
line go up 0.15in then go right 0.15in
text "feat2"
line from F1 go up 0.3in then go right 0.15in
text "feat3"
]
Y: [
up; text "feat1"; F1: dot invis
line go up 0.15in then go right 0.15in
text "feat2"
line from F1 go up 0.3in then go right 0.15in
text "feat3"
line from F1 go up 0.45in then go right 0.15in
text "feat4"
] with sw at 0.5in east of last.se
arrow color gray from X.e go right until even with Y.w
```
</div>
## Splitting the stack
Use $$gs branch split$$ to split a branch with one or more commits
into two or more branches.
The command presents a prompt allowing you to select one or more
*split points* in the branch's history.
After selecting the split points,
it will prompt you for a name for each new branch.
```freeze language="terminal"
{green}${reset} gs branch split
{green}Select commits{reset}:
{yellow}▶ c4fb996{reset} Add an aardvark {gray}(12 minutes ago){reset}
{yellow} f1d2d2f{reset} Refactor the pangolin {gray}(5 minutes ago){reset}
■ {yellow}4186a53{reset} Invent penguins {gray}(1 second ago){reset} [penguin]
Done
```
!!! tip
You can use $$gs commit split$$ and $$gs branch edit$$
to safely and easily manipulate the commits in the branch
before splitting it into multiple branches.
### Splitting non-interactively
```freeze language="terminal" float="right"
{green}${reset} gs branch split {gray}\{reset}
--at HEAD~2:aardvark {gray}\{reset}
--at HEAD~1:pangolin
```
If the interactive prompt is not suitable for your workflow,
you can also split a branch non-interactively
by specifying the split points with the `--at` option
one or more times.
The option takes the form:
```
--at COMMIT:NAME
```
Where `COMMIT` is a reference to a commit in the branch's history,
and `NAME` is the name of the new branch.
## Moving branches around
Use the $$gs upstack onto$$ command to move a branch onto another base branch,
and bring its upstack branches along with it.
<div class="grid" markdown>
```freeze language="terminal"
{green}${reset} gs branch checkout feat2
{green}${reset} gs upstack onto main
```
```pikchr
linerad = 0.05in
lineht = 0.15in
linewid = 0.15in
X: [
text "main"; up;
line go up then go right
text "feat1"; up;
line go up then go right
text "feat2"; up;
line go up then go right
text "feat3"
]
Y: [
text "main"; up; M: dot invis
line go up then go right
text "feat1"; up;
move to M
line go up lineht*2.5 then go right
text "feat2"; up;
line go up then go right
text "feat3"
] with sw at 0.5in east of last.se
color = gray
arrow from X.e go right until even with Y.w
```
</div>
!!! tip
Omit the target branch name
to get a list of branches to choose from.
This is useful when you realize that a branch
does not actually depend on the base branch it is stacked on.
With this command, you can move an entire section of the stack
down to a different base branch, or even to the trunk branch,
reducing the number of changes that need to merge before the target branch.
### Moving only the current branch
In rarer cases, you want to extract the current branch from the stack
and move it to a different base branch,
while leaving the upstack branches where they are.
Use $$gs branch onto$$ for this purpose.
<div class="grid" markdown>
```freeze language="terminal"
{green}${reset} gs branch checkout feat2
{green}${reset} gs branch onto main
```
```pikchr
linerad = 0.05in
lineht = 0.15in
linewid = 0.15in
X: [
text "main"; up;
line go up then go right
text "feat1"; up;
line go up then go right
text "feat2"; up;
line go up then go right
text "feat3"
]
Y: [
text "main"; up; M: dot invis
line go up then go right
text "feat1"; up;
line go up then go right
text "feat3"; up
move to M
line go up lineht*4 then go right
text "feat2"
] with sw at 0.5in east of last.se
color = gray
arrow from X.e go right until even with Y.w
```
</div>
This is most useful when you find that a branch is completely independent
of other changes in the stack.
## Removing branches from the stack
Use $$gs branch delete$$ to remove a branch from the stack
and delete it from the repository.
Branches that are upstack from the deleted branch
will be restacked on top of the deleted branch's original base branch.
<div class="grid" markdown>
```freeze language="terminal"
{green}${reset} gs branch delete feat2
{green}INF{reset} feat2: deleted (was 644a286)
```
```pikchr
linerad = 0.05in
lineht = 0.15in
linewid = 0.15in
X: [
text "main"; up;
line go up then go right
text "feat1"; up;
line go up then go right
text "feat2"; up;
line go up then go right
text "feat3"
]
Y: [
text "main"; up;
line go up then go right
text "feat1"; up;
line go up then go right
text "feat3"
] with sw at 0.5in east of last.se
color = gray
arrow from X.e go right until even with Y.w
```
</div>
!!! tip
Omit the target branch name
to get a list of branches to choose from.
### Removing a branch without deleting it
```freeze language="terminal" float="left"
{green}${reset} gs branch untrack feat2
```
If you want to remove a branch from the stack
but don't want to delete the branch from the repository,
use the $$gs branch untrack$$ command.
+112
View File
@@ -0,0 +1,112 @@
---
title: Concepts
icon: material/lightbulb
description: >-
Terminology used throughout the documentation and commands.
hide: [toc]
---
# Concepts
git-spice introduces a few concepts on top of Git.
This section lists a few that are frequently used in the documentation.
```pikchr float="right"
lineht = 0.3in
linewid = 0.4in
up
Main: box "main" fit ht 0.25in fill 0xffffff
dot; line
box "A" same
dot; line
box "B" same fill 0xd0cee0
dot; line lineht/2
ADot: dot invis
left; line
up; line ht lineht/2
box "C" same fill A.fill
move to ADot
right; line
up; line ht lineht/2
box "D" same
dot; line
box "E" same
$padding = 0.1in
US: box thin behind A fill 0xb0fbfd \
with ne at E.ne+($padding, $padding) \
wid E.e.x-C.w.x+($padding*2) \
ht E.n.y-B.y+$padding/2
text "Upstack" with nw at last.nw
DS: box thin behind A fill 0xc49bf9 \
with nw at (last box.w.x, B.y-$padding/2) \
wid US.wid \
ht B.y-A.s.y+$padding/2
text "Downstack" with nw at last.nw-(0,$padding)
S: box thin behind US fill 0xccccfb \
with nw at US.nw+(-$padding, $padding*2) \
wid US.wid+$padding*2 \
ht US.n.y-DS.s.y+$padding*3
text "Stack" with nw at last.nw
text "Current" "branch" with e at (S.w.x-$padding, B.y)
arrow chop thin dashed from last to B
text "Trunk" with w at Main.e
box "F" same as A \
with nw at $padding east of (S.e, B.ne)
line from 1/2 way between A.n and B.s \
go right until even with F \
then to F.s
text "Sibling" with s at F.n
```
**Branch**
: A regular Git branch.
Branches can have a *base*: the branch they were created from.
The branch currently checked out is called the *current branch*.
In the diagram, `B` is the current branch, and `A` is its base.
**Trunk**
: The default branch of a repository.
This is "main" or "master" in most repositories.
Trunk is the only branch that does not have a base branch.
**Stack**
: A stack is a collection of branches stacked on top of each other
in a way that each branch except the trunk has a base branch.
In the diagram, `A` is stacked on top of trunk,
`B` is stacked on top of `A`, and so on.
A branch can have multiple branches stacked on top of it.
**Downstack**
: Downstack refers to the branches below the current branch,
all the way to, but not including, the trunk branch.
**Upstack**
: Upstack refers to the branches stacked on top of the current branch,
those branches' upstacks, and so on until no more branches remain.
If a branch has multiple branches stacked on top of it,
they are both upstack from it.
**Sibling**
: A sibling to a branch is a branch that shares the same base branch.
In the diagram, `F` is a sibling to `B`, and `C` is a sibling to `D`.
**Restacking**
: Restacking is the process of moving a rebasing the contents of a branch
on top of its base branch, which it may have diverged from.
This is done to keep the branch up-to-date with its base branch,
and maintain a linear history.
+12
View File
@@ -0,0 +1,12 @@
---
icon: material/compass
template: templates/index_page.html
title: User Guide
hide: [toc]
description: >-
Details on how to use git-spice to manage your repositories.
---
The User Guide covers the features of git-spice in more detail.
For a list of all available commands, see the [Command Reference](../cli/index.md).
+80
View File
@@ -0,0 +1,80 @@
---
title: Internals
icon: octicons/codescan-16
description: >-
A look at the inner workings of git-spice for the curious.
---
# git-spice internals
!!! warning
This page covers internal details about the implementation of git-spice.
Most users do not need to read this.
It is presented here for the curious,
and in the interest of transparency.
Do not rely on internal details to remain stable.
These may change at any time.
## Local storage
git-spice stores information about your repository and branches
in a local Git ref named: `refs/spice/data`.
Information is stored in JSON files with roughly the following structure:
```tree
repo # repository-level information
templates # cached GitHub PR templates
rebase-continue # information about ongoing operations
branches # branch tracking information
feat1
feat2
...
prepared # ephemeral per-branch PR form information
feat1
feat2
...
```
git-spice operations that manipulate this information
will usually include what prompted the change.
You can explore this information by running
the following command in a repository using git-spice:
```bash
git log --patch refs/spice/data
```
## Secret storage
git-spice stores your GitHub authentication token
in a system-specific secure storage.
On macOS, this is the system Keychain.
On Linux, it uses the [Secret Service](https://specifications.freedesktop.org/secret-service/latest/),
which is typically provided by [GNOME Keyring](https://specifications.freedesktop.org/secret-service/latest/).
<!-- TODO (if we enable Windows): On Windows, it uses the Windows Credential Manager APIs. -->
Credit goes to [zalando/go-keyring](https://github.com/zalando/go-keyring)
for providing a cross-platform API for this purpose.
## Git interactions
git-spice does not use a third-party Git implementation.
All operations are performed directly against the Git CLI,
often relying on Git's [plumbing commands](https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain).
!!! question "Why?"
Most third-party Git implementations trail behind in feature parity.
For example, many third-party implementations can misbehave when you make
use of advanced features like
[git worktree](https://git-scm.com/docs/git-worktree),
[git sparse-checkout](https://git-scm.com/docs/git-sparse-checkout),
or [sparse indexes](https://git-scm.com/docs/sparse-index).
By relying on the highly scriptable and machine-consumable Git plumbing
we don't have to deal with those issues.
+94
View File
@@ -0,0 +1,94 @@
---
icon: octicons/stop-16
title: Limitations
description: >-
Usage constraints and limitations when using git-spice
to interact with GitHub.
---
git-spice's handling of GitHub Pull Requests is limited
by what's possible with GitHub and its handling of Git commits.
Some limitations imposed on git-spice are listed below.
## Write access required
When a branch `F` is stacked on another branch `B`,
and you want to create stacked Pull Requests from them,
the PR for `F` will be created against `B`.
To do this, git-spice needs to push both branches to the same repository.
Therefore, to use git-spice to stack PRs,
you need write access to the repository:
specifically the ability to push new branches.
## Squash-merges restack the upstack
If a Pull Request is squash-merged into the trunk branch,
all commits in that PR are replaced with a single commit with a different hash.
The branches upstack from that PR are not aware of this new commit,
still referring to the old, unsquashed history of the branch.
GitHub does not yet know to reconcile this new commit with the upstack branches,
even though the contents are the same.
```pikchr
linewid = 0.25in
X: [
right
text "A" small; line; text "B" small
line go linewid heading 45; right
text "C" small; line; text "D" small
line go linewid heading 45; right
text "E" small
text "main" with e at A.w
text "feat1" with e at (last.e.x, C.y)
line thin chop dotted from last to C
text "feat2" with e at (last text.e.x, E.y)
line thin chop dotted from last to E
]
Y: [
right
text "A" small; line; text "B" small; line; text "CD" small
move to B.ne
line go linewid heading 45; right
text "C" small; line; text "D" small
line go linewid heading 45; right
text "E" small
text "main" with e at A.w
text "feat2" with e at (last text.e.x, E.y)
line thin chop dotted from last to E
] with n at 0.5in below last.s
Z: [
right
text "A" small; line; text "B" small; line; text "CD" small
line go linewid*2 heading 45; right
text "E" small
text "main" with e at A.w
text "feat2" with e at (last text.e.x, E.y)
line thin chop dotted from last to E
] with n at 0.5in below last.s
arrow " squash-merge" ljust from X.s to Y.n
arrow " restack" ljust from Y.s to Z.n
```
As a result of this, when a branch is squash-merged into the trunk branch,
branches upstack from it need to be restacked, and all their PRs updated.
<!-- TODO: can be alleviated somewhat if we implement
https://github.com/abhinav/git-spice/issues/65 -->
## Base branch change may dismiss approvals
Some GitHub repositories are configured to dismiss prior approvals of PRs
when the base branch of that PR is changed.
There is no workaround to this except to reconfigure the repository
as this setting is fundamentally incompatible with a PR stacking workflow.
+88
View File
@@ -0,0 +1,88 @@
---
title: PR stacks
icon: octicons/git-pull-request-16
description: >-
Create and update stacked pull requests from a stack of branches.
---
# Pull Request Stacks
## Submitting pull requests
When your local changes are ready,
use the following commands to submit your changes upstream:
- $$gs branch submit$$ (or $$gs branch submit|gs bs$$)
submits the current branch
- $$gs downstack submit$$ (or $$gs downstack submit|gs dss$$)
submits the current branch and all branches below it
- $$gs upstack submit$$ (or $$gs upstack submit|gs uss$$)
submits the current branch and all branches above it
- $$gs stack submit$$ (or $$gs stack submit|gs ss$$)
submits all branches in the stack
Branch submission is an idempotent operation:
pull requests will be created for branches that don't already have them,
and updated for branches that do.
For new pull requests, these commands will prompt you for PR information.
For example:
```freeze language="ansi"
--8<-- "captures/branch-submit.txt"
```
!!! important
Be aware that for stacks with multiple branches,
you must have write access to the repository
so that you can push branches to it.
See [Limitations](limits.md) for more information.
### Non-interactive submission
Use the `--fill` flag provided by all the above commands
to fill in the PR information from commit messages
and skip the interactive prompts.
```freeze language="terminal"
{green}${reset} gs stack submit --fill
{green}INF{reset} Created #123: https://github.com/abhinav/git-spice/pull/123
{green}INF{reset} Created #125: https://github.com/abhinav/git-spice/pull/125
```
Additionally, with $$gs branch submit$$,
you may also specify title and body directly.
```freeze language="terminal"
{green}${reset} gs branch submit {gray}\{reset}
--title {blue}"Fix a bug"{reset} {gray}\{reset}
--body {blue}"This fixes a very bad bug."{reset}
{green}INF{reset} Created #123: https://github.com/abhinav/git-spice/pull/123
```
!!! info "Setting draft status non-interactively"
Pull requests may be marked as draft or ready for review
non-interactively with the `--draft` and `--no-draft` flags.
By default, the submit commands will leave
the draft state of existing PRs unchanged.
If the `--draft` or `--no-draft` flags are provided,
the draft state of all PRs will be set accordingly.
## Syncing with upstream
To sync with the upstream repository,
use $$gs repo sync$$ (or $$gs repo sync|gs rs$$).
```freeze language="terminal" float="right"
{green}${reset} gs repo sync
{green}INF{reset} main: pulled 3 new commit(s)
{green}INF{reset} feat1: #123 was merged
{green}INF{reset} feat1: deleted (was 9f1c9af)
```
This will update the trunk branch (e.g. `main`)
with the latest changes from the upstream repository,
and delete any local branches whose PRs have been merged.
+67
View File
@@ -0,0 +1,67 @@
---
icon: material/tooltip-check
title: How-to
description: >-
Common tasks and how to perform them with git-spice.
---
# How-to...
## Manage an existing stack with git-spice
If you have an existing, manually managed stack of branches,
you can import it into git-spice with repeated use of $$gs branch track$$.
**Steps:**
1. Check out the base branch and initialize git-spice.
```bash
git checkout main
gs repo init
```
2. Check out the first branch in the stack and track it.
```bash
git checkout feat1
gs branch track
```
3. Repeat the previous step for each branch in the stack.
```bash
git checkout feat2
gs branch track
# ... repeat until done ...
```
4. You may now use git-spice to manage the stack as usual.
## Import an existing Pull Request
git-spice can recognize and manage GitHub Pull Requests
that were not created using git-spice.
**Steps:**
1. Check the PR out locally.
For example, if you're using the GitHub CLI:
```bash
gh pr checkout 123
```
2. Track the branch with git-spice.
```bash
gs branch track
```
3. Attempt to re-submit the branch.
git-spice will automatically detect the existing open PR for it,
and associate the branch with that PR.
```bash
gs branch submit
```
+112
View File
@@ -0,0 +1,112 @@
---
icon: material/home
hide:
- toc
description: >-
git-spice is a tool for stacking Git branches.
It helps you manage and navigate a stack of branches
that build on top of each other.
---
# git-spice
![](img/logo.png){ align=right width=250px }
git-spice is a tool for stacking Git branches.
It lets you manage and navigate stacks of branches,
conveniently modify and rebase them,
and create GitHub Pull Requests from them.
<div class="grid" markdown>
```freeze language="shell"
# Create a branch
$ gs branch create
# Restack a branck
$ gs branch restack
# Restack all branches
$ gs stack restack
```
```freeze language="shell"
# Submit a PR
$ gs branch submit
# Submit all PRs
$ gs stack submit
# Sync with trunk
$ gs repo sync
```
</div>
!!! question "What is stacking?"
Stacking refers to the practice of creating branches or pull requests
that build on top of each other.
It allows chaining interdependent changes together,
while still keeping the individual changes small and focused.
See also [:material-frequently-asked-questions: FAQ > What is stacking?](faq.md#what-is-stacking)
[:material-run: Get started](start/index.md){ .md-button }
[:material-compass: Learn more](guide/index.md){ .md-button }
## Features
<div class="grid cards" markdown>
- [:octicons-git-branch-16:{ .lg .middle } __Branch management__](guide/branch.md)
---
Create, edit, and navigate stacks of branches with ease.
With git-spice's branch management commands,
you can keep your stack in sync with the trunk branch,
automatically rebase dependent branches, and more.
- [:octicons-git-pull-request-16:{ .lg .middle } __Pull Request Management__](guide/pr.md)
---
Create GitHub Pull Requests from your stack with a single command.
git-spice can create
[a PR for the current branch](cli/index.md#gs-branch-submit),
[PRs for the entire stack](cli/index.md#gs-stack-submit), or
[parts of](cli/index.md#gs-upstack-submit)
[the stack](cli/index.md#gs-downstack-submit).
If a branch already has a PR, git-spice will update it.
If a PR is merged,
git-spice will automatically restack branches that depend on it.
- [:material-stairs:{ .lg .middle } __Incremental improvements__](start/stack.md)
---
git-spice does not need to be adopted all at once.
It does not expect you to flip your entire workflow upside down.
Incorporate it into your workflow at your own pace,
one feature at a time.
- [:material-cloud-off-outline:{ .lg .middle } __Offline-first__](guide/internals.md)
---
git-spice operates entirely locally.
It talks directly to Git, and when you ask for it, to GitHub.
All state is stored locally in your Git repository.
A network connection is not required, except when pushing or pulling.
- [:material-lightbulb-on:{ .lg .middle } __Intuitive shorthands__](cli/shorthand.md)
---
Most commands in git-spice have easy-to-remember shorthands.
For example, $$gs branch create$$ can be shortened to $$gs branch create|gs bc$$.
Explore the list of shorthands with `--help` or at [Shorthands](cli/shorthand.md).
We recommend adopting these incrementally.
</div>
+193
View File
@@ -0,0 +1,193 @@
---
icon: material/lock
description: >-
Authenticate with GitHub to push and pull changes.
---
# Authentication
git-spice is offline-first.
It does not require authentication for local stacking operations.
However, once you want to push or pull changes to/from a GitHub repository,
you will need to authenticate with GitHub.
This page covers the authentication options for git-spice.
## Logging in
To authenticate with GitHub, run:
```sh
gs auth login github
```
This will present you with a list of authentication methods.
See [Authentication methods](#authentication-methods) for details
on what to expect from each method,
or skip on to [Pick an authentication method](#picking-an-authentication-method).
## Authentication methods
git-spice provides several ways to authenticate with GitHub.
### OAuth
With OAuth authentication, you will take the following steps:
1. Authenticate yourself on github.com in your browser.
2. Authorize git-spice to act on your behalf on the **current device only**.
```freeze language="terminal"
{green}${reset} gs auth login
Select an authentication method: {red}OAuth{reset}
{gray}1.{reset} Visit {cyan}https://github.com/login/device{reset}
{gray}2.{reset} Enter code: ABCD-1234
The code expires in a few minutes.
It will take a few seconds to verify after you enter it.
```
Two options are available for OAuth:
- **OAuth**: grants access to all repositories, public and private.
- **OAuth: Public repositories only**:
grants access to public repositories only.
For more granular control, use [GitHub App](#github-app) authentication.
!!! note
For private repositories owned by organizations,
you will need a member with administrative access to the repository
to allow installation of the git-spice OAuth App.
If that is not an option,
use a [Personal Access Token](#personal-access-token).
### GitHub App
With GitHub App authentication, you will take the following steps:
1. Authenticate yourself on github.com in your browser.
2. Authorize git-spice to act on your behalf on the **current device only**.
3. Install the [git-spice GitHub App](https://github.com/apps/git-spice)
on the repositories you want to use git-spice with.
```freeze language="terminal"
{green}${reset} gs auth login
Select an authentication method: {red}GitHub App{reset}
{gray}1.{reset} Visit {cyan}https://github.com/login/device{reset}
{gray}2.{reset} Enter code: ABCD-1234
The code expires in a few minutes.
It will take a few seconds to verify after you enter it.
```
**Important**: Authentication alone does not grant any access.
You **must** install the GitHub App to access repositories with git-spice.
!!! note
For private repositories owned by organizations,
you will need a member with administrative access to the repository
to allow installation of the git-spice GitHub App.
If that is not an option,
use a [Personal Access Token](#personal-access-token).
### Personal Access Token
To use a Personal Access Token with git-spice,
you will generate a Personal Access Token on GitHub
and enter it in the prompt.
```freeze language="terminal"
{green}${reset} gs auth login
Select an authentication method: {red}Personal Access Token{reset}
{green}Enter Personal Access Token{reset}:
```
The token may be a classic token or a fine-grained token.
=== "Classic token"
With classic tokens, you can grant access to all repositories,
or all public repositories only.
These tokens have the ability to never expire.
To use a classic token:
1. Go to <https://github.com/settings/tokens/new>.
This may ask you to re-authenticate.
2. In the token creation form:
- enter a descriptive note for the token
- pick an expiration window, or select "No expiration"
- select `repo` scope for full access to all repositories,
or `public_repo` for access to public repositories only
3. Click "Generate token" and copy the token.
=== "Fine-grained token"
With fine-grained tokens, you have more granular control over
repositories that you grant access to.
These token must always have an expiration date.
To use a fine-grained token:
1. Go to <https://github.com/settings/personal-access-tokens/new>.
This may ask you to re-authenticate.
2. In the token creation form:
- pick a descriptive note for the token
- pick an expiration window
- in the *Repository access* section, select the repositories
you want to use git-spice with
- in the *Repository permissions* section,
grant **Read and write** access to **Pull requests** and **Contents**
3. Click "Generate token" and copy the token.
After you have a token, enter it into the prompt.
### GitHub CLI
If you have the [GitHub CLI](https://cli.github.com/) installed and authenticated,
you can select this as the authentication method.
This requires no additional steps.
We'll request a token from GitHub CLI as needed.
### GITHUB_TOKEN
If you have a `GITHUB_TOKEN` environment variable set,
that takes precedence over all other authentication methods.
The $$gs auth login$$ operation will fail if you use this method.
## Picking an authentication method
[OAuth](#oauth) and [GitHub App](#github-app) authentication are best if you
have the permissions needed to install OAuth/GitHub Apps
on all repositories that you want to use git-spice with.
The two are equivalent in terms of user experience.
Use GitHub App authentication if you don't want to give git-spice access
to all your repositories.
[GitHub CLI](#github-cli) is the most convenient method if you already have
the GitHub CLI installed and authenticated.
It loses some security benefits of the other methods,
as it re-uses the token from the GitHub CLI.
You lose the ability to revoke the git-spice token
without revoking the GitHub CLI token.
[Personal Access Token](#personal-access-token) is flexible and secure.
It may be used even with repositories where you don't have permission to
install OAuth/GitHub Apps.
However, it requires manual token management, making it less convenient.
[GITHUB_TOKEN](#github_token) is the least convenient and the least secure method.
It is intended only for CI/CD environments where you have no other choice.
!!! question "Where is my token stored?"
The token is stored in a system-specific secure storage.
See [Secret storage](../guide/internals.md#secret-storage) for details.
+49
View File
@@ -0,0 +1,49 @@
---
icon: material/console-line
description: >-
Set up shell completion for Bash, Zsh, and Fish.
---
# Shell completion
git-spice supports completion for Bash, Zsh, and Fish.
To set up completion, follow the instructions below.
=== "Bash"
Add the following line to your `.bashrc` or `.bash_profile`:
```bash
eval "$(gs shell completion bash)"
```
=== "Zsh"
Add the following line to your `.zshrc` or `.zprofile`:
```zsh
eval "$(gs shell completion zsh)"
```
=== "Fish"
Add the following line to your `config.fish`:
```fish
eval "$(gs shell completion fish)"
```
Then restart the shell or create a new shell session.
??? info "Debugging shell completion"
If shell completion misbehaves and you want to file a bug report,
first retry the completion with `COMP_DEBUG=1` set.
```freeze language="terminal"
{green}${reset} {blue}export{reset} COMP_DEBUG=1
{green}${reset} gs branch onto {gray}# <TAB>{reset}
```
This will print debugging information about the completion process.
Include this output in your bug report.
+11
View File
@@ -0,0 +1,11 @@
---
icon: octicons/gear-16
template: templates/index_page.html
hide: [toc]
description: >-
Configuring git-spice for your workflow.
---
# Setting up
This section guides you through various **optional** setup steps for git-spice.
+16
View File
@@ -0,0 +1,16 @@
---
icon: material/run
hide: [toc]
description: >-
Get started using git-spice, a tool for stacking Git branches.
template: templates/listing.html
listing:
- install.md
- stack.md
- submit.md
---
# Get started
Start here if you're just exploring
and would like to see what git-spice can do.
+51
View File
@@ -0,0 +1,51 @@
---
icon: material/download
description: >-
Install git-spice on your system and start using it.
next_page: stack.md
---
# Installation
git-spice may be installed with a pre-built binary
or built from source.
## Pre-built binary
To install a **pre-built binary**, take the following steps:
=== "Homebrew/Linuxbrew"
```bash
brew install abhinav/tap/git-spice
```
=== "AUR (ArchLinux)"
```bash
git clone https://aur.archlinux.org/git-spice-bin.git
cd git-spice-bin
makepkg -si
# Or, with an AUR helper like yay:
yay -S git-spice-bin
```
Pre-built binaries for other platforms can be found on the
[GitHub Releases page](https://github.com/abhinav/git-spice/releases).
## Build from source
To **build from source**, follow these steps:
1. Install the [Go compiler](https://go.dev/dl).
2. Run the following command:
```bash
go install go.abhg.dev/gs@latest
```
## Next steps
- [ ] [Create your first stack](stack.md)
+156
View File
@@ -0,0 +1,156 @@
---
title: Your first stack
icon: material/baby-carriage
description: >-
Create your first stack of branches with git-spice.
Modify and restack them with ease.
prev_page: install.md
next_page: submit.md
---
With git-spice installed, start experimenting with stacking.
## Initialize git-spice
1. Start by creating an new Git repository to play inside.
```bash
mkdir repo
cd repo
git init
git commit --allow-empty -m "Initial commit"
```
2. Initialize git-spice in the repository.
This will set up the internal storage for git-spice in your repository.
```bash
gs repo init
```
!!! info
This step isn't absolutely required.
git-spice will initialize itself automatically when needed.
## Track a branch
Next, stack a branch on top of `main`.
1. Create a new branch and make some changes:
```bash
git checkout -b feat1
echo "Hello, world!" > hello.txt
git add hello.txt
git commit -m "Add hello.txt"
```
2. Add the branch to git-spice.
```bash
gs branch track
```
This results in a single branch stacked on top of `main`.
```pikchr center="false"
linerad = 0.05in
text "main"
up; line go up 0.15in then go right 0.15in
text "feat1"
```
The above operations are frequently done together.
git-spice provides a command to do all of them in one go: `gs branch create`.
## Use gs branch create
Stack another branch on top of `feat1` with $$gs branch create$$.
1. Check out feat1 and prepare another change:
```bash
echo "This project is cool!" > README.md
git add README.md
```
2. Create a new branch, commit the staged changes,
and add the branch to git-spice.
```bash
gs branch create feat2
```
!!! tip
Use `gs branch create -a` to automatically stage changes to tracked
files.
This behaves similarly to `git commit -a`.
This results in a stack that looks like this:
```pikchr center="false"
linerad = 0.05in
text "main"
up; line go up 0.15in then go right 0.15in
text "feat1"
up; line go up 0.15in then go right 0.15in
text "feat2"
```
## Modify mid-stack
Time to modify a branch in the middle of the stack.
1. Check out `feat1`:
```bash
gs down
```
!!! info
The $$gs down$$ command moves us "down" the stack,
as opposed to $$gs up$$ which moves us "up".
2. Make a change to `hello.txt` and commit it:
```bash
echo "How are you?" >> hello.txt
git add hello.txt
git commit -m "Add a question to hello.txt"
```
3. **Restack** branches that are out of sync with the current branch.
```bash
gs upstack restack
```
!!! tip
You can use $$gs commit create$$ to combine the commit and restack steps.
4. Go back to `feat2` and verify:
```bash
gs up
cat hello.txt
```
## Summary
**This section covered:**
- [x] $$gs branch create$$ is a shortcut for creating a branch,
committing to it, and tracking it with git-spice.
- [x] $$gs up$$ and $$gs down$$ provide relative navigation through the stack.
- [x] $$gs upstack restack$$ updates branches
that are out of sync with the current branch.
## Next steps
- [ ] Use $$gs commit create$$ to combine the commit and restack steps
- [ ] Explore different flags of $$gs branch create$$
- [ ] [Create your first stacked pull requests](submit.md)
+192
View File
@@ -0,0 +1,192 @@
---
title: Your first stacked PRs
icon: material/slide
description: >-
Turn a stack of branches into Pull Requests.
Update them all in one go.
prev_page: stack.md
next_page: ../guide/index.md
---
# Your first stacked Pull Requests
This page will walk you through creating and updating
GitHub Pull Requests with git-spice.
## Prerequisites
- [x] [Create a stack of branches](stack.md).
For this tutorial, we'll assume the following stack.
Go back to the previous page if you need to create it.
```pikchr center="false"
linerad = 0.05in
text "main"
up; line go up 0.15in then go right 0.15in
text "feat1"
up; line go up 0.15in then go right 0.15in
text "feat2"
```
- [x] Set up a GitHub repository.
??? info "Optional: Create an experimental repository"
If you're following along with the tutorial,
you may want to create a new repository on GitHub
to experiment with instead of using a real project.
To do this, if you have the GitHub CLI installed,
run the following inside your experimental repository:
```bash
gh repo create gs-playground \
--public \
--source=$(pwd) \
--push
```
If you don't have the GitHub CLI installed,
go to <https://github.com/new> and follow the instructions there.
## Create a Pull Request
1. Check out `feat1`.
=== "git"
```bash
git checkout feat1
```
=== "gs"
```bash
gs branch checkout feat1
```
=== "gs shorthand"
```bash
gs bco feat1
```
2. Submit a PR.
=== "gs"
```bash
gs branch submit
```
=== "gs shorthand"
```bash
gs bs
```
3. Follow the prompts on-screen to create the PR.
For example:
```freeze language="ansi"
--8<-- "captures/branch-submit.txt"
```
## Stack on top
1. Check out `feat2`.
```bash
gs up
```
2. Submit a PR.
```bash
gs branch submit
```
3. Follow the prompts on-screen to create the PR.
The new PR will now be stacked on top of the previous one:
GitHub will show `feat1` as the base branch for the PR.
## Modify mid-stack
Modify the `feat1` branch and update the PR.
1. Check out `feat1`.
```bash
gs down
```
2. Make some changes.
=== "gs"
```bash
echo "Not bad, how about you?" >> hello.txt
git add hello.txt
gs commit create -m "follow up"
```
=== "gs shorthand"
```bash
echo "Not bad, how about you?" >> hello.txt
git add hello.txt
gs cc -m "follow up"
```
!!! info
We use the $$gs commit create$$ command here.
This will commit to the current branch,
and rebase the upstack branches on top of the new commit.
3. Update all PRs in the stack.
```bash
gs stack submit
```
This will push to both PRs in the stack.
If one of the branches was not submitted yet,
it will prompt you to create a PR for it.
## Merge a PR
1. Open up the Pull Request for `feat1` on GitHub
and merge it into `main`.
2. Run the following command to sync the stack with the trunk:
```bash
gs repo sync
```
This will delete `feat1` locally,
and rebase `feat2` on top of `main`.
3. Submit the PR for `feat2` to update the pull request
if necessary.
```bash
gs branch submit
```
## Summary
**This section covered:**
- [x] $$gs branch submit$$ creates or updates a PR for the current branch.
- [x] $$gs stack submit$$ creates or updates PRs for the entire stack.
- [x] $$gs repo sync$$ syncs the stack with the trunk branch,
deletes merged branches, and rebases the remaining branches.
## Next steps
- [ ] Explore other submit commands:
$$gs upstack submit$$, $$gs downstack submit$$
- [ ] Browse the [User Guide](../guide/index.md) to learn more
+8
View File
@@ -0,0 +1,8 @@
//go:build tools
package main
import (
_ "github.com/charmbracelet/freeze"
_ "go.abhg.dev/gs"
)
+114 -61
View File
@@ -1,8 +1,8 @@
//go:build dumpmd
package main
import (
"bufio"
"errors"
"fmt"
"io"
"os"
@@ -12,52 +12,107 @@ import (
"github.com/alecthomas/kong"
)
// dumpMarkdownCmd is a hidden flag that makes the command
// dupm a Markdown reference to stdout and exit.
// dumpMarkdownCmd is a hidden commnad that dumps
// a Markdown reference to stdout and exit.
type dumpMarkdownCmd struct {
Out string `help:"Output file" type:"path"`
w io.Writer
Ref string `name:"ref" help:"Output file for command reference."`
Shorthands string `name:"shorthands" help:"Output file for shorthands table."`
}
func (cmd *dumpMarkdownCmd) Run(app *kong.Kong) (err error) {
var w io.Writer = os.Stdout
if cmd.Out != "" && cmd.Out != "-" {
f, err := os.Create(cmd.Out)
if err != nil {
return fmt.Errorf("create output file: %w", err)
}
defer func() {
err = errors.Join(err, f.Close())
}()
w = f
func (cmd *dumpMarkdownCmd) Run(app *kong.Kong, shorts shorthands) (err error) {
ref, err := os.Create(cmd.Ref)
if err != nil {
return err
}
defer func() { _ = ref.Close() }()
buf := bufio.NewWriter(w)
defer func() {
err = errors.Join(err, buf.Flush())
}()
d := cliDumper{w: ref}
d.dump(app.Model)
cmd.w = buf
cmd.dump(app.Model)
if cmd.Shorthands != "" {
f, err := os.Create(cmd.Shorthands)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
dumpShorthands(f, shorts)
}
return nil
}
func (cmd *dumpMarkdownCmd) println(args ...interface{}) {
fmt.Fprintln(cmd.w, args...)
func dumpShorthands(w io.Writer, shorts shorthands) {
keys := make([]string, 0, len(shorts))
for key := range shorts {
keys = append(keys, key)
}
slices.Sort(keys)
var t table
t.appendHeaders("Shorthand", "Long form")
for _, key := range keys {
cmd := cmdFullName(shorts[key].Command)
link := fmt.Sprintf("[%v](/cli/index.md#%v)", cmd, strings.ReplaceAll(cmd, " ", "-"))
t.addRow("gs "+key, link)
}
t.dump(w)
}
func (cmd *dumpMarkdownCmd) print(args ...interface{}) {
fmt.Fprint(cmd.w, args...)
type table struct {
headers []string
rows [][]string
headerColumn bool
}
func (cmd *dumpMarkdownCmd) printf(format string, args ...interface{}) {
fmt.Fprintf(cmd.w, format, args...)
func (t *table) appendHeaders(headers ...string) {
t.headers = append(t.headers, headers...)
}
func (cmd dumpMarkdownCmd) dump(app *kong.Application) {
cmd.header(1, app.Name+" command reference")
func (t *table) addRow(row ...string) {
t.rows = append(t.rows, row)
}
func (t *table) dump(w io.Writer) {
fmt.Fprint(w, "|")
for _, h := range t.headers {
fmt.Fprintf(w, " **%s** |", h)
}
fmt.Fprintln(w)
fmt.Fprintln(w, "|", strings.Repeat(" --- |", len(t.headers)))
for _, row := range t.rows {
if t.headerColumn {
row[0] = "**" + row[0] + "**"
}
fmt.Fprintln(w, "|", strings.Join(row, " | "), "|")
}
}
type cliDumper struct {
w io.Writer
}
func (cmd *cliDumper) dump(app *kong.Application) {
// H1 is filled by the Markdown file that includes the result.
var groupKeys, groupTitles []string
cmdByGroup := make(map[string][]*kong.Node)
for _, subcmd := range app.Leaves(true) {
var key, title string
if grp := subcmd.ClosestGroup(); grp != nil {
key = grp.Key
title = grp.Title
}
if _, ok := cmdByGroup[key]; !ok {
groupKeys = append(groupKeys, key)
groupTitles = append(groupTitles, title)
}
cmdByGroup[key] = append(cmdByGroup[key], subcmd)
}
// TODO: separate "inspect" and "dump" steps
cmd.println("```")
cmd.println("gs" + app.Summary())
cmd.println("```")
@@ -79,23 +134,6 @@ func (cmd dumpMarkdownCmd) dump(app *kong.Application) {
}
cmd.println()
var groupKeys, groupTitles []string
cmdByGroup := make(map[string][]*kong.Node)
for _, subcmd := range app.Leaves(true) {
var key, title string
if grp := subcmd.ClosestGroup(); grp != nil {
key = grp.Key
title = grp.Title
}
if _, ok := cmdByGroup[key]; !ok {
groupKeys = append(groupKeys, key)
groupTitles = append(groupTitles, title)
}
cmdByGroup[key] = append(cmdByGroup[key], subcmd)
}
for i, key := range groupKeys {
lvl := 2
title := groupTitles[i]
@@ -110,19 +148,12 @@ func (cmd dumpMarkdownCmd) dump(app *kong.Application) {
}
}
func (cmd dumpMarkdownCmd) dumpCommand(node *kong.Node, level int) {
func (cmd cliDumper) dumpCommand(node *kong.Node, level int) {
if node.Hidden {
return
}
var parts []string
for n := node; n != nil && n.Type == kong.CommandNode; n = n.Parent {
parts = append(parts, n.Name)
}
parts = append(parts, "gs")
slices.Reverse(parts)
cmd.header(level, strings.Join(parts, " "))
cmd.header(level, cmdFullName(node))
cmd.println("```")
cmd.println("gs " + node.Summary())
cmd.println("```")
@@ -159,11 +190,11 @@ func (cmd dumpMarkdownCmd) dumpCommand(node *kong.Node, level int) {
}
}
func (cmd dumpMarkdownCmd) dumpArg(arg *kong.Positional) {
func (cmd cliDumper) dumpArg(arg *kong.Positional) {
cmd.printf("* `%s`: %s\n", arg.Name, arg.Help)
}
func (cmd dumpMarkdownCmd) dumpFlag(flag *kong.Flag) {
func (cmd cliDumper) dumpFlag(flag *kong.Flag) {
if flag.Hidden {
return
}
@@ -191,6 +222,28 @@ func (cmd dumpMarkdownCmd) dumpFlag(flag *kong.Flag) {
cmd.printf("`: %s\n", flag.Help)
}
func (cmd dumpMarkdownCmd) header(level int, text string) {
func (cmd cliDumper) header(level int, text string) {
cmd.printf("%s %s\n\n", strings.Repeat("#", level), text)
}
func (cmd cliDumper) println(args ...interface{}) {
fmt.Fprintln(cmd.w, args...)
}
func (cmd cliDumper) print(args ...interface{}) {
fmt.Fprint(cmd.w, args...)
}
func (cmd cliDumper) printf(format string, args ...interface{}) {
fmt.Fprintf(cmd.w, format, args...)
}
func cmdFullName(node *kong.Node) string {
var parts []string
for n := node; n != nil && n.Type == kong.CommandNode; n = n.Parent {
parts = append(parts, n.Name)
}
parts = append(parts, "gs")
slices.Reverse(parts)
return strings.Join(parts, " ")
}
+8
View File
@@ -0,0 +1,8 @@
//go:build !dumpmd
package main
// When building without the "dumpmd" build tag,
// the dumpmd does not do anything.
type dumpMarkdownCmd struct{}
+22 -13
View File
@@ -124,9 +124,7 @@ func main() {
}
}
shorthands := map[string][]string{
"can": {"commit", "amend", "--no-edit"},
}
shorthands := make(shorthands)
// For each leaf subcommand, define a combined shorthand alias.
// For example, if the command was "branch (b) create (c)",
@@ -150,27 +148,31 @@ func main() {
}
slices.Reverse(fragments)
shorthand := strings.Join(fragments, "")
if other, ok := shorthands[shorthand]; ok {
panic(fmt.Sprintf("shorthand %q for %v is already in use by %v", shorthand, n.Path(), other))
short := strings.Join(fragments, "")
if other, ok := shorthands[short]; ok {
panic(fmt.Sprintf("shorthand %q for %v is already in use by %v", short, n.Path(), other.Expanded))
}
// TODO: check if shorthand conflicts with any other aliases.
shorthands[shorthand] = fragments
shorthands[short] = shorthand{
Expanded: fragments,
Command: n,
}
}
args := os.Args[1:]
if len(args) > 0 {
if path, ok := shorthands[args[0]]; ok {
args = slices.Replace(args, 0, 1, path...)
if short, ok := shorthands[args[0]]; ok {
// TODO: Replace first non-flag argument instead.
args = slices.Replace(args, 0, 1, short.Expanded...)
}
}
komplete.Run(parser,
komplete.WithTransformCompleted(func(args []string) []string {
if len(args) > 0 {
if path, ok := shorthands[args[0]]; ok {
args = slices.Replace(args, 0, 1, path...)
if short, ok := shorthands[args[0]]; ok {
args = slices.Replace(args, 0, 1, short.Expanded...)
}
}
return args
@@ -187,11 +189,18 @@ func main() {
logger.Fatalf("gs: %v", err)
}
if err := kctx.Run(); err != nil {
if err := kctx.Run(shorthands); err != nil {
logger.Fatalf("gs: %v", err)
}
}
type shorthand struct {
Expanded []string
Command *kong.Node
}
type shorthands map[string]shorthand
type globalOptions struct {
// Flags that are not accessed directly by command implementations:
@@ -231,7 +240,7 @@ type mainCmd struct {
Trunk trunkCmd `cmd:"" group:"Navigation" help:"Move to the trunk branch"`
// Hidden commands:
DumpMD dumpMarkdownCmd `name:"dump-md" hidden:"" cmd:"" help:"Dump a Markdown reference to stdout and quit"`
DumpMD dumpMarkdownCmd `name:"dumpmd" hidden:"" cmd:"" help:"Dump a Markdown reference to stdout and quit"`
}
func (cmd *mainCmd) AfterApply(kctx *kong.Context, logger *log.Logger) error {