This document describes the GitFlow workflow used in the SKaiNET project for managing feature development, releases, and hotfixes.
GitFlow is a branching model for Git that defines specific branch types and their purposes. It provides a robust framework for managing larger projects with scheduled releases and helps teams collaborate effectively while maintaining code stability.
-
Purpose: Tracks the most recently published release — nothing more, nothing less. If it’s on
main, it has a tag and a Maven Central publish behind it. -
Lifetime: Permanent
-
Protection: Not currently enabled on GitHub (no required reviews/checks) — direct pushes are possible but should never happen outside the release step below. Enabling protection is a TODO, not a claim this document should make until it’s actually turned on.
-
Merges from:
release/andhotfix/branches only, as an explicit last step after that branch’s tag has been pushed and its publish workflow has gone green — never before, and never automatically. -
Reset 2026-08-29 (SKaiNET#1198 thread):
mainhad drifted ~1,800 commits behinddevelop(stuck at0.2.0) because the "merge release branch tomain`" step had been opened as a PR for every release since, and closed unmerged every time — `developwas already the project’s real default branch and the actual publish trigger (a pushed tag), so nothing neededmainto be current, and it wasn’t. Rather than reconcile ~1,800 commits of drift with no real value in the history itself, the old branch was renamed tolegacy(its full history is still there, just not onmainanymore) and a newmainwas created starting at the0.51.0tag.mainbefore 0.51.0 lives atlegacy, not in `main’s own history.
-
Purpose: Integration branch for features under development
-
Lifetime: Permanent
-
Protection: Direct commits should be minimal
-
Merges from:
feature/,release/, andhotfix/*branches
-
Purpose: Develop new features for upcoming releases
-
Naming convention:
feature/feature-nameorfeature/ISSUE-123-feature-name(e.g.,feature/cnn-layers,feature/transformer-embeddings) -
Branch from:
develop -
Merge back to:
develop -
Lifetime: Until feature is complete
-
Deletion: After successful merge to
develop
-
Purpose: Prepare new production releases
-
Naming convention:
release/1.2.0(following semantic versioning) -
Branch from:
develop -
Merge back to:
mainanddevelop -
Lifetime: Until release is deployed
-
Activities: Bug fixes, documentation updates, release preparation
-
Purpose: Quick fixes for critical production issues
-
Naming convention:
hotfix/1.2.1orhotfix/critical-fix -
Branch from:
main -
Merge back to:
mainanddevelop -
Lifetime: Until fix is deployed
-
Priority: High - should be processed immediately
|
Note
|
This diagram shows the conceptual GitFlow shape (tag on main after merging the release
branch in). SKaiNET’s actual sequence tags the release PR’s merge commit on develop before
touching main — see Release Process below for the real order and why.
|
gitGraph
commit id: "Initial"
branch develop
checkout develop
commit id: "Dev setup"
branch feature/cnn-layers
checkout feature/cnn-layers
commit id: "Add conv2d layer"
commit id: "Add pooling layer"
checkout develop
merge feature/cnn-layers
commit id: "Merge CNN layers"
branch feature/tokenizer
checkout feature/tokenizer
commit id: "Add word embeddings"
commit id: "Add BPE tokenizer"
checkout develop
merge feature/tokenizer
commit id: "Integration"
branch release/1.0.0
checkout release/1.0.0
commit id: "Prepare v1.0.0"
commit id: "Fix tests"
checkout main
merge release/1.0.0
commit id: "Release v1.0.0" tag: "v1.0.0"
checkout develop
merge release/1.0.0
checkout main
branch hotfix/1.0.1
checkout hotfix/1.0.1
commit id: "Fix gradient explosion"
checkout main
merge hotfix/1.0.1
commit id: "Hotfix v1.0.1" tag: "v1.0.1"
checkout develop
merge hotfix/1.0.1
checkout develop
branch feature/transformer-attention
checkout feature/transformer-attention
commit id: "Add self-attention"
-
Create a feature branch from
develop:git checkout develop git pull origin develop git checkout -b feature/lstm-layers
-
Develop the feature with regular commits:
git add . git commit -m "Implement LSTM forward pass" git push origin feature/lstm-layers
-
When feature is complete, create a pull request to
develop -
After code review and approval, merge to
develop:git checkout develop git pull origin develop git merge --no-ff feature/lstm-layers git push origin develop git branch -d feature/lstm-layers
This is the sequence actually used from 0.51.0 onward — it differs from a textbook GitFlow release
in one important way: the tag lives on develop — on the merge commit of the release PR —
independent of main, because the Maven Central publish workflow triggers on the tag push
(on: push: tags: '*' in .github/workflows/publish.yml), not on anything landing on main.
main is updated *after a successful publish, as an explicit last step — never before, and never
as a side effect of tagging.
Two gates guard the tag, because a tag push publishes and a Maven Central release cannot be taken back:
-
Dry run before the release PR merges. The publish workflow only ever runs on a tag push, so ordinary CI can never tell you it is broken. Run it by hand on the release branch first (see the steps below): same pipeline, no upload.
-
Green before tagging. Tag only a commit whose
developworkflows have finished green. A release PR that was green is not the same thing as its merge commit being green.-
Create a release branch from
develop:git checkout develop git pull origin develop git checkout -b release/1.2.0
-
Perform release preparations, each as its own commit:
-
CHANGELOG.md: full entry for the release -
README.md: short "What’s New" highlights for this release only, pointing toCHANGELOG.mdfor history — do not let a "Previously, in …" cascade accumulate here again -
docs/antora.yml(skainet_version) and any hardcoded dependency-coordinate snippets in the tutorials (grep for the previous version string acrossdocs/andREADME.mdto catch drift) -
./gradlew generateKernelMatrix generateDocs— run this after the version bump below, not before, or the regenerated docs stamp the wrong version -
gradle.properties(VERSION_NAME) — its own commit, last, matchingrelease: X.Y.Z -
Run final tests; fix any release-blocking bugs on the branch
-
-
Dry-run the publish workflow on the release branch and wait for it to go green:
git push origin release/1.2.0 gh workflow run publish.yml --ref release/1.2.0 gh run watch "$(gh run list --workflow publish.yml --branch release/1.2.0 --limit 1 --json databaseId -q '.[0].databaseId')"A manually started run (
workflow_dispatch) executes the whole release pipeline — native matrix, Android SDK + NDK setup, signing — but ends inpublishToMavenLocalon the runner and is never given the Maven Central credentials, so it cannot upload. Its job summary lists the version and how many modules, POMs and signatures it produced. This is the only way to find a broken runner setup step before the tag exists (0.56.0 found one after). -
Open a PR from the release branch to
developand merge it once CI is green — this is what actually brings the version bump back into the integration branch:gh pr create --base develop --head release/1.2.0 --title "Release 1.2.0" # wait for CI to go green, then merge with a MERGE COMMIT (not squash, not rebase)
-
Wait until every workflow on that merge commit has finished green on
develop— the merge commit is what gets tagged, and it has its own CI run:git fetch origin MERGE_SHA="$(git rev-parse origin/develop)" # the "Merge pull request #… from …/release/1.2.0" commit gh run list --commit "$MERGE_SHA" --json workflowName,status,conclusion \ -q '.[] | "\(.workflowName): \(.conclusion // .status)"' # every line must say "success" before you continue
-
Tag that merge commit with an annotated tag — not a commit on
main, which doesn’t have this release yet, and not the release branch’srelease: X.Y.Zcommit, which lacks whatever else landed ondevelopalongside it:git tag -a 1.2.0 "$MERGE_SHA" -m "SKaiNET 1.2.0 — <headline>" git push origin 1.2.0
Pushing the tag triggers the publish workflow. Wait for it to go green before the next step — a red publish run means the tag exists but nothing actually shipped to Maven Central; do not treat tagging alone as "released."
If the publish run fails: check Maven Central first. If nothing was uploaded under the version, fix the cause on
developand move the tag to the new commit (delete and re-create it; tell anyone who fetched it togit fetch --tags --force) — the workflow file that runs is the one at the tagged commit, so re-running the old tag cannot pick up a workflow fix. If anything was uploaded, the version is burned: leave the tag alone and cut a patch release. -
Once the publish workflow succeeds, merge
mainup to the release — the step every release before 0.51.0 skipped:git fetch origin git push origin "$MERGE_SHA":refs/heads/main # the tagged commit; fast-forward, main has no protection to route around
This is a manual step by design (see the
mainBranch section above) — not automated on publish success. Do it as part of closing out the release, not as an afterthought. -
Delete the release branch once both merges (into
developandmain) are done:git push origin --delete release/1.2.0
-
-
Create a hotfix branch from
main:git checkout main git pull origin main git checkout -b hotfix/1.2.1
-
Implement the fix:
git add . git commit -m "Fix memory leak in tensor operations"
-
Merge to
main:git checkout main git merge --no-ff hotfix/1.2.1 git tag -a v1.2.1 -m "Hotfix version 1.2.1" git push origin main --tags -
Merge to
develop:git checkout develop git merge --no-ff hotfix/1.2.1 git push origin develop git branch -d hotfix/1.2.1
-
Use descriptive names that reflect the purpose
-
Include issue numbers when applicable:
feature/ISSUE-123-transformer-embeddings -
Use lowercase with hyphens:
feature/add-attention-mechanism
-
Use present tense: "Add feature" not "Added feature"
-
Keep first line under 50 characters
-
Provide detailed description for complex changes
-
Reference issue numbers: "Fix gradient clipping in LSTM (closes #123)"
-
Always create pull requests for merging to
mainanddevelop -
Require code review before merging
-
Include comprehensive description of changes
-
Ensure all tests pass before merging
-
Use
--no-ffflag to preserve branch history
-
Follow Semantic Versioning
-
Tag all releases with version numbers
-
Update version in project files before release
-
Maintain CHANGELOG.md with release notes
-
Run tests on all feature branches
-
Run comprehensive test suite on
developandmain -
Block merges if tests fail
-
A pushed tag (in practice always the release PR’s merge commit on
develop) is what triggers the Maven Central publish workflow — not landing onmain. -
The same workflow started by hand (
workflow_dispatch) is a dry run: full pipeline into Maven Local on the runner, never given the Maven Central credentials. -
mainis a read-only mirror of "what’s been published," updated manually after the fact (see the Release Process above); it has no deploy step of its own. -
developis where CI runs on every push/PR; there is no separate staging deploy.
-
developis protected today: requiredbuild-jobstatus check, no force-pushes, no deletions, enforced for admins too. -
mainis not currently protected — turning that on (required status checks at minimum) is a TODO, tracked so this document doesn’t quietly drift from reality again.
When encountering merge conflicts:
-
Update your branch with latest changes:
git checkout your-branch git fetch origin git merge origin/develop
-
Resolve conflicts manually in your editor
-
Test the resolution
-
Commit the merge resolution
If you commit to wrong branch:
-
Create a new branch from the correct base:
git checkout correct-base-branch git checkout -b new-feature-branch git cherry-pick commit-hash
-
Reset the wrong branch:
git checkout wrong-branch git reset --hard HEAD~1
GitFlow provides a structured approach to managing code changes in collaborative environments. By following this workflow consistently, the SKaiNET project maintains code quality, enables parallel development, and ensures stable releases.
For questions or clarifications about this workflow, please refer to the original GitFlow article or reach out to the project maintainers.