Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Confirm Git is available
run: git --version
- name: Run complete test suite
run: npm test
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## [1.4.1] - 2026-09-04

### Fixed

- Corrected Panic Recovery guidance for staged wrong-branch commits, reflog-based recovery, force-push overwrites, pull choices, and operation aborts.
- Removed misleading retention promises and tightened safety/reversibility labels for history rewriting and recovery that depends on temporary Git objects.

### Added

- Added dependency-free real-Git integration tests covering recovery, conflicts, branch deletion, and rejected pushes.
- Added GitHub Actions CI for the complete test suite.

### Changed

- Made mutually exclusive pull and abort commands visibly alternative choices in Panic Recovery.
- Hardened version and recovery-data validation.

## [1.4.0] - 2026-05-23

### Changed
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[Live Demo](https://ikelaiah.github.io/git-map/)
[GitHub Repository](https://github.com/ikelaiah/git-map)

[![Version](https://img.shields.io/badge/Version-1.4.0-2f6fed?style=for-the-badge)](CHANGELOG.md)
[![Version](https://img.shields.io/badge/Version-1.4.1-2f6fed?style=for-the-badge)](CHANGELOG.md)
[![License: MIT](https://img.shields.io/badge/License-MIT-34d399?style=for-the-badge)](LICENSE)
[![No Build](https://img.shields.io/badge/Build-none-3bc5a7?style=for-the-badge)](index.html)
[![Dependencies](https://img.shields.io/badge/Dependencies-none-22c55e?style=for-the-badge)](package.json)
Expand Down Expand Up @@ -75,7 +75,7 @@ The project stays no-build and dependency-free, split by responsibility:
- `src/panic.js`: Panic Recovery decision-tree rendering and card highlight.
- `scripts/validate.mjs`: data integrity checks.

Run tests and validators:
Run the unit/data suite, real-Git behavioral tests in temporary repositories, and validators:

```bash
npm test
Expand All @@ -88,7 +88,7 @@ npm test
- `git add -p`: stage selected hunks instead of whole files.
- `git commit -m "message"`: save staged changes as a local commit.
- `git push`: publish local commits to the shared remote branch.
- `git pull`: fetch and integrate remote commits into the current branch.
- `git pull`: fetch and integrate remote commits into the current branch; choose a merge or rebase strategy when histories have diverged.
- `git pull --ff-only`: fetch and update only when the current branch can fast-forward.
- `git pull --rebase`: fetch and replay local commits on top of the upstream.
- `git pull --no-rebase`: fetch and merge the upstream, creating a merge commit when needed.
Expand All @@ -105,6 +105,7 @@ npm test
- `git reset --hard HEAD`: discard all local tracked changes.
- `git restore .`: discard unstaged workspace edits by restoring files from the index.
- `git stash -u`: stash tracked and untracked files.
- `git stash pop --index`: restore a stash and ask Git to restore its saved staging state too.
- `git stash branch <branch>`: create a branch from a stash.
- `git log --oneline --graph --decorate --all`: view commit history as a graph.
- `git blame <file>`: see who last changed each line.
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "git-map",
"version": "1.4.0",
"version": "1.4.1",
"private": true,
"description": "Visual Git sandbox for mapping status, experimenting with branches, and recovering from common mistakes.",
"scripts": {
"test": "node --test tests/*.test.mjs && node scripts/validate.mjs",
"test:unit": "node --test tests/*.test.mjs",
"test": "npm run test:unit && npm run test:git && npm run validate",
"test:unit": "node --test tests/branch-model.test.mjs tests/cross-file-constants.test.mjs tests/panic-data.test.mjs tests/status-matcher.test.mjs tests/validator.test.mjs",
"test:git": "node --test tests/git-integration.test.mjs",
"validate": "node scripts/validate.mjs"
}
}
12 changes: 12 additions & 0 deletions scripts/panic-validator.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const VALID_REVERSIBILITY = new Set(["safe", "caution", "danger"]);
const DESTRUCTIVE_OR_REWRITING_COMMAND = /(?:--force(?:-with-lease)?|\bpush -f\b|\breset --(?:hard|soft)\b|\bcommit --amend\b|\bfilter-repo\b|\bbranch -D\b|\bclean -fd?\b)/;

function isNonEmptyString(value) {
return typeof value === "string" && value.trim().length > 0;
Expand Down Expand Up @@ -38,11 +39,22 @@ export function validatePanicData(panicData) {
fail(`Panic recovery "${recovery.id}" is missing commands.`);
} else {
recovery.commands.forEach((step, index) => {
if (isNonEmptyString(step.heading)) {
return;
}
if (!isNonEmptyString(step.command) || !isNonEmptyString(step.note)) {
fail(`Panic recovery "${recovery.id}" command ${index} is missing command or note.`);
}
});
}

if (recovery.reversibility === "safe") {
recovery.commands.forEach((step) => {
if (DESTRUCTIVE_OR_REWRITING_COMMAND.test(step.command || "")) {
fail(`Panic recovery "${recovery.id}" is marked safe but includes a destructive or history-rewriting command.`);
}
});
}
});

const referencedRecoveryIds = new Set();
Expand Down
7 changes: 7 additions & 0 deletions scripts/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const dataCode = fs.readFileSync("src/data.js", "utf8");
const branchModelCode = fs.readFileSync("src/branch-map-model.js", "utf8");
const panicDataCode = fs.readFileSync("src/panic-data.js", "utf8");
const versionCode = fs.readFileSync("src/version.js", "utf8");
const packageJson = JSON.parse(fs.readFileSync("package.json", "utf8"));
const readme = fs.readFileSync("README.md", "utf8");
const changelog = fs.readFileSync("CHANGELOG.md", "utf8");
const indexHtml = fs.readFileSync("index.html", "utf8");
Expand Down Expand Up @@ -38,6 +39,12 @@ if (!version) {
if (!changelog.includes(`## [${version}]`)) {
fail(`CHANGELOG is missing an entry for src/version.js (${version}).`);
}
if (packageJson.version !== version) {
fail(`package.json version (${packageJson.version}) does not match src/version.js (${version}).`);
}
if (!readme.includes(`Version-${version}-`)) {
fail(`README version badge does not match src/version.js (${version}).`);
}
[
["index.html", indexHtml],
["branch-map.html", branchHtml],
Expand Down
5 changes: 3 additions & 2 deletions src/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ const commands = [
color: "var(--red)",
marker: "arrow-red",
zones: ["remote", "workspace"],
note: "Fetches the configured upstream and integrates it into the current branch. Current Git defaults to fast-forward-only unless pull strategy config or options say otherwise."
note: "Fetches the configured upstream and integrates it into the current branch. For divergent histories, choose or configure a rebase or merge strategy; --ff-only is a separate explicit safety option."
},
{
id: "push",
Expand Down Expand Up @@ -382,6 +382,7 @@ const toolCommands = {
stash: [
{ command: "git stash list", note: "Show saved stashes." },
{ command: "git stash show -p", note: "Preview what is inside a stash." },
{ command: "git stash pop --index", note: "Restore a stash and ask Git to restore its saved staging/index state too." },
{ command: "git stash -u", note: "Stash tracked and untracked files." },
{ command: "git stash branch <branch>", note: "Create a branch from a stash." }
],
Expand Down Expand Up @@ -614,7 +615,7 @@ const statusScenarios = [
label: "Diverged",
zone: "remote",
commandId: "fetch",
summary: "Both local and remote have commits the other side does not have. Plain git pull may stop under the fast-forward-only default, so choose rebase, merge, or run git fetch first and then merge origin/<branch>.",
summary: "Both local and remote have commits the other side does not have. Choose a rebase or merge strategy, or fetch first and then merge origin/<branch>; do not treat these alternatives as a sequence.",
checks: ["git status", "git fetch", "git log --oneline --graph --decorate --all"],
next: ["git pull --rebase", "git pull --no-rebase", "git fetch", "git merge origin/<branch>"]
},
Expand Down
Loading
Loading