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 branch’s own commit 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 the release branch’s own commit, 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.
-
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
-
-
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:git push origin release/1.2.0 gh pr create --base develop --head release/1.2.0 --title "Release 1.2.0" # wait for CI to go green, then merge
-
Tag the release branch’s own
release: X.Y.Zcommit directly — not a commit onmain, which doesn’t have this release yet:git tag -a 1.2.0 <release-commit-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."
-
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 <release-commit-sha>:refs/heads/main # 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 (any branch, in practice always the
release/*branch’s own commit) is what triggers the Maven Central publish workflow — not landing onmain. -
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.