Inquiry Question 1: How are large-scale software solutions developed and managed?
Use version control to manage source code, including commits, branches, merges, pull requests and remote repositories
A focused answer to the HSC Software Engineering Module 4 dot point on Git. Commits, branches, merges, pull requests, the worked feature-branch workflow, and the traps markers look for.
Reviewed by: AI editorial process; not yet individually human-reviewed
Have a quick question? Jump to the Q&A page
What this dot point is asking
NESA wants you to use Git for source control: create commits, work on branches, push to a remote, open pull requests, and merge. You should be able to describe the feature-branch workflow that almost every professional team uses.
The answer
Why version control
- History: every change is recorded, with the author, timestamp and message.
- Collaboration: many developers can work on the same project without overwriting each other.
- Branching: experimental work lives separately from the main code.
- Rollback: if a change breaks something, revert to a previous state.
- Bisect: when a bug appears, Git can binary search through history to find which commit introduced it.
Git is by far the most common version control system. GitHub, GitLab and Bitbucket are hosted Git services.
Core concepts
- Repository (repo): the project, including all its history.
- Commit: a snapshot of the project at a moment in time. Identified by a SHA-1 hash. Each commit points to its parent.
- Branch: a movable pointer to a commit. The default branch is usually
main(ormaster). - HEAD: the commit you currently have checked out.
- Remote: a copy of the repo hosted elsewhere (GitHub).
- Pull request (PR) / Merge request (MR): a proposal to merge a branch into another, reviewed before being accepted.
The standard workflow
The feature-branch graph below shows how a branch diverges from main, accumulates commits, and is merged back. The merge commit on main has two parents.
# Start: clone the remote repo
git clone https://github.com/team/project.git
cd project
# Create a branch for your feature
git checkout -b feature/add-login
# Make changes, then stage and commit them
git add login.py templates/login.html
git commit -m "Add login form with bcrypt verification"
# Make more changes, more commits...
git add tests/test_login.py
git commit -m "Add tests for login flow"
# Push the branch to the remote
git push -u origin feature/add-login
# Open a pull request on GitHub.
# After review and approval, merge into main.
# Sync local main and clean up
git checkout main
git pull
git branch -d feature/add-login
Commit messages
A good commit message explains the why, not just the what. Format:
Add login form with bcrypt verification
Previously, login compared passwords as plain text strings, which
left the database vulnerable to a credential dump. This commit
hashes passwords with bcrypt at signup and uses checkpw at login.
Existing users will need to reset their passwords on next login.
The first line is the subject (50 chars max). A blank line, then the body wrapped at 72 chars.
Merging strategies
When a pull request is merged, three options:
- Merge commit: creates a new commit on main that has both parents. Preserves the branch history exactly.
- Squash and merge: combines all commits on the branch into a single commit on main. Cleaner history, loses fine-grained detail.
- Rebase and merge: replays the branch's commits on top of main as individual commits. Linear history, requires care.
Most teams use squash-and-merge for clean main history.
Conflict resolution
When two branches change the same lines, Git cannot merge automatically. It marks the conflict in the file:
<<<<<<< HEAD
return "v2"
=======
return "v3"
>>>>>>> feature/new-version
You edit the file to pick the correct content, then:
git add conflicted_file.py
git commit
.gitignore
Some files should never be committed: build artefacts, secrets, IDE settings, node_modules. List patterns in .gitignore:
# Python
__pycache__/
*.pyc
venv/
# Secrets
.env
config/secrets.yml
# IDEs
.idea/
.vscode/
# Build output
dist/
build/
Pull requests in detail
A pull request triggers:
- Automated checks (test runner, linter, type checker, security scanner).
- Human review (the team approves or requests changes).
- Optional protections (require at least N approvals, require all checks to pass, require up-to-date with main).
Only after all checks pass and reviews are approved can the PR merge. This is the gate that keeps main in a working state.
Tags and releases
A tag marks a specific commit (typically a release):
git tag -a v1.0.0 -m "Initial public release"
git push --tags
Worked example: a team of four
Four developers work on the same code base.
- Each clones the repo locally:
git clone .... - Each works on their own feature branch:
git checkout -b feature/their-task. - They commit small, focused changes throughout the day.
- At the end of a unit of work, they
git push -u origin feature/their-taskand open a PR. - The other three review the PR, leave comments, request changes.
- CI runs the test suite on the PR.
- Once approved and passing, the PR is squashed-and-merged.
- The developer pulls main (
git checkout main && git pull) and deletes the merged branch (git branch -d feature/their-task).
Conflicts arise when two PRs change the same lines. The second PR to merge has to update from main and resolve the conflict locally before merging.
Exam-style practice questions
Practice questions written in the style of NESA exam questions on this dot point, with worked answer explainers. The year tag is the paper they imitate, not the source.
2025 HSC5 marksDescribe the feature-branch workflow with Git. Identify the role of commits, branches, pull requests and merges, and explain how this workflow supports a team of four developers working in parallel.Show worked answer →
In the feature-branch workflow, the main branch always holds working, deployable code. New work is done on short-lived feature branches.
A developer starts by creating a branch off main: git checkout -b feature/add-login. They make small commits as they go, each capturing one logical change with a message explaining the why. A commit is a snapshot of the project at a moment in time, with a unique hash and a parent pointer.
When the feature is ready, they git push the branch to the remote (GitHub, GitLab, Bitbucket) and open a pull request (PR). A PR is a proposal to merge the branch into main. Other team members review the diff, leave comments, and request changes. Automated checks run on the PR: tests, linters, security scans.
Once approved and passing checks, the PR is merged into main. The merge creates a new commit on main that incorporates the changes from the branch.
For four developers working in parallel:
- Each works on their own branch, so they cannot break each other's work in progress.
- Pull requests catch bugs before they reach main.
- Code review spreads knowledge across the team.
- Conflicts are surfaced at merge time, not when committing, and Git's three-way merge resolves most automatically.
Markers reward the four operations defined correctly, the feature-branch flow named, and at least one team-collaboration benefit (parallel work, code review, automated checks, bisectable history).
Practice questions
Original practice questions graded from foundation to exam level, each with a full worked solution. Try them before revealing the solution.
foundation3 marksDefine, in one sentence each, a commit, a branch and a remote repository.Show worked solution →
Commit: a snapshot of the entire project at a point in time, uniquely identified by a hash and linked to its parent commit(s).
Branch: a movable pointer to a commit, allowing an independent line of development that does not affect other branches until merged.
Remote repository: a copy of the repository hosted on a server (such as GitHub) that developers push to and pull from to share history.
Marking criteria: 1 mark for each correct definition; a definition that only names the term without describing what it does or represents does not earn the mark.
foundation3 marksWrite the sequence of Git commands needed to create a new branch called `feature/search`, stage a modified file `search.py`, and commit it with the message "Add keyword search".Show worked solution →
git checkout -b feature/search
git add search.py
git commit -m "Add keyword search"
Marking criteria: 1 mark for correctly creating and switching to the branch, 1 mark for correctly staging the file, 1 mark for a correctly formed commit command with a message.
core4 marksA developer runs `git log --oneline` on their feature branch and sees this output (most recent first):
```
a3f9e21 (HEAD -> feature/reports, origin/feature/reports) Add CSV export
c71b8d4 Fix rounding in totals
e05a112 Add unit tests for report totals
9c44f70 (main) Generate monthly report skeleton
```
Using this output, (a) identify which commit the branch diverged from main at, and (b) explain what the developer should do next to get this work merged into main, naming the two Git/GitHub features involved.
Show worked solution →
(a) Divergence point. The branch diverged from main at commit 9c44f70, the last commit shown with (main) next to it; the three commits above it (e05a112, c71b8d4, a3f9e21) exist only on feature/reports.
(b) Next steps. The branch has already been pushed to the remote, shown by origin/feature/reports alongside HEAD. The developer should open a pull request on GitHub proposing to merge feature/reports into main. Teammates review the diff and automated continuous integration checks (tests, linters) run against the branch; once approved and passing, the PR is merged into main.
Marking criteria: 1 mark for correctly identifying 9c44f70 as the divergence point, 1 mark for correctly reading that the branch is already pushed to the remote, 1 mark for naming the pull request, 1 mark for naming CI/automated checks and review as the gate before merging.
core4 marksA developer opens a file and sees the following after attempting to merge:
```
<<<<<<< HEAD
TAX_RATE = 0.10
=======
TAX_RATE = 0.15
>>>>>>> feature/tax-update
```
Explain what has happened, and describe the exact steps needed to resolve it and complete the merge.
Show worked solution →
This is a merge conflict: both the current branch (HEAD) and the branch being merged in (feature/tax-update) changed the same line, so Git cannot automatically decide which version is correct and has inserted both, separated by conflict markers.
To resolve it: (1) open the file and decide the correct value, for example keeping TAX_RATE = 0.15 if that is the intended update; (2) delete all three conflict marker lines (<<<<<<<, =======, >>>>>>>) along with the version not being kept; (3) save the file; (4) stage the resolved file with git add <file>; (5) complete the merge with git commit (Git pre-fills a merge commit message).
Marking criteria: 1 mark for correctly identifying it as a merge conflict from two competing changes, 1 mark for explaining why Git could not resolve it automatically, 1 mark for correctly removing markers and choosing content, 1 mark for the correct git add then git commit sequence to finish the merge.
core5 marksCompare merge commit, squash-and-merge, and rebase-and-merge as ways of integrating a pull request into main. For each, state what happens to the branch's commit history.Show worked solution →
- Merge commit
- Git creates a new commit on main with two parents (the previous tip of main and the tip of the feature branch). Every individual commit from the branch stays visible in history exactly as it was made. This gives the most complete record but can leave main's history cluttered with small work-in-progress commits.
- Squash-and-merge
- All commits on the feature branch are combined into a single new commit on main. Main's history stays clean, with one entry per feature, but the fine-grained detail of how the feature was built (individual commit messages, intermediate states) is lost.
- Rebase-and-merge
- Each commit from the feature branch is individually replayed on top of the current tip of main, producing a linear history with no merge commit. This keeps history easy to read in a straight line, but rewrites the commit hashes of the branch, so it must never be done on commits already shared/pushed on a branch others are using.
Marking criteria: 1 mark for correctly describing merge commit's two-parent history, 1 mark for describing squash's single-commit outcome and trade-off, 1 mark for describing rebase's linear replay outcome, 1 mark for a correct trade-off/warning for at least one strategy, 1 mark for overall accuracy and clarity across all three.
exam6 marksA team of five developers is building a web application. Evaluate how a Git-based feature-branch workflow, combined with pull requests and continuous integration, supports the team in producing reliable, maintainable software. Your answer should address both collaboration and code quality.Show worked solution →
This is a 6-mark EVALUATE: markers reward a judgement supported by specific mechanisms, not a list of Git commands.
Model answer.
A feature-branch workflow gives each of the five developers an isolated line of development: git checkout -b feature/x lets a developer make many small, focused commits without touching main or any other developer's in-progress work. This directly supports collaboration, because five people can work on unrelated features simultaneously with no risk of one person's unfinished code blocking or breaking another's.
Reliability is protected at the integration point rather than at every commit. When a branch is ready, the developer pushes it and opens a pull request. This triggers two independent quality gates: automated continuous integration (running the test suite, a linter and often a type checker on every push to the branch) catches regressions and style issues before a human even looks at the code; human code review then catches design problems, missed edge cases and knowledge gaps that automated tools cannot, while also spreading understanding of the codebase across the team rather than leaving it siloed in one developer's head.
Maintainability benefits from the resulting commit history. Because branches are short-lived and PRs are reviewed individually, main accumulates a sequence of reviewed, tested, logically separate changes; if a defect appears later, git bisect can search this history efficiently to find the exact commit responsible, and if a specific change causes trouble it can be reverted independently of unrelated work.
The main risk this workflow does not eliminate is integration conflicts: five people editing overlapping files will still generate merge conflicts, which surface as PR merge time rather than being silently avoided. However, because conflicts are resolved locally by the developer who has full context on their own change, and because Git's three-way merge resolves the large majority of non-overlapping changes automatically, this cost is manageable and far smaller than the cost of five developers editing one shared file with no version control at all.
On balance, the feature-branch workflow with mandatory PRs and CI is highly effective for a five-person team: it enables true parallel work, inserts both automated and human quality checks before code reaches main, and produces a maintainable, bisectable history, at the cost of occasional but manageable merge conflicts.
Marker's note: top-band answers (1) explain the collaboration benefit with a specific mechanism (isolated branches, not just "it lets people work together"), (2) distinguish what CI catches from what human review catches, (3) connect the workflow to a maintainability outcome such as bisect or revert, (4) acknowledge a genuine limitation, and (5) close with an explicit evaluative judgement.
exam5 marksThe `calculate_total()` function in a production application started returning incorrect results sometime in the last 40 commits, but nobody noticed until now. Explain, with example commands, how `git bisect` can be used to find the exact commit that introduced the bug, and explain why this is more efficient than checking each commit in order.Show worked solution →
Using bisect. Start the process, marking a known-good commit and the known-bad current state:
git bisect start
git bisect bad HEAD
git bisect good a1b2c3d # a commit from before the bug appeared
Git checks out a commit roughly halfway between good and bad. The developer tests calculate_total() at that commit and reports the result:
git bisect good # if the function is still correct here
# or
git bisect bad # if the function is already broken here
Git repeats this, each time halving the remaining range of suspect commits, until it identifies the single first commit where the bug appears, then reports it and the process ends with git bisect reset.
Why this is efficient. Checking each of the 40 commits in order (linear search) could require up to 40 tests in the worst case. Bisect performs a binary search: each test eliminates half of the remaining candidates, so finding the culprit among 40 commits takes at most tests rather than 40, a large saving of developer time, especially when each test involves running the application.
Marking criteria: 1 mark for correctly starting bisect with good/bad commits, 1 mark for correctly describing the good/bad reporting loop, 1 mark for identifying that the process ends with the first bad commit found, 1 mark for explaining the binary-search halving mechanism, 1 mark for the efficiency comparison (roughly versus tests).
