Salto for
Salesforce
Guides
SHARE
Knuckles
October 10, 2024
7
min read
When it comes to Salesforce development, effective version control is crucial for managing changes, collaborating with team members, and ensuring the integrity of your projects. Git, the most popular version control system, offers Salesforce developers a powerful way to track and manage changes to their Salesforce orgs. This guide will walk you through the basics of using Git with Salesforce, providing you with the foundational knowledge you need to streamline your development processes.
Version control is not a new concept. Actually, it’s already in Salesforce in some metadata types, such as flows and process builders. For example, in Flows, Salesforce provides a built-in mechanism to have multiple versions of a flow (as shown in the screenshot below).
If there were a similar mechanism for other metadata types—like validation rules, profiles, and fields—we wouldn’t need to use Git for version control. Obviously, Salesforce isn’t in the business of recreating version control as a built-in mechanism, but it’s nice to see that such a thing already exists for very specific metadata types.
These multiple versions allow us to experiment. We can create a new version, make lots of changes, and then if we don’t like it, we can always go back to a previous version of that flow. In the case of CI/CD, version control is a core enabler for continuous integration. That bears repeating: You cannot implement continuous integration without version control.
There are various types of software for implementing version control, but Git is the most popular. Git works based on files stored on your computer. Git is able to version—track multiple versions of—files, and that could be any file on your computer. It could be a text file, an image, a document, or even an SFDX representation of Salesforce metadata. So, if we have XML files representing the metadata in our Salesforce org, we can version them with Git.
As we said above, we use Git to version control the SFDX files of a Salesforce org. However, this mechanism is a bit awkward because Git doesn’t know about our org. What we do in Git may not be exactly what we have in our org. And if we make a change in our org, Git may not know about that, which means our org and Git can fall out of sync.
This isn’t anything new, though—we know this can also happen if we make changes locally to an SFDX project but never deploy them to our org. Likewise, we could create a validation rule in our org and never retrieve it to our SFDX project. Both of these actions would cause our project and org to be out of sync.
It’s very important to understand how Git is used in the context of CI/CD as there are two different scenarios. The first scenario is what we’re going to call “local development tasks”. These include things like making changes to an Apex class and keeping track of those changes, reviewing previous versions of an Apex class or a validation rule, resolving conflicts, and just generally keeping track of work in progress. Even if you don’t intend to implement CI/CD, this use of Git is something you can use today.
The second scenario in which Git is used in the context of CI/CD is for automating deployments. This is also what enables continuous integration. In this model, we’d have an SFDX project in Git (through one of the common Git providers, like GitHub, Bitbucket, GitLab, etc.), and we’d take the contents of that Git branch and use them as the source of truth for deploying to another Salesforce org. This is an advanced concept, which we’ll look at in more depth later on in the guide.
To learn how to set up git, we’ll start in our SFDX project and make sure we’re connected to our dev environment.
The first step is to make sure that we have Git installed. If we execute the command git, we’ll get an explanation of the different git commands available. If we were to get an error message or something that says “git not found”, it means we don’t have git installed on our computer.
The next step is to make sure we have an extension called GitLens, which we can download from the extension marketplace in Visual Studio Code. We’ll be using git through GitLens and out of the box Source Control features here in Visual Studio Code.
Once we’ve confirmed that git is installed on our computer, we’ll now initialize a git repository on the SFDX project. Normally, we’d do this with the git init command, but for the sake of focusing on concepts over commands (which you can easily search online), we’ll use the Visual Studio Code UI and GitLens to initialize our repository.
When we click on the Source Control button, we’ll see a message saying that our SFDX project doesn’t have a git repository, so we’ll then click the Initialize Repository button.
Essentially, “initializing” means we’re telling git what we want to track. In our example, let’s say we want to track everything. Whatever is in the SFDX project, we want to keep versions of that. For the commit message, we’ll use “initial commit” and then click “Commit”.
(If a message pops up saying there are no staged changes to commit, asking if we’d like to stage all our changes and commit them directly, we’ll click “Yes”.)
Everything in our SFDX project is now being tracked by git. This means that any changes we make moving forward will be stored in git. Let’s see what that looks like.
Let’s say we’re on the Account Service Layer class, and we want to create a new method to get a default account. We’ll use the code shown in the image below.
(Tip: We’re using GitHub Copilot here, which is an amazing tool and we highly recommend it.)
We now have the basic first implementation of what a default account would look like, so let’s save it and deploy it to the source org (which, in this case, is our sandbox). We could then go and do some testing or perhaps create an Apex test class to confirm that it works. The important thing is that we’ve made a change in our SFDX project.
Now, notice this line here:
This line shows that we made a change two seconds ago, but that it’s an uncommitted change. This means git is aware that we made a change here, but we haven’t yet told git that we want to track this or that we want to take a snapshot of this change.
To do that, we’ll go to the Source Control tab. There, we can see that there are two files that have changed. One is an internal SFDX project file (which we won’t be dealing with), and the other is the change that git has detected on the Account Service Layer class.
If we click on it, we can see exactly what the change was. On the left, we see the previous version of the class. On the right, we see in green the new version—the changes—that we made.
Again, this is git telling us it has recognized that we’ve made a change, but we haven’t yet told git that we want to actually keep track of this change. To do that, we’ll click on the plus sign next to the file under Changes in the Source Control tab. This will stage the change, which is basically telling git that we’re interested in this change. To commit the change—or, essentially, tell git to take a snapshot of it—we’ll enter a commit message and click commit.
And that’s it!
Having done that, we can make more changes. As another example, let’s say we want to add a default account number. We’ll save it, deploy it to the org, and then commit the change.
Again, we’ll find the change in the Source Control tab. This time, for the sake of illustration, do not stage your change by clicking the plus sign. Instead, simply type your commit message and click “Commit”.
A message (the same one you might’ve seen previously) will appear saying that there are no staged changes to commit and asking if we want to stage all our changes and commit them directly. And that’s right—we didn’t stage our change this time. In almost all scenarios, we’ll want to stage and commit at the same time, so we can actually skip that manual staging step by clicking “Always” on this message. This makes it so that it always stages if we click on commit.
Now, let’s add a description, save and deploy to the source org, and then commit the change.
To see everything we’ve done, let’s head back to the Explorer tab in the sidebar. At the bottom, we’ll see a timeline of all the changes we made on the Account Service Layer.
If we click on “initial commit” to see what change we made, we won’t see anything at all in the left pane because this was a brand new addition to the Git repo. That’s when we took the first snapshot.
Likewise, for the other changes we made (adding a default implementation for creating an account, adding the account number field, etc), we won’t see anything on the left side because those lines did not exist in the previous version.
Just doing this, without worrying about CI/CD and Git-based deployments, is super useful. You can make changes and start saving them to Git, and, if you get confused, can’t remember what you did, or want to go back to a previous version, you’ll be able to see a timeline of all the changes you made. It’s also very easy to understand who did what and when.
One problem with committing changes the way we just did above is that it makes it a bit hard to go back to a previous version. Let’s say we want to go back to the initial implementation, as shown in the timeline below. This means we’ll want to get rid of the account number and the description that we added.
It’s not impossible to go back to this version, but it does require a bit of back and forth,, we recommend using Git branches so it’s much easier to isolate our work. Let’s see what that looks like.
A Git branch is basically a copy of the main line of work, which we use to work in isolation. For reference, all the changes we made in the exercise above were in the main line of work (also known as the master). With Git branches, we can do whatever we want to the branch, and it will still be completely isolated from the main line. Git branches are very important for a Git-based workflow, as well as CI/CD.
Below is a visualization of Git branches.
We make our changes first on the main line, and from there we can create another branch (e.g. develop), and from that branch, we can create more branches. Branches are not only made from the main line. Again, a branch is just a copy or clone of another branch. Eventually, we can merge those branches together to get the changes back into the main line. Let’s look at an example.
Back in our project, we’ll use the Apex Class Utilities. Let’s say we want to make many different changes to this logic, and we don’t want to have a lot of different commits because we may be confused later as to which commit is which. We want to have everything on a completely separate branch so that it works in isolation.
In Visual Studio Code, we’ll hit Cmd + Shift + P (for Mac) or Ctrl + Shift + P (for Windows) and click on the Git: Create Branch From option.
It’ll ask us where we’re going to create the branch from. In other words, what is the source of this clone? In this case, it’ll be the master, which is the same as the head. These are just two different ways of referring to the current main line of work. Once we select “Master”, you’ll give the branch a name. For our example, we’ll name it “changes-to-utils”.
Now, let’s start making a few changes. First, let’s take one of our classes and rename it as “theApexClass”, then save and deploy to our sandbox, and commit. We’ll call this “renamed var to theApexClass”.
For another change, let’s change the group name. Type “instance of” and commit.
As we start committing changes, we’ll see these changes appearing on the timeline of the branch. If we click on the name of the branch in the bottom left, we can go back to the master branch, which is our original main line of work.
Once back in the master, we’ll see that the changes we just made in the branch are not there. The class name is not theApexClass, and the group name is an empty string. This is also reflected in the timeline. In the branch timeline, we’ll see there are two commits (the ones we just made), but in the master branch timeline, those commits don’t exist.
This makes things a lot easier! We can be making changes to the class in one branch, and in the end, if we decide we don’t like it or it’s not what we intended, we can just switch back to the master. It’s as if those changes never happened.
One thing that’s important to note is that changing between the master and various branches does not change what’s in our org. This only changes what’s in our SFDX project, so we still need to deploy to the source org if we do in fact want the changes in this branch to be deployed to the org. Later on, if we decide we don’t like it or we want to revert back to the master, we’ll need to go back to the master and then deploy.. As mentioned previously, Git doesn’t know anything about our Salesforce org.
Let’s say, though, that we’re happy with the changes we made in the branch and we want to incorporate them into the main line. To do that, we have to merge the branches. Click Cmd + Shift + P again, and select the Merge Branch command. It’ll ask which branch you’re going to merge from. In our case, we’re on the master branch, and we want to merge the Changes-to-utils branch into the master, so we’ll click on Changes-to-utils.
And that’s it! The changes will be merged, and the commits will also be added to the main line of work.
We can also use Git as a backup and collaboration tool with GitHub, which is a hosted Git service that runs in the cloud. Until now, we’ve been using Git on our computer, but we can also host our Git history in the cloud on a software like GitHub—meaning we’ll have all our commits, branches, and change history in the cloud and not stored only on our computer.
GitHub is one of many providers (there’s also GitLab, Bitbucket, and lots of others), and it can be used to backup our code. For example, if our computer were to crash, we wouldn’t want our entire SFDX project and its whole change history to disappear along with it. By using GitHub, we can keep this information in the cloud and restore it if needed. GitHub can also be used for collaboration and CI/CD, but that’s beyond the scope of this guide.
Here, we’ll look at how to use GitHub as a backup for our personal projects. It’s worth noting that this is not usually how GitHub is used in the real world, where most of the time GitHub, GitLab, Bitbucket, or any of these providers are used for collaboration and CI/CD, which means multiple people will be using the same Git provider and collaborating on the changes. However, to get our feet wet, we’ll start by seeing how Git can be used as a backup.
To get started, we’ll head to github.com. We’ve already got an account, but it’s very easy to create one if needed. To create a new repository,, we’ll click “New” under “Repositories.”
Now we’ll need to give our new repository a name. This could be the same name as our SFDX project, or it could be something else. For our example, we’ll name it “My SFDX Backup”.
For this example, we’ll also make our repository public, meaning anyone on the internet can see it, but note that it could also be made private. At the bottom, we’ll click “Create repository”, and then we’ll be given three options to populate the repository.
If we’re working on a new repository that doesn’t exist, we’ll go with the top instructions. But, since we have an existing repository on our computer, we’ll copy the instructions for that and paste those commands into our SFDX project. Now, if we go back to GitHub and refresh, we’ll see our entire SFDX project there, along with all our commits.
This is great! As mentioned above, if we were to lose our computer or have something happen to it, we could just go to GitHub and we’d have an entire backup of our project and all the changes we’ve made.
For example, if we click on the commits, we can see for each one what the repository looked like at that point in time, as well as see the commit details for a specific change.
This is very useful if you’re working on a Salesforce app and, although you’re not implementing CI/CD, you want to backup your code. It’s something you can do without anyone else having to be involved in the process.
Quick note: What used to be called the master is now called the main. It is the same branch representing the main line of work, but Git now uses updated terminology.
Let’s now look at how we can interact with GitHub from our local Git repository.
Once we’re ready to commit, we’ll click “Commit” as we did before. However, this time we’ll notice that after we commit, there’s a “Sync changes” button with an up arrow next to it. This means that we can push this to our remote repository. If we go to GitHub and refresh without hitting “Sync changes”, we’ll see that the commit is not yet there because it hasn’t been pushed from the local repository. So, let’s click on “Sync changes”, head back to GitHub, and refresh. Voila! Our commit is there.
This process could also work the other way around, but it’s not as common if we’re using GitHub for personal use. However, if we’re using GitHub as a team (which, again, is not something we’ll cover in depth in this guide), you may want to pull changes from GitHub back into your local repository. Let’s take a quick look at what that process would look like.
If we click on “Classes” in GitHub, we can make a change to a class directly in GitHub. (Doing this isn’t a very common use case, but let’s go with it for the sake of example as this is what it would look like if someone else were pushing a change to this class.)
Let’s say we’re going to remove this description:
We’ll then commit this directly on GitHub by choosing “Commit directly to the main branch” and clicking on “Commit changes”.
Back inside our local repository, we’ll open the command palette and search for “pull”.
Click on “Git: Pull”. Then, if we go to the Account Service Layer, we’ll see that commit in the timeline, which came from the remote repository where we removed the comment description.
Once again, this is not a real use case if we’re only using GitHub for our own personal use, but if we’re using GitHub as a team and people are pushing changes to their repository, this is a way for us to pull those changes and incorporate them into our local Git repository. This concept is key for continuous integration, so keep it in mind.
Now, what if we did lose our computer and we wanted to access this backup? How would we actually do that? First, we’ll open up the terminal and create a new folder called My Restored Backup.
Then, heading back to GitHub, we’ll click “Code” and copy the URL shown there.
Back in the terminal, we’ll use the git clone command and paste the URL we just copied. We are now cloning that project.
Now if we use an ls command to show all the files here, we’ll see there’s a new folder called My SFDX backup.
Let’s go to that folder, open it with Visual Studio Code—and there’s our project! We should see all the Apex classes, all the fields, and everything else. We’ll also be able to see the history of all the changes we made. This is a really useful way to use GitHub as a backup of your SFDX project and your Git history.
It’s time to talk about Git and the multiverse of madness. What exactly does this mean? When we start looking into Git and Salesforce, we’ll come across the term “source of truth”. We want Git to be the source of truth for our changes. This is possible if we use Git-based deployments, which, as we said earlier, is a way to deploy what’s in a Git branch. However, it’s important to understand that anything can be the source of truth for your metadata.
For example, it could be the SFDX project, the local Git repository, the remote Git repository, or the Salesforce org. All four of these entities can have a different view of our Salesforce metadata.
This can get confusing—and we’re going to look at exactly how confusing it can get so that we get some experience and have a full understanding of the limitations and challenges of using Git with Salesforce. Let’s take a look at an example.
Back in our SFDX project, we’ve got a class named Apex Class Utilities. Let’s first run an SFDX diff to make sure that our Salesforce org is in sync with the SFDX project. Then, let’s say we want to remove the last two lines from one of the comments. We’ll do that, and then save.
Now, where is the source of truth for this Apex class? If we go back to the last commit we made (before this change, since we haven’t committed this one yet), we’ll see there’s a pending commit there where we removed those lines—so that’s still sort of in our project. As far as our local Git repository is concerned, this is the source of truth for this Apex class. In this version, those lines are still there.
But as far as our SFDX project is concerned, those lines are not there. This is because we’ve removed the lines from the project. However, because we haven’t committed those changes to Git, Git doesn’t know about them—or rather, Git is aware that we made the changes, but we haven’t yet told it to keep track of them.
Now, let’s say this is actually a shared sandbox, and another developer pushed a change to this Apex class via changeset. The change that developer made was adding a completely new line where we previously removed those two lines, in the Salesforce org. In our project, if we run an SFDX diff, we’ll see that the line exists in the org but not in the project.
We now have three versions of this Apex class: the version in the project (where that line doesn’t exist at all), the version in the Salesforce org (where the line only exists in the org), and the version in Git (where we have the original lines before we removed them).
This is very confusing. Git doesn’t know what’s happening in our SFDX project or in our Salesforce org, and our Salesforce org doesn’t know what’s happening in our SFDX project or our Git repository. We hear people say that Git has the history of all our changes, but what if those changes are not happening through Git? What if the change was made through a deployment of a changeset, the metadata API, or something else? How is Git supposed to know about that?
The answer is that it can’t. Using Git with Salesforce can be a bit confusing at times because we’re trying to force Git to know everything about our Salesforce org, which isn’t always possible. It’s worth noting that as we advance and start using Git for deployments, this problem will clear up. However, right now, just keep in mind that when you use Git, SFDX, and changesets at the same time, you may end up with different versions of the same metadata.
In this guide, we covered the essentials of using Git with Salesforce, including setting up a Git repository, managing changes through commits, and leveraging branches for isolated development. We also explored the use of GitHub as a backup and collaboration tool, and looked at the complexities of maintaining a consistent “source of truth” across different environments. By mastering these Git fundamentals, you can better manage your Salesforce projects, reduce the risk of errors, and collaborate more effectively with your team.
Salto for
Salesforce
SHARE
Knuckles
October 10, 2024
7
min read
When it comes to Salesforce development, effective version control is crucial for managing changes, collaborating with team members, and ensuring the integrity of your projects. Git, the most popular version control system, offers Salesforce developers a powerful way to track and manage changes to their Salesforce orgs. This guide will walk you through the basics of using Git with Salesforce, providing you with the foundational knowledge you need to streamline your development processes.
Version control is not a new concept. Actually, it’s already in Salesforce in some metadata types, such as flows and process builders. For example, in Flows, Salesforce provides a built-in mechanism to have multiple versions of a flow (as shown in the screenshot below).
If there were a similar mechanism for other metadata types—like validation rules, profiles, and fields—we wouldn’t need to use Git for version control. Obviously, Salesforce isn’t in the business of recreating version control as a built-in mechanism, but it’s nice to see that such a thing already exists for very specific metadata types.
These multiple versions allow us to experiment. We can create a new version, make lots of changes, and then if we don’t like it, we can always go back to a previous version of that flow. In the case of CI/CD, version control is a core enabler for continuous integration. That bears repeating: You cannot implement continuous integration without version control.
There are various types of software for implementing version control, but Git is the most popular. Git works based on files stored on your computer. Git is able to version—track multiple versions of—files, and that could be any file on your computer. It could be a text file, an image, a document, or even an SFDX representation of Salesforce metadata. So, if we have XML files representing the metadata in our Salesforce org, we can version them with Git.
As we said above, we use Git to version control the SFDX files of a Salesforce org. However, this mechanism is a bit awkward because Git doesn’t know about our org. What we do in Git may not be exactly what we have in our org. And if we make a change in our org, Git may not know about that, which means our org and Git can fall out of sync.
This isn’t anything new, though—we know this can also happen if we make changes locally to an SFDX project but never deploy them to our org. Likewise, we could create a validation rule in our org and never retrieve it to our SFDX project. Both of these actions would cause our project and org to be out of sync.
It’s very important to understand how Git is used in the context of CI/CD as there are two different scenarios. The first scenario is what we’re going to call “local development tasks”. These include things like making changes to an Apex class and keeping track of those changes, reviewing previous versions of an Apex class or a validation rule, resolving conflicts, and just generally keeping track of work in progress. Even if you don’t intend to implement CI/CD, this use of Git is something you can use today.
The second scenario in which Git is used in the context of CI/CD is for automating deployments. This is also what enables continuous integration. In this model, we’d have an SFDX project in Git (through one of the common Git providers, like GitHub, Bitbucket, GitLab, etc.), and we’d take the contents of that Git branch and use them as the source of truth for deploying to another Salesforce org. This is an advanced concept, which we’ll look at in more depth later on in the guide.
To learn how to set up git, we’ll start in our SFDX project and make sure we’re connected to our dev environment.
The first step is to make sure that we have Git installed. If we execute the command git, we’ll get an explanation of the different git commands available. If we were to get an error message or something that says “git not found”, it means we don’t have git installed on our computer.
The next step is to make sure we have an extension called GitLens, which we can download from the extension marketplace in Visual Studio Code. We’ll be using git through GitLens and out of the box Source Control features here in Visual Studio Code.
Once we’ve confirmed that git is installed on our computer, we’ll now initialize a git repository on the SFDX project. Normally, we’d do this with the git init command, but for the sake of focusing on concepts over commands (which you can easily search online), we’ll use the Visual Studio Code UI and GitLens to initialize our repository.
When we click on the Source Control button, we’ll see a message saying that our SFDX project doesn’t have a git repository, so we’ll then click the Initialize Repository button.
Essentially, “initializing” means we’re telling git what we want to track. In our example, let’s say we want to track everything. Whatever is in the SFDX project, we want to keep versions of that. For the commit message, we’ll use “initial commit” and then click “Commit”.
(If a message pops up saying there are no staged changes to commit, asking if we’d like to stage all our changes and commit them directly, we’ll click “Yes”.)
Everything in our SFDX project is now being tracked by git. This means that any changes we make moving forward will be stored in git. Let’s see what that looks like.
Let’s say we’re on the Account Service Layer class, and we want to create a new method to get a default account. We’ll use the code shown in the image below.
(Tip: We’re using GitHub Copilot here, which is an amazing tool and we highly recommend it.)
We now have the basic first implementation of what a default account would look like, so let’s save it and deploy it to the source org (which, in this case, is our sandbox). We could then go and do some testing or perhaps create an Apex test class to confirm that it works. The important thing is that we’ve made a change in our SFDX project.
Now, notice this line here:
This line shows that we made a change two seconds ago, but that it’s an uncommitted change. This means git is aware that we made a change here, but we haven’t yet told git that we want to track this or that we want to take a snapshot of this change.
To do that, we’ll go to the Source Control tab. There, we can see that there are two files that have changed. One is an internal SFDX project file (which we won’t be dealing with), and the other is the change that git has detected on the Account Service Layer class.
If we click on it, we can see exactly what the change was. On the left, we see the previous version of the class. On the right, we see in green the new version—the changes—that we made.
Again, this is git telling us it has recognized that we’ve made a change, but we haven’t yet told git that we want to actually keep track of this change. To do that, we’ll click on the plus sign next to the file under Changes in the Source Control tab. This will stage the change, which is basically telling git that we’re interested in this change. To commit the change—or, essentially, tell git to take a snapshot of it—we’ll enter a commit message and click commit.
And that’s it!
Having done that, we can make more changes. As another example, let’s say we want to add a default account number. We’ll save it, deploy it to the org, and then commit the change.
Again, we’ll find the change in the Source Control tab. This time, for the sake of illustration, do not stage your change by clicking the plus sign. Instead, simply type your commit message and click “Commit”.
A message (the same one you might’ve seen previously) will appear saying that there are no staged changes to commit and asking if we want to stage all our changes and commit them directly. And that’s right—we didn’t stage our change this time. In almost all scenarios, we’ll want to stage and commit at the same time, so we can actually skip that manual staging step by clicking “Always” on this message. This makes it so that it always stages if we click on commit.
Now, let’s add a description, save and deploy to the source org, and then commit the change.
To see everything we’ve done, let’s head back to the Explorer tab in the sidebar. At the bottom, we’ll see a timeline of all the changes we made on the Account Service Layer.
If we click on “initial commit” to see what change we made, we won’t see anything at all in the left pane because this was a brand new addition to the Git repo. That’s when we took the first snapshot.
Likewise, for the other changes we made (adding a default implementation for creating an account, adding the account number field, etc), we won’t see anything on the left side because those lines did not exist in the previous version.
Just doing this, without worrying about CI/CD and Git-based deployments, is super useful. You can make changes and start saving them to Git, and, if you get confused, can’t remember what you did, or want to go back to a previous version, you’ll be able to see a timeline of all the changes you made. It’s also very easy to understand who did what and when.
One problem with committing changes the way we just did above is that it makes it a bit hard to go back to a previous version. Let’s say we want to go back to the initial implementation, as shown in the timeline below. This means we’ll want to get rid of the account number and the description that we added.
It’s not impossible to go back to this version, but it does require a bit of back and forth,, we recommend using Git branches so it’s much easier to isolate our work. Let’s see what that looks like.
A Git branch is basically a copy of the main line of work, which we use to work in isolation. For reference, all the changes we made in the exercise above were in the main line of work (also known as the master). With Git branches, we can do whatever we want to the branch, and it will still be completely isolated from the main line. Git branches are very important for a Git-based workflow, as well as CI/CD.
Below is a visualization of Git branches.
We make our changes first on the main line, and from there we can create another branch (e.g. develop), and from that branch, we can create more branches. Branches are not only made from the main line. Again, a branch is just a copy or clone of another branch. Eventually, we can merge those branches together to get the changes back into the main line. Let’s look at an example.
Back in our project, we’ll use the Apex Class Utilities. Let’s say we want to make many different changes to this logic, and we don’t want to have a lot of different commits because we may be confused later as to which commit is which. We want to have everything on a completely separate branch so that it works in isolation.
In Visual Studio Code, we’ll hit Cmd + Shift + P (for Mac) or Ctrl + Shift + P (for Windows) and click on the Git: Create Branch From option.
It’ll ask us where we’re going to create the branch from. In other words, what is the source of this clone? In this case, it’ll be the master, which is the same as the head. These are just two different ways of referring to the current main line of work. Once we select “Master”, you’ll give the branch a name. For our example, we’ll name it “changes-to-utils”.
Now, let’s start making a few changes. First, let’s take one of our classes and rename it as “theApexClass”, then save and deploy to our sandbox, and commit. We’ll call this “renamed var to theApexClass”.
For another change, let’s change the group name. Type “instance of” and commit.
As we start committing changes, we’ll see these changes appearing on the timeline of the branch. If we click on the name of the branch in the bottom left, we can go back to the master branch, which is our original main line of work.
Once back in the master, we’ll see that the changes we just made in the branch are not there. The class name is not theApexClass, and the group name is an empty string. This is also reflected in the timeline. In the branch timeline, we’ll see there are two commits (the ones we just made), but in the master branch timeline, those commits don’t exist.
This makes things a lot easier! We can be making changes to the class in one branch, and in the end, if we decide we don’t like it or it’s not what we intended, we can just switch back to the master. It’s as if those changes never happened.
One thing that’s important to note is that changing between the master and various branches does not change what’s in our org. This only changes what’s in our SFDX project, so we still need to deploy to the source org if we do in fact want the changes in this branch to be deployed to the org. Later on, if we decide we don’t like it or we want to revert back to the master, we’ll need to go back to the master and then deploy.. As mentioned previously, Git doesn’t know anything about our Salesforce org.
Let’s say, though, that we’re happy with the changes we made in the branch and we want to incorporate them into the main line. To do that, we have to merge the branches. Click Cmd + Shift + P again, and select the Merge Branch command. It’ll ask which branch you’re going to merge from. In our case, we’re on the master branch, and we want to merge the Changes-to-utils branch into the master, so we’ll click on Changes-to-utils.
And that’s it! The changes will be merged, and the commits will also be added to the main line of work.
We can also use Git as a backup and collaboration tool with GitHub, which is a hosted Git service that runs in the cloud. Until now, we’ve been using Git on our computer, but we can also host our Git history in the cloud on a software like GitHub—meaning we’ll have all our commits, branches, and change history in the cloud and not stored only on our computer.
GitHub is one of many providers (there’s also GitLab, Bitbucket, and lots of others), and it can be used to backup our code. For example, if our computer were to crash, we wouldn’t want our entire SFDX project and its whole change history to disappear along with it. By using GitHub, we can keep this information in the cloud and restore it if needed. GitHub can also be used for collaboration and CI/CD, but that’s beyond the scope of this guide.
Here, we’ll look at how to use GitHub as a backup for our personal projects. It’s worth noting that this is not usually how GitHub is used in the real world, where most of the time GitHub, GitLab, Bitbucket, or any of these providers are used for collaboration and CI/CD, which means multiple people will be using the same Git provider and collaborating on the changes. However, to get our feet wet, we’ll start by seeing how Git can be used as a backup.
To get started, we’ll head to github.com. We’ve already got an account, but it’s very easy to create one if needed. To create a new repository,, we’ll click “New” under “Repositories.”
Now we’ll need to give our new repository a name. This could be the same name as our SFDX project, or it could be something else. For our example, we’ll name it “My SFDX Backup”.
For this example, we’ll also make our repository public, meaning anyone on the internet can see it, but note that it could also be made private. At the bottom, we’ll click “Create repository”, and then we’ll be given three options to populate the repository.
If we’re working on a new repository that doesn’t exist, we’ll go with the top instructions. But, since we have an existing repository on our computer, we’ll copy the instructions for that and paste those commands into our SFDX project. Now, if we go back to GitHub and refresh, we’ll see our entire SFDX project there, along with all our commits.
This is great! As mentioned above, if we were to lose our computer or have something happen to it, we could just go to GitHub and we’d have an entire backup of our project and all the changes we’ve made.
For example, if we click on the commits, we can see for each one what the repository looked like at that point in time, as well as see the commit details for a specific change.
This is very useful if you’re working on a Salesforce app and, although you’re not implementing CI/CD, you want to backup your code. It’s something you can do without anyone else having to be involved in the process.
Quick note: What used to be called the master is now called the main. It is the same branch representing the main line of work, but Git now uses updated terminology.
Let’s now look at how we can interact with GitHub from our local Git repository.
Once we’re ready to commit, we’ll click “Commit” as we did before. However, this time we’ll notice that after we commit, there’s a “Sync changes” button with an up arrow next to it. This means that we can push this to our remote repository. If we go to GitHub and refresh without hitting “Sync changes”, we’ll see that the commit is not yet there because it hasn’t been pushed from the local repository. So, let’s click on “Sync changes”, head back to GitHub, and refresh. Voila! Our commit is there.
This process could also work the other way around, but it’s not as common if we’re using GitHub for personal use. However, if we’re using GitHub as a team (which, again, is not something we’ll cover in depth in this guide), you may want to pull changes from GitHub back into your local repository. Let’s take a quick look at what that process would look like.
If we click on “Classes” in GitHub, we can make a change to a class directly in GitHub. (Doing this isn’t a very common use case, but let’s go with it for the sake of example as this is what it would look like if someone else were pushing a change to this class.)
Let’s say we’re going to remove this description:
We’ll then commit this directly on GitHub by choosing “Commit directly to the main branch” and clicking on “Commit changes”.
Back inside our local repository, we’ll open the command palette and search for “pull”.
Click on “Git: Pull”. Then, if we go to the Account Service Layer, we’ll see that commit in the timeline, which came from the remote repository where we removed the comment description.
Once again, this is not a real use case if we’re only using GitHub for our own personal use, but if we’re using GitHub as a team and people are pushing changes to their repository, this is a way for us to pull those changes and incorporate them into our local Git repository. This concept is key for continuous integration, so keep it in mind.
Now, what if we did lose our computer and we wanted to access this backup? How would we actually do that? First, we’ll open up the terminal and create a new folder called My Restored Backup.
Then, heading back to GitHub, we’ll click “Code” and copy the URL shown there.
Back in the terminal, we’ll use the git clone command and paste the URL we just copied. We are now cloning that project.
Now if we use an ls command to show all the files here, we’ll see there’s a new folder called My SFDX backup.
Let’s go to that folder, open it with Visual Studio Code—and there’s our project! We should see all the Apex classes, all the fields, and everything else. We’ll also be able to see the history of all the changes we made. This is a really useful way to use GitHub as a backup of your SFDX project and your Git history.
It’s time to talk about Git and the multiverse of madness. What exactly does this mean? When we start looking into Git and Salesforce, we’ll come across the term “source of truth”. We want Git to be the source of truth for our changes. This is possible if we use Git-based deployments, which, as we said earlier, is a way to deploy what’s in a Git branch. However, it’s important to understand that anything can be the source of truth for your metadata.
For example, it could be the SFDX project, the local Git repository, the remote Git repository, or the Salesforce org. All four of these entities can have a different view of our Salesforce metadata.
This can get confusing—and we’re going to look at exactly how confusing it can get so that we get some experience and have a full understanding of the limitations and challenges of using Git with Salesforce. Let’s take a look at an example.
Back in our SFDX project, we’ve got a class named Apex Class Utilities. Let’s first run an SFDX diff to make sure that our Salesforce org is in sync with the SFDX project. Then, let’s say we want to remove the last two lines from one of the comments. We’ll do that, and then save.
Now, where is the source of truth for this Apex class? If we go back to the last commit we made (before this change, since we haven’t committed this one yet), we’ll see there’s a pending commit there where we removed those lines—so that’s still sort of in our project. As far as our local Git repository is concerned, this is the source of truth for this Apex class. In this version, those lines are still there.
But as far as our SFDX project is concerned, those lines are not there. This is because we’ve removed the lines from the project. However, because we haven’t committed those changes to Git, Git doesn’t know about them—or rather, Git is aware that we made the changes, but we haven’t yet told it to keep track of them.
Now, let’s say this is actually a shared sandbox, and another developer pushed a change to this Apex class via changeset. The change that developer made was adding a completely new line where we previously removed those two lines, in the Salesforce org. In our project, if we run an SFDX diff, we’ll see that the line exists in the org but not in the project.
We now have three versions of this Apex class: the version in the project (where that line doesn’t exist at all), the version in the Salesforce org (where the line only exists in the org), and the version in Git (where we have the original lines before we removed them).
This is very confusing. Git doesn’t know what’s happening in our SFDX project or in our Salesforce org, and our Salesforce org doesn’t know what’s happening in our SFDX project or our Git repository. We hear people say that Git has the history of all our changes, but what if those changes are not happening through Git? What if the change was made through a deployment of a changeset, the metadata API, or something else? How is Git supposed to know about that?
The answer is that it can’t. Using Git with Salesforce can be a bit confusing at times because we’re trying to force Git to know everything about our Salesforce org, which isn’t always possible. It’s worth noting that as we advance and start using Git for deployments, this problem will clear up. However, right now, just keep in mind that when you use Git, SFDX, and changesets at the same time, you may end up with different versions of the same metadata.
In this guide, we covered the essentials of using Git with Salesforce, including setting up a Git repository, managing changes through commits, and leveraging branches for isolated development. We also explored the use of GitHub as a backup and collaboration tool, and looked at the complexities of maintaining a consistent “source of truth” across different environments. By mastering these Git fundamentals, you can better manage your Salesforce projects, reduce the risk of errors, and collaborate more effectively with your team.