Skip to content

Commit

Permalink
updates to git guidance from workshop feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
yantantether committed Jan 3, 2025
1 parent bc4a67c commit 9faea03
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 131 deletions.
12 changes: 0 additions & 12 deletions src/development/coding-naming-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ order: 3
review:
last_reviewed_date: 2023-05-06
review_cycle: ANNUAL
related:
title: Related articles
tag: branching
---
It is important to adopt a naming convention:

Expand Down Expand Up @@ -102,15 +99,6 @@ Terms used in the naming conventions are:
<ul>
<li><code>hotfix</code></li>
<li><code>develop</code></li>
<li><code>build</code></li>
<li><code>ci</code></li>
<li><code>docs</code></li>
<li><code>feature</code></li>
<li><code>fix</code></li>
<li><code>perf</code></li>
<li><code>refactor</code></li>
<li><code>style</code></li>
<li><code>test</code></li>
</ul>
</td>
</tr>
Expand Down
130 changes: 11 additions & 119 deletions src/development/dev-git-branching-strategy.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,16 @@
---
layout: article
title: "Git branching strategy"
description: "Define a branching strategy to avoid confusion and reduce risk"
tags: [dev, branching]
description: "A branching strategy provides a structure for pushing changes through to production"
tags: [git]
order: 8
status: DRAFT
review:
title: Related
last_reviewed_date: 2024-09-20
review_cycle: ANNUAL
---
!!! warning Standard documentation

All projects must define and publish a branching strategy in the project documentation site. See [project documentation](#project-documentation)

!!!

A Git branching strategy is a set of rules to manage and organize code changes in a Git system. A strategy helps to:

* Prevent conflicts: Avoid merge conflicts that can occur when multiple developers work on the same project at the same time
* Increase productivity: Allow multiple developers to work on different features simultaneously
* Reduce versioning management time: Help teams to manage release of code more efficiently

There are [many Git branching strategies](https://graphite.dev/guides/git-branching-strategies) including:

* Git Flow
* Github Flow
* Trunk Based Development

## NHSBSA branching strategy

Our standard branching strategy is based off traditional Git Flow. It defines three types of branch:
Our standard branching strategy is based on traditional Git-flow and defines three types of branch:

* __Production__
* __Release candidate__
Expand All @@ -45,7 +26,7 @@ The `main` branch must contain the exact same code as production. It is created

### Release candidate

A release candidate branch will hold code intended for release into production. Quality controls such as automated acceptance tests must succeed before a release candidate is accepted for release into production.
A release candidate branch will hold code intended for release into production. Quality controls such as automated acceptance tests must succeed before a release candidate is accepted into production.

We use these branch names for release candidates:

Expand All @@ -59,93 +40,17 @@ Projects teams should adopt a naming convention to convey additional information
e.g. `develop-wcag-2-2`

* Changes must not be pushed directly to release candidate branches. Use branch protection to prevent developers from pushing.
* Rebased code may be force pushed by Maintainers only when aligning divergent production code (see [rebasing](#rebasing) below). Rebasing a protected branch will require temporary rule changes.
* Rebased code may be force pushed by Maintainers only when aligning divergent production code (see [rebasing](../dev-git/#rebasing)). Rebasing a protected branch will require temporary change of push rules.

### Change candidate

A change candidate branch is the most common type of branch, used by developers to iterate their change, before [review](../coding-peer-review/) and merge into a release candidate branch.

We adopt this convention for change candidate branch names:

* `build`
Changes that affect the build system or external dependencies (example scopes: maven, npm).
* `ci`
Changes to our CI configuration files and scripts (example scopes: Gitlab-CI, Azure Devops)
* `docs`
Documentation only changes
* `feature`
A new feature
* `fix`
A bug fix
* `perf`
A code change that improves performance
* `refactor`
A code change that neither fixes a bug nor adds a feature
* `style`
Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* `test`
Adding missing tests or correcting existing tests

Branch names must follow our [naming conventions](../coding-naming-conventions/) to include change ID and description. e.g. `feature/LIS-1234-add-back-button`

## Controls

Git repository hosting providers provide various features to add additional controls on what can be done. These must be configured to protect the branch and release process.

* Prevent unauthorised push to production and release branches
* Prevent unapproved merge of code into the release process
* Prevent non-compliant commits and branch names

::: details Gitlab configuration

* Under `Settings` > `Repository` > `Protected branches`:
* Protect named `main` branch, and wildcard `develop*` and `hotfix*`
* Allowed to merge: `Developers` and `Maintainers`
* Allowed to push and merge: `No-one`
* Allowed to force push: `No`
* Under `Settings` > `Repository` > `Branch rules`:
* Add rule for `All protected branches`
* Add approval rule
* Required approvals at `1` as a minimum. Projects may choose more approvers.
* Under `Settings` > `Merge requests`
* Merge method: `Fast-forward merge`
* Under `Settings` > `Push rules` > `Branch name`
* Branch name regex: `(main|develop|hotfix)|(build|ci|docs|feature|fix|perf|refactor|style|test)[\-\/]([A-Z]{3,4}|NO-JIRA)`

:::
Change candidate branch names follow our [naming conventions](../coding-naming-conventions/) to include change ID and description:

## Rebasing
* `{change ID}-{brief description}`

Rebasing is a way to tidy up the Git commit history. It involves rewriting commits in various ways:

* Fixing merge conflicts: Rebasing can alter changes in a branch to avoid conflict with changes in another branch.
* Cleaning up project history: Rebasing can create a cleaner project history by combining multiple commits into one.
* Editing commit messages: Rebasing can be used to edit previous commit messages.
* Deleting or reverting commits: Rebasing can be used to delete or revert commits that are no longer necessary

!!! warning Proceed with caution
Rebasing will change the Git commit graph and has potential to create an inconsistent repository that is difficult to recover from. Rebase with caution and consult your professional lead if you are unsure of what you are doing.

Whenever a shared branch is rebased, the entire team needs to be notified. Every team member will need to reset their local branch to the rewritten remote:

e.g.

```bash
git fetch --all
git reset --hard <branch> origin/<branch>
```

!!!

### Rebase or merge

We recommend that project teams rebase their branches against the upstream branch whenever they diverge. This provides a more streamlined and understandable git history, and simplifies squashing.

Merging from an upstream branch on the other hand makes squashing and reverting change more difficult.

### Squashing

We recommend that developers squash commits into logical commits for change candidate branches prior to merge into a release candidate branch. This removes unneccesary commits that can confuse and provides a clean and logical git history.
e.g. `LIS-1234-add-back-button`

## Workflow

Expand All @@ -155,7 +60,6 @@ We recommend that developers squash commits into logical commits for change cand
* Imported repositories:
* may have adopted the non-standard `master` branch. This should be changed to `main`
* must import into an empty repository in the Git hosting provider. Merging a repository into a pre-existing `main` will cause confusion in the git history
* [Controls](#controls) must be applied as above

### Release Candidate

Expand All @@ -171,17 +75,5 @@ We recommend that developers squash commits into logical commits for change cand

* Developer creates a new change candidate branch, `feature-xyz` branch from the newly created `develop-xyz` branch
* When the feature changes are complete they raise a merge request for [review](../coding-peer-review/)
* The changes are merged (ideally [commits squashed](#squashing)) in to the `develop` branch. The change candidate branch should be deleted
* The _change_ -> _release_ cycle is repeated until all the changes relating the release are stable and tested

## Project documentation

All projects must document their git branching and release strategy. Deviation from standards must also be documented along with decision records explaining the reason for deviation.

Documentation should define:

* Root git branching strategy. e.g. nhsbsa, git-flow, github-flow, trunk-based-development, other
* Branch naming convention
* Rebasing approach including rebase vs merge commits, and squashing
* Branch protection
* Approval rules
* The changes are merged (ideally [commits squashed](../dev-git/#squashing)) in to the `develop` branch. The change candidate branch should be deleted
* The _change_ -> _release_ cycle is repeated until all the changes relating to the release are stable and tested
193 changes: 193 additions & 0 deletions src/development/dev-git.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
layout: article
title: "Git"
description: "Define how your team use Git to avoid confusion and reduce risk"
tags: [dev]
order: 8
status: DRAFT
related:
title: Related articles
tag: git
review:
last_reviewed_date: 2024-09-20
review_cycle: ANNUAL
---
Teams should be autonomous and adopt tools and processes that fit their circumstances, however we have these non-negotiable standards that must be adhered to:

* __Project documentation__
All projects must document their [branching strategy](#branching-strategy), [git usage](git-usage) and [controls](#controls). Changes from our standard approaches must be held as decision records with a rationale for the deviation.
* __Main is production__
The `main` branch must always reflect the current release, running in production. Keeping `main` as production makes it easier to hotfix urgent changes, such as fixing zero-day exploits.
* __Change attribution__
Code changes must contain the [author](#git-configuration) and [issue ID](#conventional-commits) such as a Jira ticket number.
* __Release by SemVer Tag__
Released versions of code must be identified by a Git tag on the commit that was quality assured. Release tags must follow the [Semantic Versioning standard 2.0.0](https://semver.org/spec/v2.0.0.html). Pre-release semantic versions must not be released into production.
* __Assure quality before release__
Before a codebase is tagged for released, it must be assured as meeting requirements:
* [Peer review](../coding-peer-review/)
* [Quality assurance](../coding-quality-assurance/)
* [Testing](../../testing/test-frameworks/)
* __Binary dependencies are not held in source control__
Package managers use manifest files to declare dependencies, resolved at build time. We also benefit from software composition analysis tools that check for vulnerabilities.

## Branching Strategy

A branching strategy is a set of rules to manage and organize code changes in a Git system. A strategy helps to:

* Prevent conflicts: Avoid merge conflicts that can occur when multiple developers work on the same project at the same time
* Increase productivity: Allow multiple developers to work on different features simultaneously
* Reduce versioning management time: Help teams to manage release of code more efficiently

There are [many Git branching strategies](https://graphite.dev/guides/git-branching-strategies) including:

* Git-flow
* GitHub-flow
* Trunk Based Development

We recommend using our [standard branching strategy](../dev-git-branching-strategy/), based on git-flow.

## Git usage

### Git configuration

Ensure your local Git configuration is correct.

* __Identity__
Ensure that your committer identity (name and email) is the same as your Git hosting provider identity (Gitlab, Github, Azure).
If you are working on an Open Source project, you may choose to remain anonymous by using a cipher identity.
* __Alias__
Optimise Git usage by providing an alias complex git incantations.

::: details Suggested alias configuration:

```text
[alias]
cp=cherry-pick
co=checkout
br=branch
lg=log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)'
so=show --pretty='parent %Cred%p%Creset commit %Cred%h%Creset%C(yellow)%d%Creset%n%n%w(72,2,2)%s%n%n%w(72,0,0)%C(cyan)%an%Creset %Cgreen%ar%Creset'
```

:::

### Conventional commits

We recommend adopting [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) to structure commit messages.

```html
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
```

* The commit header should contain the type of change, ticket reference and a short description

```text
feat: LIS-123 adds back button
```

* The optional body should contain details of specific changes if required
* The optional footer should contain [git-trailer](https://git-scm.com/docs/git-interpret-trailers) formatted meta-information

::: details Conventional commit types

* `build`
Changes that affect the build system or external dependencies (example scopes: maven, npm).
* `ci`
Changes to the CI configuration files and scripts (example scopes: Gitlab-CI, Azure Devops)
* `docs`
Documentation only changes
* `feat`
A new feature
* `fix`
A bug fix
* `perf`
A code change that improves performance
* `refactor`
A code change that neither fixes a bug nor adds a feature
* `style`
Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). We always recommend separating formatting changes from functional changes
* `test`
Adding missing tests or correcting existing tests

:::

### Issue trackers

Git hosting providers often integrate with issue tracking services. Use commit messages to automatically post updates or transition tickets:

::: details Gitlab to Jira

* [Gitlab Jira integration](https://docs.gitlab.com/ee/integration/jira/)
On Merge Request creation, a Jira ticket ID in the branch name, commit message or MR title will link the ticket to the MR

:::

### Rebasing

Rebasing is a way to tidy up the Git commit history. It involves rewriting commits for a mix of purposes:

* Fixing merge conflicts: Rebasing can alter changes in a branch to avoid conflict with changes in another branch.
* Cleaning up project history: Rebasing can create a cleaner project history by combining multiple commits into one.
* Editing commit messages: Rebasing can be used to edit previous commit messages.
* Deleting or reverting commits: Rebasing can be used to delete or revert commits that are no longer necessary

!!! warning Rebase with caution
Rebasing will change the Git commit graph and has potential to create an inconsistent repository that is difficult to recover from. Rebase with caution and consult your professional lead if you are unsure of what you are doing.

Whenever a shared branch is rebased, the entire team must be notified. Every team member will need to reset their local branch to the rewritten remote:

e.g.

```bash
git fetch --all
git reset --hard <branch> origin/<branch>
```

!!!

### Squashing

We recommend that developers squash commits into logical commits for change candidate branches prior to merge into a release candidate branch. This removes unneccesary commits that can confuse, providing a clean and logical git history. Use interactive rebase to take control of squashing, and the resulting commit messages.

Git hosting providers often support squashing on merge. We recommend against this. Automating removes control from authors on the resulting commit message. There is a risk that detail is lost from the squashed commits, or too much needless information is included. There is a further risk that issue IDs are lost, removing audit.

Git commits must never be squashed automatically when merging into `main`. Change is only allowed into `main` after it has been tested in a pre-production environment. Squashing will change the HEAD commit, rendering the tests invalid.

### Rebase or merge

We recommend that project teams rebase their branches against the upstream branch whenever they diverge. This provides a more streamlined and understandable git history, and simplifies squashing.

Using a merge commit from an upstream branch makes squashing and reverting change more difficult.

We advise squashing unecessary commits before a rebase. This simplifies conflict resolution.

## Controls

Git repository hosting providers support adding controls on what can be done with a hosted Git repository. These must be configured to protect the branch and release process.

* Prevent unauthorised push to production and release branches
* Prevent unapproved merge of code into the release process
* Prevent non-compliant commits and branch names

::: details Gitlab configuration

* Under `Settings` > `Repository` > `Protected branches`:
* Protect named `main` branch, and wildcard `develop*` and `hotfix*`
* Allowed to merge: `Developers` and `Maintainers`
* Allowed to push and merge: `No-one`
* Allowed to force push: `No`
* Under `Settings` > `Repository` > `Branch rules`:
* Add rule for `All protected branches`
* Add approval rule
* Required approvals at `1` as a minimum. Projects may choose more approvers.
* Under `Settings` > `Merge requests`
* Merge method: `Fast-forward merge`
* Under `Settings` > `Push rules` > `Branch name`
* Branch name regex: `(main|develop|hotfix)|(build|ci|docs|feature|fix|perf|refactor|style|test)[\-\/]([A-Z]{3,4}|NO-JIRA)`

:::

0 comments on commit 9faea03

Please sign in to comment.