Salesforce · · 29 min read

Version Control for Salesforce Developers

Getting started with Git and GitHub for Salesforce development — installing Git, setting up local and remote repos, essential commands, branching strategies, and best practices.

Part 57: Version Control for Salesforce Developers

Welcome back to the Salesforce series. We have spent dozens of posts learning how to build things — triggers, classes, integrations, flows, and more. But there is one foundational practice that separates professional development teams from those who are constantly firefighting, and that is version control.

If you have ever accidentally overwritten someone else’s changes in a sandbox, lost track of what was deployed and when, or spent hours trying to figure out why a class that worked last week is suddenly broken, you already understand the pain that version control solves. You just might not have had a name for it.

This is Part 57 of the series (Topic 3, Section 21). We are going to cover everything you need to get started with Git and GitHub as a Salesforce developer — from understanding what version control is and why it matters, to installing Git, creating repositories, mastering essential commands, and adopting branching strategies that keep your team sane. By the end, you will have a working version control setup and the knowledge to use it effectively on real projects.


What is Version Control and Why Does It Matter

Version control is a system that records changes to files over time so that you can recall specific versions later. Think of it as an unlimited undo button for your entire project, combined with a detailed history of who changed what, when, and why.

The Problem Without Version Control

In the traditional Salesforce world, many teams work directly in sandboxes. Developer A writes a trigger in the dev sandbox. Developer B writes a different trigger in the same sandbox. Developer A deploys to UAT and accidentally overwrites Developer B’s changes because the change set did not include them. Developer B’s work is gone. Nobody noticed until a week later when QA found the bug.

This is not a hypothetical scenario. This happens every single day on Salesforce teams that do not use version control. The sandbox is not a backup. It is not a history. It is a snapshot of the current state, and when something gets overwritten, it is gone.

Other common problems include not knowing which version of a class is in production versus the sandbox, having no way to roll back a bad deployment, and being unable to tell who made a particular change or why they made it.

What Version Control Gives You

Version control solves all of these problems and more.

Complete history. Every change is recorded. You can see exactly what changed, who changed it, and when. You can read the commit message to understand why the change was made. If a bug is introduced, you can trace it back to the exact change that caused it.

Safe collaboration. Multiple developers can work on the same codebase simultaneously without overwriting each other’s work. Version control systems have mechanisms (branching and merging) that allow parallel work to be combined safely.

Rollback capability. If a deployment breaks something, you can revert to the previous version in seconds. You do not have to manually undo changes or try to remember what the code looked like before.

Code review. Before changes are merged into the main codebase, team members can review them. This catches bugs, enforces coding standards, and spreads knowledge across the team. This is done through pull requests on platforms like GitHub.

Accountability and audit trail. In regulated industries, you need to know who changed what and when. Version control provides this automatically.

Centralized vs Distributed Version Control

There are two main types of version control systems.

Centralized Version Control Systems (CVCS) use a single central server that contains the entire history. Developers check out files from the server, make changes, and check them back in. Subversion (SVN) is the most well-known example. The problem is that if the central server goes down, nobody can commit changes or view history. You are completely dependent on that one server.

Distributed Version Control Systems (DVCS) give every developer a complete copy of the entire repository, including the full history. You can commit, branch, and view history entirely offline. When you are ready, you push your changes to a shared remote repository. Git and Mercurial are distributed systems.

The key advantage of a distributed system is resilience. Every developer has a complete backup. If the server goes down, any developer’s copy can be used to restore it. You can also work offline — on a plane, in a coffee shop with bad WiFi, or during an internet outage. You commit locally and push when you are back online.

Git is the Standard

Git was created by Linus Torvalds in 2005 to manage the Linux kernel source code. It was designed to be fast, support distributed workflows, and handle large projects. Today, Git is the de facto standard for version control across the entire software industry. It is not even close — Git dominates.

For Salesforce developers specifically, Git is essential because:

  • Salesforce DX (SFDX) and the Salesforce CLI are built around source-driven development, which assumes you are using Git.
  • Salesforce DevOps Center integrates directly with Git repositories.
  • CI/CD pipelines for Salesforce deployments use Git as the source of truth.
  • Every major DevOps tool in the Salesforce ecosystem (Copado, Gearset, Flosum, AutoRABIT) uses Git under the hood.

If you are doing Salesforce development in 2026 and you are not using Git, you are already behind. The good news is that Git is free, well-documented, and not as hard to learn as its reputation suggests.


How to Install Git and Set Up Your Local Environment

Let us get Git installed on your machine and configure it properly.

Installing Git on macOS

macOS often comes with Git pre-installed through the Xcode Command Line Tools. Open Terminal and type:

git --version

If Git is installed, you will see something like git version 2.44.0. If it is not installed, macOS will prompt you to install the Xcode Command Line Tools, which include Git.

Alternatively, you can install Git through Homebrew, which is the recommended approach because it keeps Git updated to the latest version:

brew install git

After installation, verify it with git --version again.

Installing Git on Windows

On Windows, download the installer from the official Git website at git-scm.com. Run the installer and accept the default settings — they are sensible for most users. The installer includes Git Bash, which gives you a Unix-like terminal on Windows. You can also use Git from PowerShell or the Windows Command Prompt after installation.

If you use Windows Package Manager (winget), you can install Git from the command line:

winget install --id Git.Git -e --source winget

Installing Git on Linux

On Debian/Ubuntu-based systems:

sudo apt-get update
sudo apt-get install git

On Fedora:

sudo dnf install git

On Arch Linux:

sudo pacman -S git

Verifying the Installation

Regardless of your operating system, confirm Git is installed correctly:

git --version

You should see the version number printed. If you see an error, the installation did not succeed or Git is not in your system PATH.

Configuring Git — Your Identity

Before you make any commits, you must tell Git who you are. This information is attached to every commit you make and is how your team knows who made which change.

git config --global user.name "Your Full Name"
git config --global user.email "your.email@example.com"

Use the same email address that you will use for your GitHub account. The --global flag means these settings apply to all Git repositories on your machine. If you need different settings for a specific project (for example, a personal email for personal projects), you can run the same commands without --global inside that project’s directory.

You can verify your configuration at any time:

git config --list

This will show all your Git settings, including your name and email.

Additional Useful Configuration

Set your default branch name to main (Git’s default used to be master, but the industry has largely moved to main):

git config --global init.defaultBranch main

Set your preferred text editor for commit messages. If you like VS Code:

git config --global core.editor "code --wait"

Enable colored output in the terminal for better readability:

git config --global color.ui auto

Setting Up .gitignore for Salesforce Projects

A .gitignore file tells Git which files and directories to ignore — files that should not be tracked or committed. This is critical for Salesforce projects because your local project will contain files that are machine-specific, generated, or sensitive.

For an SFDX project, create a .gitignore file in the root of your project with at least the following:

# Salesforce-specific
.sfdx/
.sf/

# OS-specific
.DS_Store
Thumbs.db

# IDE-specific
.vscode/
.idea/
*.sublime-*

# Node modules (if using LWC or npm packages)
node_modules/

# Logs
*.log
npm-debug.log*

# Environment and local config
.env
.env.local

# Compiled output
deploy/

The .sfdx/ and .sf/ directories contain local authentication tokens and scratch org information. You absolutely do not want to commit these — they contain secrets and they are specific to your machine. The node_modules/ directory can be enormous and is always recreated from package.json using npm install, so there is no reason to track it.

We will discuss more .gitignore best practices in the Section Notes at the end.


How to Set Up a Remote Repository on GitHub

Git works locally on your machine, but to collaborate with others and have a backup in the cloud, you need a remote repository. GitHub is the most popular platform for hosting Git repositories, and it is what Salesforce’s own DevOps Center integrates with.

Creating a GitHub Account

If you do not already have a GitHub account, go to github.com and sign up. The free tier is more than sufficient for most use cases — it includes unlimited public and private repositories, GitHub Actions for CI/CD, and collaboration features.

Choose a professional username. Your GitHub profile will become part of your developer identity, so something like jsmith-dev is better than xXx_c0d3r_xXx.

Creating a New Repository on GitHub

Once logged in, click the + icon in the top right corner and select “New repository.” Fill in the details:

  • Repository name: Something descriptive like my-salesforce-project or account-management-app.
  • Description: A brief explanation of what the project does.
  • Public or Private: Private if it is work-related or contains proprietary code. Public if it is a personal learning project you want to share.
  • Initialize with a README: Check this if you are starting a new project. Skip it if you are pushing an existing local project.
  • Add .gitignore: GitHub offers templates. You can select one for Salesforce or add your own later.

Click “Create repository” and GitHub will show you instructions for connecting a local repository.

Connecting Your Local Repository to GitHub

If you already have a local Git repository, you connect it to GitHub with:

git remote add origin https://github.com/yourusername/your-repo-name.git

The word origin is a conventional name for your primary remote repository. You can call it anything, but origin is the standard that every tutorial and tool expects.

Verify the remote was added:

git remote -v

This will show you the URLs for fetching and pushing.

If you want to clone an existing GitHub repository to your local machine instead:

git clone https://github.com/yourusername/your-repo-name.git

This creates a local copy of the repository with the remote already configured.

SSH vs HTTPS Authentication

When you interact with GitHub (pushing, pulling, cloning private repos), you need to authenticate. There are two main methods:

HTTPS is the simpler option. You use your GitHub username and a Personal Access Token (not your password — GitHub no longer accepts passwords for Git operations). The URL looks like https://github.com/user/repo.git.

SSH uses a cryptographic key pair. You generate a public/private key pair on your machine, upload the public key to GitHub, and Git uses the private key to authenticate automatically. The URL looks like git@github.com:user/repo.git. SSH is more convenient once set up because you do not need to enter credentials for every push and pull.

For most beginners, HTTPS with a Personal Access Token is the easiest way to start. As you become more comfortable, SSH is worth setting up for the convenience.

Setting Up a Personal Access Token (PAT)

Since GitHub no longer accepts passwords for Git operations over HTTPS, you need a Personal Access Token:

  1. Go to GitHub Settings (click your profile picture, then Settings).
  2. Scroll down to “Developer settings” in the left sidebar.
  3. Click “Personal access tokens” and then “Tokens (classic).”
  4. Click “Generate new token.”
  5. Give it a descriptive name like “Salesforce Dev Machine.”
  6. Set an expiration date (90 days is a reasonable balance between security and convenience).
  7. Select scopes — at minimum, check repo for full repository access.
  8. Click “Generate token” and copy the token immediately. You will not be able to see it again.

When Git asks for your password during a push or pull, use this token instead of your GitHub password.

To avoid entering the token every time, you can configure a credential helper:

git config --global credential.helper osxkeychain   # macOS
git config --global credential.helper store          # Linux (stores in plain text)
git config --global credential.helper manager        # Windows (Git Credential Manager)

On macOS, the keychain helper stores your credentials securely. On Windows, Git Credential Manager handles this. On Linux, the store helper saves credentials in a plain text file, which is less secure — consider using cache with a timeout instead if security is a concern.

If you prefer SSH authentication:

# Generate a new SSH key pair
ssh-keygen -t ed25519 -C "your.email@example.com"

# Start the SSH agent
eval "$(ssh-agent -s)"

# Add your private key to the agent
ssh-add ~/.ssh/id_ed25519

Then copy your public key:

cat ~/.ssh/id_ed25519.pub

Go to GitHub Settings, then “SSH and GPG keys,” click “New SSH key,” paste the public key, and save. Test the connection:

ssh -T git@github.com

You should see a message confirming successful authentication.


Essential Git Commands

This is the section you will refer back to the most. These are the commands you will use every single day as a Salesforce developer working with Git.

git init — Initialize a New Repository

git init

This creates a new Git repository in the current directory. It adds a hidden .git folder that contains all the version control metadata. Run this once when starting a new project that is not already a Git repo.

For Salesforce projects, you typically run sf project generate first to create the SFDX project structure, and then git init inside that directory.

git clone — Copy an Existing Repository

git clone https://github.com/user/repo.git

This downloads a complete copy of a remote repository, including its entire history, and sets up the remote connection automatically. Use this when joining an existing project.

You can also clone into a specific directory name:

git clone https://github.com/user/repo.git my-local-folder

git status — Check the Current State

git status

This is the command you will run most often. It shows you which files have been modified, which are staged for commit, which are untracked (new files Git does not know about), and which branch you are on. Get in the habit of running git status before every commit to make sure you are committing what you think you are committing.

git add — Stage Changes for Commit

# Stage a specific file
git add force-app/main/default/classes/AccountHandler.cls

# Stage all changes in a directory
git add force-app/

# Stage all changes in the entire project
git add .

# Stage parts of a file interactively
git add -p

Staging is a concept that trips up beginners. When you modify a file, Git notices the change, but it does not automatically include that change in your next commit. You have to explicitly tell Git which changes you want to include by staging them with git add. This gives you control — you can make ten changes but only commit three of them.

Think of staging as putting items in a shopping cart before checking out. You browse, add items to the cart, and when you are ready, you check out (commit) everything in the cart.

git commit — Save a Snapshot

git commit -m "Add AccountHandler trigger to set priority on insert"

A commit is a snapshot of your staged changes. It is saved permanently in your repository’s history. Every commit has a unique identifier (a SHA hash), the author’s name and email, a timestamp, and a message describing what changed.

For longer commit messages with a subject and body:

git commit -m "Add AccountHandler trigger to set priority on insert" -m "When a new Account is created, the trigger evaluates the Annual Revenue field and sets the Priority__c field to High, Medium, or Low accordingly. This replaces the workflow rule that was previously handling this logic."

We will talk about commit message conventions in the Section Notes.

git push — Send Changes to the Remote

# Push to the remote (first time for a new branch)
git push -u origin main

# Subsequent pushes (once tracking is set up)
git push

Push sends your local commits to the remote repository. The -u flag (short for --set-upstream) links your local branch to the remote branch so that future pushes and pulls know where to go.

git pull — Get Changes from the Remote

git pull

Pull downloads changes from the remote repository and merges them into your current branch. It is essentially a git fetch followed by a git merge. Run this regularly to stay up to date with your team’s changes.

git fetch — Download Without Merging

git fetch

Fetch downloads the latest changes from the remote but does not merge them into your working branch. This is useful when you want to see what has changed on the remote without affecting your current work. After fetching, you can inspect the changes and decide when to merge.

git fetch
git log origin/main --oneline

This shows you what commits are on the remote main branch without changing anything in your local branch.

git branch — Manage Branches

# List all local branches
git branch

# List all branches including remote
git branch -a

# Create a new branch
git branch feature/account-trigger

# Delete a branch (only if merged)
git branch -d feature/account-trigger

# Delete a branch (force, even if not merged)
git branch -D feature/account-trigger

Branches are one of Git’s most powerful features. A branch is an independent line of development. You create a branch, make your changes there, and when you are done, merge it back into the main branch. This keeps the main branch stable while you work.

git checkout and git switch — Move Between Branches

# Switch to an existing branch
git checkout feature/account-trigger

# Create a new branch and switch to it
git checkout -b feature/account-trigger

# The modern alternative (Git 2.23+)
git switch feature/account-trigger
git switch -c feature/account-trigger

git checkout is the traditional command for switching branches. Git 2.23 introduced git switch as a cleaner alternative that only handles branch switching (checkout does too many things). Both work fine.

git merge — Combine Branches

# First, switch to the branch you want to merge INTO
git checkout main

# Then merge the feature branch
git merge feature/account-trigger

Merge takes the changes from one branch and applies them to another. If both branches modified different files (or different parts of the same file), Git combines them automatically. If both branches modified the same lines of the same file, you get a merge conflict that you must resolve manually.

git log — View History

# Full log
git log

# Compact one-line format
git log --oneline

# Show the last 5 commits
git log -5

# Show a graph of branches
git log --oneline --graph --all

# Show commits by a specific author
git log --author="Abhishek"

# Show commits that changed a specific file
git log -- force-app/main/default/classes/AccountHandler.cls

The log is your history book. Use it to understand what has happened in the project, find when a specific change was introduced, or review what a teammate has been working on.

git diff — See What Changed

# See unstaged changes
git diff

# See staged changes
git diff --staged

# Compare two branches
git diff main..feature/account-trigger

# See changes in a specific file
git diff force-app/main/default/classes/AccountHandler.cls

Diff shows you the exact lines that were added, removed, or modified. It is invaluable for reviewing your changes before committing.

git stash — Temporarily Shelve Changes

# Stash your current changes
git stash

# List all stashes
git stash list

# Apply the most recent stash
git stash pop

# Apply a specific stash
git stash apply stash@{2}

# Stash with a descriptive message
git stash push -m "WIP: account trigger refactor"

Stash is incredibly useful when you are in the middle of something and need to switch context. Maybe you are working on a feature branch and your manager asks you to fix a production bug immediately. You stash your in-progress work, switch to the hotfix branch, fix the bug, and then come back and pop your stash to continue where you left off.

git reset — Undo Changes

# Unstage a file (keep the changes in your working directory)
git reset HEAD force-app/main/default/classes/AccountHandler.cls

# Unstage all files
git reset HEAD

# Undo the last commit but keep the changes staged
git reset --soft HEAD~1

# Undo the last commit and unstage the changes (keep files modified)
git reset --mixed HEAD~1

# Undo the last commit and discard all changes (DANGEROUS)
git reset --hard HEAD~1

Reset is powerful and potentially dangerous. The --soft and --mixed options are safe — they undo commits but keep your file changes. The --hard option permanently deletes your changes. Use --hard with extreme caution and only when you are certain you want to throw away your work.

git rebase — Rewrite History (Use With Care)

# Rebase your feature branch onto main
git checkout feature/account-trigger
git rebase main

Rebase takes your branch’s commits and replays them on top of the target branch. The result is a cleaner, linear history without merge commits.

When should you use rebase versus merge? The general rule is:

  • Use rebase to update your feature branch with the latest changes from main. This keeps your branch up to date with a clean history.
  • Use merge when you are combining a completed feature branch back into main. This preserves the full history of when branches were created and merged.
  • Never rebase commits that have been pushed to a shared remote branch and that other people are working on. Rebasing rewrites commit history, and if other developers have based their work on the original commits, you will create a mess.

Resolving Merge Conflicts

Merge conflicts happen when two branches modify the same lines of the same file. Git cannot automatically determine which version is correct, so it asks you to resolve the conflict manually.

When a conflict occurs, Git marks the file with conflict markers:

<<<<<<< HEAD
public static void processAccount(Account acc) {
    acc.Priority__c = 'High';
=======
public static void processAccount(Account acc) {
    acc.Priority__c = 'Critical';
>>>>>>> feature/priority-update

The section between <<<<<<< HEAD and ======= is the version in your current branch. The section between ======= and >>>>>>> is the version from the branch you are merging in.

To resolve the conflict:

  1. Open the file and find the conflict markers.
  2. Decide which version to keep, or combine them into a new version.
  3. Remove all the conflict markers (<<<<<<<, =======, >>>>>>>).
  4. Save the file.
  5. Stage the resolved file with git add.
  6. Complete the merge with git commit.

Most code editors (VS Code, IntelliJ) have built-in merge conflict resolution tools that make this process visual and much easier. VS Code highlights conflicts and gives you clickable buttons to accept the current change, the incoming change, or both.

Tip: The best way to deal with merge conflicts is to prevent them. Pull changes from the main branch frequently, keep your branches short-lived, and communicate with your team about which files you are working on.


Section Notes

These notes cover branching strategies, .gitignore best practices, commit message conventions, and a few things you should almost never do.

Branching Strategies

A branching strategy defines how your team uses branches to organize work. There is no single correct strategy — it depends on your team size, release cadence, and workflow.

Feature Branches. This is the simplest and most common strategy. Every new feature or bug fix gets its own branch created from main. When the work is done, it is merged back into main through a pull request. Branch names are descriptive: feature/account-trigger, bugfix/lead-conversion-error, hotfix/production-null-pointer.

Feature branches work well for most Salesforce teams. They are easy to understand, keep the main branch stable, and integrate naturally with pull request reviews on GitHub.

GitFlow. GitFlow is a more structured strategy that uses multiple long-lived branches: main (production code), develop (integration branch), feature/* branches, release/* branches, and hotfix/* branches. Features are merged into develop, and when a release is ready, a release branch is created from develop, tested, and then merged into both main and develop.

GitFlow is well-suited for teams with scheduled releases, which is common in Salesforce. If your team deploys to production on a fixed schedule (every two weeks, once a month), GitFlow gives you a clear process for managing releases. However, it is more complex and can feel heavy for small teams.

Trunk-Based Development. In trunk-based development, there is one main branch (the trunk) and developers commit to it frequently — often multiple times per day. Feature branches exist but are very short-lived (hours, not weeks). This strategy requires a mature CI/CD pipeline, automated testing, and feature flags to hide incomplete work.

Trunk-based development is the fastest strategy but requires the most discipline and tooling. It is common in high-performing engineering teams but can be challenging for Salesforce projects where deployment pipelines are slower.

My recommendation for most Salesforce teams: Start with feature branches. Keep branches short-lived (days, not weeks). Require pull request reviews before merging to main. This gives you 90% of the benefit with 10% of the complexity.

.gitignore Best Practices for SFDX Projects

Beyond the basics I covered earlier, here are additional recommendations for your .gitignore:

Always ignore .sfdx/ and .sf/ directories. These contain org authentication tokens and scratch org details. Committing them is a security risk and they do not work on other people’s machines anyway.

Ignore IDE configuration files unless your entire team uses the same IDE with the same settings. The .vscode/ directory can optionally be committed if your team standardizes on VS Code extensions and settings, but this is a team decision.

Do not ignore sfdx-project.json. This file defines your project structure and must be committed. Similarly, package.json and package-lock.json should be committed if you use npm packages.

Ignore scratch org definition files only if they contain sensitive data. The config/project-scratch-def.json file is usually fine to commit — it just defines the scratch org shape.

Be explicit rather than using broad wildcards. Instead of ignoring all .json files (which would exclude sfdx-project.json), ignore specific files or directories.

Commit Message Conventions

Good commit messages are one of the most underrated practices in software development. Six months from now, when you are trying to figure out why a particular change was made, a message like “fix stuff” is useless. A message like “Fix null pointer in AccountHandler when AnnualRevenue is blank” tells you everything you need to know.

A widely adopted convention is the Conventional Commits format:

<type>(<scope>): <subject>

<body>

<footer>

Common types:

  • feat — A new feature
  • fix — A bug fix
  • refactor — Code change that neither fixes a bug nor adds a feature
  • test — Adding or updating tests
  • docs — Documentation changes
  • chore — Maintenance tasks (build process, dependencies)

Examples:

feat(account): Add priority calculation trigger

fix(opportunity): Handle null Amount in close date validation

refactor(lead): Extract conversion logic into separate handler class

test(contact): Add test coverage for duplicate detection

The scope is optional but helpful — it tells you which part of the codebase is affected. For Salesforce projects, the scope might be an object name, a feature area, or a module name.

Keep the subject line under 50 characters if possible, and definitely under 72. Use the imperative mood (“Add feature” not “Added feature” or “Adding feature”). Do not end the subject line with a period.

If you need to explain more, add a body separated by a blank line. The body can be as long as needed and should explain the what and why, not the how (the code shows the how).

When to Use Force Push (Almost Never)

git push --force (or git push -f) overwrites the remote branch with your local branch, regardless of what is on the remote. This is dangerous because it can permanently delete other people’s commits from the remote.

There are only two scenarios where force push is acceptable:

  1. After rebasing a branch that only you are working on. If you rebase your feature branch onto the latest main, your commit history has been rewritten and a normal push will be rejected. A force push is necessary. But only do this if nobody else is working on that branch.

  2. Cleaning up a branch before a pull request review. If you want to squash messy WIP commits into clean ones before submitting for review, you will need to force push. Again, only if the branch is yours alone.

In all other cases, do not force push. If you are ever tempted to force push to main or any shared branch, stop and think about what you are doing. You are about to rewrite shared history, which will cause problems for everyone on your team.

A safer alternative is git push --force-with-lease, which only force pushes if the remote branch is where you expect it to be. If someone else has pushed commits since you last fetched, the push will be rejected. This protects against accidentally overwriting someone else’s work.

Pull Request Etiquette

While we are talking about collaboration practices, a few notes on pull requests since they go hand-in-hand with branching:

  • Keep pull requests small and focused. A PR that changes 500 lines across 20 files is hard to review. A PR that changes 50 lines in 3 files is easy.
  • Write a clear description of what the PR does and why.
  • Reference any related tickets or issues.
  • Respond to review comments promptly and professionally.
  • Do not merge your own pull requests without at least one approval from a teammate.

Common Mistakes to Avoid

Committing directly to main. Always work on a branch, even for small changes. This keeps main stable and gives you the option to abandon work without affecting anyone.

Giant commits. Commit frequently with small, logical changes. A commit that says “Add account trigger, update three test classes, fix lead conversion, and refactor opportunity handler” is impossible to review or roll back partially.

Not pulling before pushing. If you try to push and the remote has changed, your push will be rejected. Get in the habit of pulling before you start work and before you push.

Committing secrets. Never commit API keys, passwords, tokens, or other secrets. Use environment variables and make sure your .gitignore excludes files that contain secrets. Once a secret is committed, even if you delete it in a subsequent commit, it is in the Git history forever (unless you use advanced history-rewriting techniques, which are messy).

Fear of branching. Branches are free in Git. They take almost no space and are incredibly fast to create. Do not be afraid to create a branch for every piece of work, no matter how small.


Wrapping Up

Version control is not just a tool — it is a fundamental shift in how you approach development. Once you start using Git, you will wonder how you ever developed without it. The ability to track every change, collaborate without fear of overwriting, review code before it reaches production, and roll back when things go wrong is transformative.

Here is what we covered:

  • Version control fundamentals — why it matters, centralized vs distributed, and why Git is the standard.
  • Installing and configuring Git — setting up your identity, verifying the installation, and creating a proper .gitignore for Salesforce projects.
  • Remote repositories on GitHub — creating repos, connecting local to remote, and choosing between SSH and HTTPS authentication.
  • Essential Git commands — init, clone, status, add, commit, push, pull, fetch, branch, checkout, merge, log, diff, stash, reset, and rebase.
  • Best practices — branching strategies (feature branches, GitFlow, trunk-based), commit message conventions, .gitignore recommendations, and why force push should be your last resort.

Start small. Initialize a Git repository for your current Salesforce project. Make a few commits. Push to GitHub. Create a branch for your next piece of work. The muscle memory will build quickly, and within a few weeks, these commands will be second nature.

In Part 58, we will shift gears to talk about The Basics of Clean Code — how to write Apex (and code in general) that is readable, maintainable, and a pleasure to work with rather than a puzzle to decipher. See you there.