Milestone 2 · Software Technology
Working as a team in git: issues, branches, merge requests
Milestone 2: the team workflow – protected main and develop, a branch per issue, commit messages checked by git hooks, labelled issues on a board, and reviewed merge requests merged with merge commits.
Milestone 2, in week 7, is the first prototype: about 30% of the use cases work, and version control is working and actively used by every member. From this milestone on, your GitLab activity – issues, branches, commits, merge requests, reviews – weighs heavily in your score, because it is the record of who did what.
This page is the team workflow the course recommends, as packaged in its template repository: two protected branches, a branch per issue, a fixed commit format checked by git hooks, and merge requests reviewed by a teammate. It assumes you know commits, branches and merges; if not, read Git and GitHub: the mental model, in pictures first.
The rules on one screen
| What | Rule |
|---|---|
| Branches | main = released state, develop = integration. Both protected: nobody pushes to them, code arrives only through merge requests. |
| Branch name | feature/ABC-12-short-title (or bugfix/), where ABC-12 is issue #12. One issue = one branch. Starts from develop, returns to it. |
| Commit | ABC-12: add save file validation – imperative, English, at most 72 characters. The hook adds the prefix from the branch name. |
| Issue | All work starts from an issue: template, milestone, assignee, type label. |
| Board | Todo → Doing → Review → Closed. You move the card; the merge request closes the issue. |
| Merge request | Template filled, one approval from a teammate, green pipeline, all threads resolved, merge commit. The author merges. |
| Definition of Done | Acceptance criteria met, unit tests for the model, pipeline green, MR merged, wiki updated. |
ABC stands for your project's 2–5 letter key; the template uses KEY as a placeholder that you replace once. Older course material says master; new GitLab projects create main – same role, different name.
Branches: main, develop, one per issue
Two long-lived branches carry the project. develop is where finished work comes together, and it should always build. main only moves when the team releases a milestone. All actual work happens on short-lived branches that start from develop and come back through a merge request (Fig. 1).
| Branch | Purpose | From | Back into |
|---|---|---|---|
main | Released, demoed state. Tagged after every merge. | – | – |
develop | Integration. Feature MRs land here; always builds. | – | – |
feature/ABC-12-… | One issue: a feature, but also a refactor, tests, docs. | develop | develop |
bugfix/ABC-34-… | A bug found on develop. | develop | develop |
release/v1.0 | Finishing a milestone release: version, README, small fixes. | develop | main, then main → develop |
hotfix/ABC-56-… | An urgent fix on the released version. | main | main, then main → develop |
Branch names
feature/ABC-12-implement-save-load ← issue #12, a new feature (also: refactor, tests, docs)
bugfix/ABC-34-fix-wall-collision ← issue #34, a bug found on develop
release/v1.0 ← preparing the milestone release, no issue number
hotfix/ABC-56-crash-on-empty-save ← issue #56, urgent fix on the released version
anna-branch, peter-dev, test2, final ← not branch names: they say who, not whatThe issue page in GitLab has a Create merge request / Create branch button that generates the name for you, in exactly this form if the project's branch name template is set. And push your branch every day, finished or not: it is your backup, and it shows the team where you are.
Issues, labels, milestones, the board
Every piece of work is an issue – a feature, a bug, a refactor, a test, a page of documentation, CI work. No issue, no branch, no commit. An issue is created from a template (Feature or Bug), has a short imperative title (Implement save/load), exactly one assignee, a milestone and a type label. If two people work on something, it is two issues.
type::…- feature, bug, refactor, test, docs, ci – what kind of work it is.
status::…- todo, doing, review – the board column. Scoped labels exclude each other, so an issue sits in one column.
priority::high- Only when it matters.
- Milestone
- M1 … M4, due on the weeks 4, 7, 10 and 13. What doesn't fit moves to the next one – it never hangs in the air.
The templates end with GitLab quick actions, which run when the issue is saved – that is how every new issue gets its labels without anyone remembering to add them:
The end of an issue description
## Acceptance criteria
- [ ] Save creates a file in the user's save folder
- [ ] Error case: loading a corrupt file keeps the current game
/label ~"type::feature" ~"status::todo"
/milestone %"M2 Prototype I"
/assign meThe path of one issue across the board and through git (Fig. 2):
From issue #12 to a pushed branch
git switch develop
git pull
git switch -c feature/ABC-12-implement-save-load
# ... work, then commit in small steps:
git add -p
git commit -m "add save file serialization" # the hook makes it "ABC-12: add save ..."
git push -u origin feature/ABC-12-implement-save-loadThe full command sequence, including updating your branch and cleaning up after the merge, is in the issue lifecycle cheat sheet.
Commit messages and hooks
The format is ABC-12: imperative summary. Imperative, in English: add, fix, remove – read it as "this commit will … add save file serialization". One commit is one logical step. If a reason needs explaining, add a body after a blank line: the body says why, the diff already says what.
Good and bad first lines
ABC-12: add save file serialization ✓ imperative, says what this step does
ABC-12: handle missing save directory ✓ one logical step
ABC-34: fix crash when settings file is empty ✓
ABC-12: fixed stuff. ✗ past tense, says nothing, ends with a period
wip ✗ no key, says nothing – the hook rejects it
ABC-12: update ✗ "update" what?A commit with a body
ABC-84: retry save when the target directory is locked
On Windows the save directory can be locked for a moment by the
antivirus; a single retry after 100 ms removes the spurious failure.You don't type the prefix. Two git hooks in the repository's githooks/ folder do the bookkeeping (Fig. 3): prepare-commit-msg reads the issue key from the branch name and prepends it, commit-msg checks the result and refuses a bad message with an explanation.
Hooks are not cloned automatically – each member switches them on once per clone. They run inside git itself, so they work from the terminal, IntelliJ, Rider, Visual Studio, VS Code and GitHub Desktop alike (on Windows through Git for Windows' bundled bash).
Once per clone, every member
git clone <repo-url>
cd <repo>
./setup-hooks.sh # once per clone; Windows cmd / PowerShell: .\setup-hooks.bat
# it runs: git config core.hooksPath githooksThe core of the two hooks
# prepare-commit-msg: take the key from the branch name ...
TICKET=$(printf '%s' "$BRANCH" | sed -nE 's#^[a-z]+/([A-Za-z]{2,5}-[0-9]+).*#\1#p')
# commit-msg: ... and reject a first line that doesn't follow the rules
printf '%s' "$FIRST_LINE" | grep -qE '^([A-Z]{2,5}-[0-9]+|release|hotfix|chore|docs|ci): .+' \
|| fail "missing or invalid prefix"
[ ${#FIRST_LINE} -gt 72 ] && fail "subject line is too long"Why ABC-12 and not #12?
- In git,
#at the start of a line is a comment: a message written in the editor as#12: add …loses its first line and git aborts with "empty commit message". - A key says which project the issue belongs to, even when the commit is quoted somewhere else. It is the form Jira and most companies use, so the habit transfers.
- GitLab links the issue through the branch name and the Closes #12 line of the merge request; the key in the message is for people reading git log and git blame.
Merge requests
A merge request (MR; on GitHub: pull request) asks the team to take your branch into develop. Its title is the issue's title; its description follows the template – what and why, how you tested it, and Closes #12. Open it early with Draft: in the title if you want feedback before it is ready.
The platform enforces four merge conditions:
- at least one approval from a teammate – never from the author,
- a green pipeline (from M3; until then the setting accepts skipped pipelines),
- every review thread resolved,
- no conflict with
develop– if there is one, the author resolves it, not the reviewer.
After the approval, the author merges, because the author knows whether everything is done. The merge method is a merge commit – no squash, no fast-forward (Fig. 4).
With merge commits, deleting the source branch after the merge loses nothing – the commits are in develop – so GitLab is set to delete it automatically. How to write and answer a review is in the code review guide.
Common mistakes
Three patterns show up every semester, and all three are visible in the commit graph (Fig. 5).
What the M2 demo shows
Show the graph in the demo. A minute on the repository – the board, the MR list, the network graph – tells the instructor more about the process than any slide.
Summary
- main = released, develop = integration; both protected. Work happens on one short-lived branch per issue.
- Branch: feature/ABC-12-short-title. Commit: ABC-12: imperative summary – the hooks add and check the prefix.
- Every piece of work is an issue with a template, a milestone, one assignee and a type label.
- Merge requests need a teammate's approval, a green pipeline and resolved threads; the author merges with a merge commit.
- Avoid: everything on master, branches named after people, one person committing for the team.