Git is one of those tools we can’t imagine building software without. At Sanmark, Git helps us collaborate smoothly, track changes clearly, and deliver stable, production-ready software with confidence.
Over time, our Git workflow has evolved. With growing teams, QA processes, and multiple releases, we needed something structured yet flexible. This blog explains the Git practice we currently follow at Sanmark, in a simple and practical way.
Initial Setup and Branching
Every project at Sanmark starts with a master (or main) branch. This branch represents production-ready code. From day one, we treat it with care — whatever lives here should be stable and deployable at any time.
Soon after, we create the dev branch. This is where active development happens. New features, improvements, and fixes are gradually collected here after initial testing. The dev branch always stays one step ahead of production.

To support proper testing, we also maintain a qa branch. This branch exists specifically for the QA process. Any feature or fix that is ready for testing is merged into qa, where the QA team verifies functionality, stability, and overall quality before it moves forward.

Feature and Fix Branches
Whenever a developer works on a new feature, they create a feature branch from the latest dev. We use the naming convention do(issue-number) (for example, do1, do2). The branch contains all commits related to that feature. Once the work is complete, the feature branch is first merged into qa for testing. If QA identifies issues, rework commits are added to the same branch and pushed. The full commit history is preserved to track progress, changes, and QA rework.

After the QA process approves the feature, it is merged into dev through a Pull Request (PR). Branches are deleted only after successful merging. Developers are encouraged to update their feature branch with the latest dev changes only if recent updates affect their current work or could create conflicts.
Bug fixes follow a similar workflow. Hotfixes for production are branched from master, while non-production fixes are branched from dev. These branches use the naming convention fix(issue-number) (e.g., fix1, fix2). After QA approval, hotfixes are merged into both master and dev, ensuring the fix is included in the next release. Non-production fixes are merged into dev after QA approval.

Commits and Pull Requests
Commit messages are vital. Each commit should clearly describe what was changed. For feature branches, the format is (issue-number) (subject) (e.g., 101 Implement user login feature). For bug fixes, we add fix after the issue number (e.g., 202 fix: resolve token validation error).
During development and QA rework, we do not squash commits, so the full history is preserved. This ensures traceability, better understanding of changes, and easy QA tracking.
Pull Requests are required for all merges into qa, dev, or master. Every PR must be reviewed by at least one team member. Developers should ensure that their branch is up-to-date with the latest changes from dev or master if necessary. Commits are never squashed during PR creation to maintain the full history.
Code Reviews
Code reviews are mandatory for all PRs. Reviewers check for correctness, readability, and adherence to our best practices guidelines. The full history, including QA rework commits, allows reviewers to understand the evolution of the feature or fix. More details can be found in our Best Practice Guidelines.
Merge Strategy
Merging is the process of integrating changes from one branch into another. We maintain all commit history without squashing to ensure traceability.
- Feature → QA → Dev : Features merge into qa first, then into dev after QA approval.
- Dev → Master : Merge dev into master using non-fast-forward to create a clear merge commit. Version number is added to the merge commit message.
(Read the post “How We Version Our Code”.)
- Fix → Master/Dev : Hotfixes merge into master directly and then into dev if created from master. Non-production fixes merge into dev only.
Developers should keep their branches updated with dev if changes affect their current work. Conflicts must be resolved manually, with all commit history preserved.

Conflict Resolution
Merge conflicts are handled by the developer responsible for the conflict. If assistance is required, the issue is escalated to the project owner. Resolving conflicts carefully ensures the code remains stable and traceable.