Skip to content

alt text

Git Best Practices

Prerequisites

  • Ability to elevate privileges, i.e. local admin access
  • Git installed

What Is Git

Git is a distributed version control system that helps teams manage changes to code and other files. It allows multiple developers to work on a project simultaneously without overwriting each other’s changes. Git tracks changes, stores version history, and supports branching and merging to facilitate collaboration.

Key Features of Git

  • Distributed Nature: Each developer has a complete copy of the repository, including the full history.
  • Branching and Merging: Enables developers to work on features independently and merge changes seamlessly.
  • Version History: Tracks every change made to files, providing accountability and easy rollback options.
  • Collaboration Tools: Supports pull requests and code reviews to improve teamwork.

Getting Started

Installation

  1. Download and install Git from git-scm.com
  2. Use the default installation configuration
  3. Verify installation by running git --version from the command line

Initial Configuration

Run the following commands to configure your Git user details

# Set your name
git config --global user.name "Your Name"

# Set your email
git config --global user.email "your email address"

# Optional: Set a preferred text editor
git config -- global core.editor "code --wait" # For VS Code

Setting Up A New Repository

Creating A New Local Repository

  1. Navigate to project directory
  2. Initialize the repository
git init
  1. Add files to the repository
git add .
  1. Commit the changes
git commit -m "Initial Commit"

Cloning an Existing Repository

  1. Copy the clone command
  2. Open a ps1 terminal or use the terminal in VS Code
  3. Navigate to the directory where the local copy of the repository will be kept
  4. Enter the following command to clone a remote repository
git clone COPIED_GIT_REPO_FROM_STEP_ONE

Basic Git Commands

As a reference, you may refer to the GitHub Git Cheat Sheet

Working with Changes

  • Check repository status:

ps1 git status

  • Adding changes to staging:
git add FILENAME # Add single file
git add . # Add all changes
  • Commit changes:
git commit -m "Commit message"

Viewing History

  • Show commit log:
git log
  • Show changes in files:
git diff

Working with Branches

  • Create a new branch:
git branch <branch_name>
  • Switch to a branch:
git checkout <branch_name>
  • Squash merge a branch
git merge --squash <branch_name>

Interacting with Remote Repository

  • Add remote repository
git remote add origin https://github.com/username/repository.git
  • Push changes to remote
git push origin <branch_name>
  • Pull changes from remote:
git pull origin <branch_name>

Best Practices for Using Git

Commits

  • Always strive to make atomic commits, i.e. keep commits small and focused on a single change or feature
  • Commit often: Regularly save progress to make it easier to debug and revert changes if necessary
  • Write clear commit messages: Use meaningful messages that describe the change succinctly. If you wish to provide a detailed description, you can use the following command. "Title" is used in the commit graph and "Detailed description" is only visible when viewing the commit details.
git commit -m "Title" -m "Detailed description"

Branching

  • Use GitHub Flow Branching Strategy: Create a new branch for each feature or bug fix
  • Create a new branch (Feature or Bugfix)
  • Make the desired changes on the new branch
  • Create a pull request to merge changes in to the main branch
  • Address any review comments
  • Squash merge pull request
  • Delete the branch
  • Follow A Naming Convention: Use descriptive names like <your-initials>/feat/<ticket-id>-<title> or <your-initials>/fix/<ticket-id>-<title>
  • The main branch will make use of protections to prevent direct check-ins, only merges from branches will be allowed

We use trunk-based development with a single main branch. Upon the release of a new version, the current code in the main branch is tagged as a release in GitHub with a unique version number. This version is assumed to be a fully-functional set of code and artifacts at the time of release.

  • main: The production-ready codebase that can be deployed by a user to immediately use the application in its current state
  • feature branches: A holding place for changes to go through final testing before they are deployed to main. Represents in-progress work.

Feature Workflow

The general flow is that you git checkout a branch from main to work on a single new feature in the code. That feature will be merged into main upon the approval of a pull request from feature --> main. All commits will be squashed to prevent a long set of commits from clogging up the main tree and rebase errors. When enough features have accumulated into main to warrant a new release of code to others, the code will be tagged with a version number and published on GitHub.

The main branch has controls put in place so you cannot push to it directly. Instead, follow this flow as best as you can. Here is a diagram that shows what that looks like:

---
title: Normal Workflow
---
gitGraph
 commit id: "Initial"
  branch feature1
  checkout feature1
    commit id: "feat1/change1"
    commit id: "feat1/change2"
  checkout main
  merge feature1 id: "Main ⬅️ Feature 1"
  branch feature2
  checkout feature2
    commit id: "feat2/change1"
    commit id: "feat2/change2"
  checkout main
  branch feature3
    commit id: "feat3/change1"
    commit id: "feat3/change2"
  checkout main
  merge feature3 id: "Main ⬅️ Feature 3"
  checkout feature2
    merge main id: "Feature 2 ⬅️ Main"
    commit id: "feat2/change3"
  checkout main
  merge feature2 id: "Main ⬅️ Feature 2"

Normally, multiple features and hotfixes will be worked simultaneously. In this case, it is important to only merge one branch at a time into and let other developers know when main has been updated. When that happens, the other feature branches must be updated to stay current with main. To apply updates:

  1. Update your local copy of the main branch (git checkout main && git pull origin main)
  2. Switch to your feature branch and pull in updates (git checkout feat2 && git pull origin main)
  3. Sort out any merge conflicts via the VS Code UI or by inspecting files and looking for values like <<<<<<< HEAD, =======, and >>>>>>>

  4. The changes in the current branch are shown between <<<<<<< HEAD and =======

  5. The incoming changes are shown between =======, and >>>>>>>
  6. At the end, none of those markers should be present, however you decide to resolve the conflicts

Collaboration

  • Pull Before Pushing: Always fetch and merge the latest changes from the remote repository before pushing
git pull origin <branch_name>
  • Use Pull Requests: Pull Requests (PR's) will be enforced to merge branches in to main. PR's will be used to as an opportunity to conduct a code review as well as ensure changes are conforming to the prescribed architecture
  • Resolve Conflicts Early: Address merge conflicts as soon as they arise
  • Use Revert rather that Reset: If a commit needs to be rolled back, use revert to create a new commit and then push that commit. This is the safest way to rollback without causing harm to teammates.
  • IF, AND ONLY IF, you are the only one using the repository, a Reset may be used
# Revert Usage
git revert <commit hash>
git pull

# Reset Usage
git reset --hard <commit hash>
git push --force

Tags

Use tags to mark release points in the repository. Take care to ensure that the code is in a working state. Tags will be used to revert to previous working versions of software. Follow the guide to performing software versioning.

git tag -a v1.0.0 -m "Version 1.0.0 release"
git push origin --tags

Miscellaneous Tips

  • Avoid using rebase in favor of squash merges
    • Reasoning: Squash merging keeps things simpler for an enterprise team like ours where multiple people may be contributing to an application. We use releases to return to previous working versions rather than git revert.
  • All repositories should include a .gitignore file to exclude things like, but not limited to:
  • secrets (.env)
  • local configurations (config.json)
  • node modules (node_modules)
  • virtual environments (.venv)