
Git Best Practices
- Git Best Practices
- Prerequisites
- What Is Git
- Key Features of Git
- Getting Started
- Setting Up A New Repository
- Basic Git Commands
- Best Practices for Using Git
- Miscellaneous Tips
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
- Download and install Git from git-scm.com
- Use the default installation configuration
- Verify installation by running
git --versionfrom 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
- Navigate to project directory
- Initialize the repository
git init
- Add files to the repository
git add .
- Commit the changes
git commit -m "Initial Commit"
Cloning an Existing Repository
- Copy the clone command
- Open a ps1 terminal or use the terminal in VS Code
- Navigate to the directory where the local copy of the repository will be kept
- 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
mainbranch - 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
mainbranch 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 statefeaturebranches: A holding place for changes to go through final testing before they are deployed tomain. 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:
- Update your local copy of the
mainbranch (git checkout main && git pull origin main) - Switch to your
featurebranch and pull in updates (git checkout feat2 && git pull origin main) -
Sort out any merge conflicts via the VS Code UI or by inspecting files and looking for values like
<<<<<<< HEAD,=======, and>>>>>>> -
The changes in the current branch are shown between
<<<<<<< HEADand======= - The incoming changes are shown between
=======, and>>>>>>> - 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.
- 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
- All repositories should include a
.gitignorefile to exclude things like, but not limited to: - secrets (
.env) - local configurations (
config.json) - node modules (
node_modules) - virtual environments (
.venv)