diff --git a/.gitea/workflows/verify.yml b/.gitea/workflows/verify.yml
new file mode 100644
index 0000000..b84a7cb
--- /dev/null
+++ b/.gitea/workflows/verify.yml
@@ -0,0 +1,39 @@
+name: Verify
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ workflow_dispatch:
+
+concurrency:
+ group: verify-${{ gitea.repository }}-${{ gitea.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ verify:
+ runs-on: ubuntu-latest
+ timeout-minutes: 45
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "22"
+ cache: npm
+ - name: Select declared npm version
+ run: npm install --global npm@11.17.0
+ - name: Install dependencies
+ run: npm ci
+ - name: Audit runtime dependencies
+ run: npm audit --omit=dev --audit-level=moderate
+ - name: Check, test, and build
+ run: npm run check
+ - name: Install browser engines
+ run: npx playwright install --with-deps chromium firefox webkit
+ - name: Browser tests
+ run: npm run test:browser
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 36e1840..98316f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.2.0 - 2026-09-02
+
+- Added exact atomic text-patch application/reversal against explicit local
+ snapshots with deterministic JSON/ZIP export.
+- Added layered ignore sources and tracked-state explanations, root
+ `.gitattributes` evaluation, and advisory release planning from Conventional
+ Commit headers.
+
## 0.1.0 - 2026-09-01
- Added the initial local-first Git Tools workbench.
diff --git a/README.md b/README.md
index 6336090..83a9074 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,15 @@ Git Tools is a standalone local-first application in the [add·ideas Toolbox](ht
- Unified and `git diff` patch parsing with file status, modes, renames, hunk counts, line numbers, statistics, binary markers, and diagnostics
- Unified patch authoring from before/after text with bounded LCS, context selection, path safety, and missing-final-newline markers
-- Root `.gitignore` matching for comments, escapes, globstars, character classes, directory rules, negation, ordered explanations, and ignored-parent diagnostics
+- Exact, atomic text-patch application and reversal against an explicit JSON repository snapshot, with downloadable JSON or deterministic ZIP output and no working-tree access
+- Layered `.gitignore` matching for comments, escapes, globstars, character classes, directory rules, negation, global excludes, `.git/info/exclude`, parent-to-child nested files, explicit tracked state, winning-rule explanations, and ignored-parent diagnostics
+- Root `.gitattributes` evaluation with set/unset/value/unspecified states, last-match provenance, and directory-pattern diagnostics
- Strict SemVer 2.0 precedence plus common exact, comparator, partial, wildcard, hyphen, tilde, caret, AND, and OR ranges
- Conventional Commit builder and structural lint with breaking-change/footer diagnostics
- Keep-a-Changelog heading/section normalization, stable ordering, and duplicate-entry removal
+- Advisory release planning from Conventional Commit headers: SemVer bump, grouped notes, tag name, invalid-input evidence, and review-only command sequence
-Patch and before/after inputs are capped at 4 MiB with additional file, line, hunk, and diff-matrix bounds. `.gitignore` evaluates one repository-root file only; it does not read nested ignore files, global excludes, index state, or submodules. No workspace or `.git` directory is accessed. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/PRIVACY-SECURITY.md](docs/PRIVACY-SECURITY.md).
+Patch and before/after inputs are capped at 4 MiB with additional file, line, hunk, and diff-matrix bounds. Applied snapshots are text-only, exact-match, atomic, limited to 5,000 files/32 MiB, and never touch the working tree. Ignore inputs are an explicit local snapshot; command-line exclude rules and submodule repository boundaries are not modeled. Attribute macros and nested attribute files are not expanded. No workspace or `.git` directory is accessed. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/PRIVACY-SECURITY.md](docs/PRIVACY-SECURITY.md).
## Development
@@ -27,7 +30,7 @@ npm run test:browser
## Release
-`npm run release:artifact` creates a deterministic `release/git-tools-0.1.0.zip` and checksum sidecar.
+`npm run release:artifact` creates a deterministic `release/git-tools-0.2.0.zip` and checksum sidecar.
## Licence
diff --git a/SOURCE.md b/SOURCE.md
index e3abdc4..d726cde 100644
--- a/SOURCE.md
+++ b/SOURCE.md
@@ -1,7 +1,7 @@
# Corresponding source
-The corresponding source for Git Tools 0.1.0 is available at:
+The corresponding source for Git Tools 0.2.0 is available at:
-https://git.add-ideas.de/lotobo/git-tools/src/tag/v0.1.0
+https://git.add-ideas.de/lotobo/git-tools/src/tag/v0.2.0
Build with Node.js 22, npm 11, `npm ci`, and `npm run release:artifact`.
diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md
index 34e3918..d74e8c1 100644
--- a/THIRD_PARTY_NOTICES.md
+++ b/THIRD_PARTY_NOTICES.md
@@ -1,12 +1,12 @@
# Third-party notices
-Git Tools 0.1.0 directly depends on these runtime packages:
+Git Tools 0.2.0 directly depends on these runtime packages:
| Package | Pinned version | Declared licence |
| -------------------------------- | -------------: | ---------------- |
-| `@add-ideas/toolbox-contract` | 0.2.3 | Apache-2.0 |
-| `@add-ideas/toolbox-helpers` | 0.1.0 | GPL-3.0-or-later |
-| `@add-ideas/toolbox-shell-react` | 0.2.3 | Apache-2.0 |
+| `@add-ideas/toolbox-contract` | 0.3.0 | Apache-2.0 |
+| `@add-ideas/toolbox-helpers` | 0.2.0 | GPL-3.0-or-later |
+| `@add-ideas/toolbox-shell-react` | 0.3.0 | Apache-2.0 |
| `react` | 19.2.8 | MIT |
| `react-dom` | 19.2.8 | MIT |
diff --git a/package-lock.json b/package-lock.json
index 3616057..f6a775b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,22 +1,23 @@
{
"name": "git-tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "git-tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"license": "GPL-3.0-or-later",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3",
- "@add-ideas/toolbox-helpers": "0.1.0",
- "@add-ideas/toolbox-shell-react": "0.2.3",
+ "@add-ideas/toolbox-contract": "0.3.0",
+ "@add-ideas/toolbox-helpers": "0.2.0",
+ "@add-ideas/toolbox-shell-react": "0.3.0",
+ "fflate": "0.8.2",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
- "@add-ideas/toolbox-testkit": "0.2.3",
+ "@add-ideas/toolbox-testkit": "0.3.0",
"@eslint/js": "10.0.1",
"@playwright/test": "1.62.1",
"@testing-library/jest-dom": "6.9.1",
@@ -42,24 +43,24 @@
}
},
"node_modules/@add-ideas/toolbox-contract": {
- "version": "0.2.3",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=20"
- }
+ "version": "0.3.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.3.0/toolbox-contract-0.3.0.tgz",
+ "integrity": "sha512-dKrK7BjOFwqJaBfJuhKxZKIld4sH0AKjEn6a0yLnbdMUFY+fFv4VSLGV2tNSBD016gumc2iNqOjUj/ld7x4rtA==",
+ "license": "Apache-2.0"
},
"node_modules/@add-ideas/toolbox-helpers": {
- "version": "0.1.0",
- "license": "GPL-3.0-or-later",
- "engines": {
- "node": ">=22"
- }
+ "version": "0.2.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.2.0/toolbox-helpers-0.2.0.tgz",
+ "integrity": "sha512-SdOqkw+P+3J3fa5iVkzb5P15rVepB001GNV21Oh8w0CZcVL+YRltgD/s+MVcTyrNijWQf3E5vtQON/3N2LLyKg==",
+ "license": "GPL-3.0-or-later"
},
"node_modules/@add-ideas/toolbox-shell-react": {
- "version": "0.2.3",
+ "version": "0.3.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.3.0/toolbox-shell-react-0.3.0.tgz",
+ "integrity": "sha512-74p6JzAOG0YCAKdlc1hLofV4ZIko7vb448S75cIiM88PKm93EHl5VD7g8YVyfM56Ui97UY9dmy+Whiq4sGzpsg==",
"license": "Apache-2.0",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3"
+ "@add-ideas/toolbox-contract": "0.3.0"
},
"peerDependencies": {
"react": ">=18 <20",
@@ -67,17 +68,16 @@
}
},
"node_modules/@add-ideas/toolbox-testkit": {
- "version": "0.2.3",
+ "version": "0.3.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.3.0/toolbox-testkit-0.3.0.tgz",
+ "integrity": "sha512-4Fk+oSvZFspOMIXr8Xy040nhAaBsIQAzsGyXWSpjn3+k3yBKq7nB1r5zCHhsXzfdLzvPDAx2KcmSNOhM330D9w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3"
+ "@add-ideas/toolbox-contract": "0.3.0"
},
"bin": {
"toolbox-check": "dist/cli.js"
- },
- "engines": {
- "node": ">=20"
}
},
"node_modules/@adobe/css-tools": {
@@ -2009,6 +2009,12 @@
}
}
},
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "license": "MIT"
+ },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"dev": true,
diff --git a/package.json b/package.json
index ed5005d..f59872c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "git-tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Inspect and author Git interchange formats locally in the browser.",
"license": "GPL-3.0-or-later",
"author": "Albrecht Degering",
@@ -39,14 +39,15 @@
"release:artifact": "npm run check && npm run test:browser && npm run package:release -- --force"
},
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3",
- "@add-ideas/toolbox-helpers": "0.1.0",
- "@add-ideas/toolbox-shell-react": "0.2.3",
+ "@add-ideas/toolbox-contract": "0.3.0",
+ "@add-ideas/toolbox-helpers": "0.2.0",
+ "@add-ideas/toolbox-shell-react": "0.3.0",
+ "fflate": "0.8.2",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
- "@add-ideas/toolbox-testkit": "0.2.3",
+ "@add-ideas/toolbox-testkit": "0.3.0",
"@eslint/js": "10.0.1",
"@playwright/test": "1.62.1",
"@testing-library/jest-dom": "6.9.1",
diff --git a/playwright.config.ts b/playwright.config.ts
index 731da62..2ab6a22 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -15,7 +15,25 @@ export default defineConfig({
timeout: 180_000,
},
projects: [
- { name: "chromium", use: { ...devices["Desktop Chrome"] } },
- { name: "firefox", use: { ...devices["Desktop Firefox"] } },
+ {
+ name: "chromium",
+ testIgnore: /responsive\.spec\.ts/,
+ use: { ...devices["Desktop Chrome"] },
+ },
+ {
+ name: "firefox",
+ testIgnore: /responsive\.spec\.ts/,
+ use: { ...devices["Desktop Firefox"] },
+ },
+ {
+ name: "webkit",
+ testIgnore: /responsive\.spec\.ts/,
+ use: { ...devices["Desktop Safari"] },
+ },
+ {
+ name: "mobile-chromium",
+ testMatch: /responsive\.spec\.ts/,
+ use: { ...devices["Pixel 5"] },
+ },
],
});
diff --git a/public/CHANGELOG.md b/public/CHANGELOG.md
index 36e1840..98316f6 100644
--- a/public/CHANGELOG.md
+++ b/public/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.2.0 - 2026-09-02
+
+- Added exact atomic text-patch application/reversal against explicit local
+ snapshots with deterministic JSON/ZIP export.
+- Added layered ignore sources and tracked-state explanations, root
+ `.gitattributes` evaluation, and advisory release planning from Conventional
+ Commit headers.
+
## 0.1.0 - 2026-09-01
- Added the initial local-first Git Tools workbench.
diff --git a/public/LICENSES/npm-runtime-licenses.txt b/public/LICENSES/npm-runtime-licenses.txt
index 45a7c95..d1e91be 100644
--- a/public/LICENSES/npm-runtime-licenses.txt
+++ b/public/LICENSES/npm-runtime-licenses.txt
@@ -1,5 +1,5 @@
==============================================================================
-@add-ideas/toolbox-contract@0.2.3
+@add-ideas/toolbox-contract@0.3.0
Declared licence: Apache-2.0
==============================================================================
--- LICENSE ---
@@ -198,7 +198,7 @@ Declared licence: Apache-2.0
==============================================================================
-@add-ideas/toolbox-helpers@0.1.0
+@add-ideas/toolbox-helpers@0.2.0
Declared licence: GPL-3.0-or-later
==============================================================================
--- LICENSE ---
@@ -879,7 +879,7 @@ Public License instead of this License. But first, please read
==============================================================================
-@add-ideas/toolbox-shell-react@0.2.3
+@add-ideas/toolbox-shell-react@0.3.0
Declared licence: Apache-2.0
==============================================================================
--- LICENSE ---
@@ -1077,6 +1077,33 @@ Declared licence: Apache-2.0
limitations under the License.
+==============================================================================
+fflate@0.8.2
+Declared licence: MIT
+==============================================================================
+--- LICENSE ---
+MIT License
+
+Copyright (c) 2023 Arjun Barrett
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
==============================================================================
react@19.2.8
Declared licence: MIT
diff --git a/public/README.md b/public/README.md
index 6336090..83a9074 100644
--- a/public/README.md
+++ b/public/README.md
@@ -8,12 +8,15 @@ Git Tools is a standalone local-first application in the [add·ideas Toolbox](ht
- Unified and `git diff` patch parsing with file status, modes, renames, hunk counts, line numbers, statistics, binary markers, and diagnostics
- Unified patch authoring from before/after text with bounded LCS, context selection, path safety, and missing-final-newline markers
-- Root `.gitignore` matching for comments, escapes, globstars, character classes, directory rules, negation, ordered explanations, and ignored-parent diagnostics
+- Exact, atomic text-patch application and reversal against an explicit JSON repository snapshot, with downloadable JSON or deterministic ZIP output and no working-tree access
+- Layered `.gitignore` matching for comments, escapes, globstars, character classes, directory rules, negation, global excludes, `.git/info/exclude`, parent-to-child nested files, explicit tracked state, winning-rule explanations, and ignored-parent diagnostics
+- Root `.gitattributes` evaluation with set/unset/value/unspecified states, last-match provenance, and directory-pattern diagnostics
- Strict SemVer 2.0 precedence plus common exact, comparator, partial, wildcard, hyphen, tilde, caret, AND, and OR ranges
- Conventional Commit builder and structural lint with breaking-change/footer diagnostics
- Keep-a-Changelog heading/section normalization, stable ordering, and duplicate-entry removal
+- Advisory release planning from Conventional Commit headers: SemVer bump, grouped notes, tag name, invalid-input evidence, and review-only command sequence
-Patch and before/after inputs are capped at 4 MiB with additional file, line, hunk, and diff-matrix bounds. `.gitignore` evaluates one repository-root file only; it does not read nested ignore files, global excludes, index state, or submodules. No workspace or `.git` directory is accessed. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/PRIVACY-SECURITY.md](docs/PRIVACY-SECURITY.md).
+Patch and before/after inputs are capped at 4 MiB with additional file, line, hunk, and diff-matrix bounds. Applied snapshots are text-only, exact-match, atomic, limited to 5,000 files/32 MiB, and never touch the working tree. Ignore inputs are an explicit local snapshot; command-line exclude rules and submodule repository boundaries are not modeled. Attribute macros and nested attribute files are not expanded. No workspace or `.git` directory is accessed. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/PRIVACY-SECURITY.md](docs/PRIVACY-SECURITY.md).
## Development
@@ -27,7 +30,7 @@ npm run test:browser
## Release
-`npm run release:artifact` creates a deterministic `release/git-tools-0.1.0.zip` and checksum sidecar.
+`npm run release:artifact` creates a deterministic `release/git-tools-0.2.0.zip` and checksum sidecar.
## Licence
diff --git a/public/SOURCE.md b/public/SOURCE.md
index e3abdc4..d726cde 100644
--- a/public/SOURCE.md
+++ b/public/SOURCE.md
@@ -1,7 +1,7 @@
# Corresponding source
-The corresponding source for Git Tools 0.1.0 is available at:
+The corresponding source for Git Tools 0.2.0 is available at:
-https://git.add-ideas.de/lotobo/git-tools/src/tag/v0.1.0
+https://git.add-ideas.de/lotobo/git-tools/src/tag/v0.2.0
Build with Node.js 22, npm 11, `npm ci`, and `npm run release:artifact`.
diff --git a/public/THIRD_PARTY_NOTICES.md b/public/THIRD_PARTY_NOTICES.md
index 34e3918..d74e8c1 100644
--- a/public/THIRD_PARTY_NOTICES.md
+++ b/public/THIRD_PARTY_NOTICES.md
@@ -1,12 +1,12 @@
# Third-party notices
-Git Tools 0.1.0 directly depends on these runtime packages:
+Git Tools 0.2.0 directly depends on these runtime packages:
| Package | Pinned version | Declared licence |
| -------------------------------- | -------------: | ---------------- |
-| `@add-ideas/toolbox-contract` | 0.2.3 | Apache-2.0 |
-| `@add-ideas/toolbox-helpers` | 0.1.0 | GPL-3.0-or-later |
-| `@add-ideas/toolbox-shell-react` | 0.2.3 | Apache-2.0 |
+| `@add-ideas/toolbox-contract` | 0.3.0 | Apache-2.0 |
+| `@add-ideas/toolbox-helpers` | 0.2.0 | GPL-3.0-or-later |
+| `@add-ideas/toolbox-shell-react` | 0.3.0 | Apache-2.0 |
| `react` | 19.2.8 | MIT |
| `react-dom` | 19.2.8 | MIT |
diff --git a/public/sw.js b/public/sw.js
index 160c63b..5bfae78 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -1,5 +1,5 @@
const CACHE_PREFIX = "git-tools-shell-";
-const CACHE_NAME = CACHE_PREFIX + "0.1.0";
+const CACHE_NAME = CACHE_PREFIX + "0.2.0";
const CORE = ["./", "./manifest.webmanifest", "./favicon.svg"];
self.addEventListener("install", (event) => {
event.waitUntil(
diff --git a/public/toolbox-app.json b/public/toolbox-app.json
index 258735d..18dd994 100644
--- a/public/toolbox-app.json
+++ b/public/toolbox-app.json
@@ -3,7 +3,7 @@
"schemaVersion": 1,
"id": "de.add-ideas.git-tools",
"name": "Git Tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Inspect and author Git interchange formats locally in the browser.",
"entry": "./",
"icon": "./favicon.svg",
@@ -29,9 +29,25 @@
"crossOriginIsolated": false,
"topLevelContext": false
},
+ "io": {
+ "accepts": [
+ { "mediaType": "text/x-diff", "extensions": [".diff", ".patch"] },
+ {
+ "mediaType": "text/plain",
+ "extensions": [".gitignore", ".gitattributes", ".txt"]
+ },
+ { "mediaType": "application/json", "extensions": [".json"] }
+ ],
+ "produces": [
+ { "mediaType": "text/x-diff", "extensions": [".patch"] },
+ { "mediaType": "application/json", "extensions": [".json"] },
+ { "mediaType": "application/zip", "extensions": [".zip"] }
+ ]
+ },
+ "capabilities": { "required": [], "optional": [] },
"privacy": {
"processing": "local",
- "fileUploads": true,
+ "fileUploads": false,
"telemetry": false,
"label": "Inputs stay in this browser; nothing is uploaded."
},
diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx
index e4887dc..d440743 100644
--- a/src/components/HelpDialog.tsx
+++ b/src/components/HelpDialog.tsx
@@ -31,7 +31,11 @@ export function HelpDialog({
×
-
Inspect and author Git interchange formats locally in the browser.
+
+ Inspect and author Git interchange formats locally in the browser, apply
+ or reverse text patches against explicit JSON snapshots, explain ignore
+ and attribute rules, and draft an advisory release plan.
+