Git is a very very powerful tool which is often quite difficult for even experienced programmers to wrap their heads around. To get started, we have to define the terms branch, repository, and remote and draw clear differences between them:
A git branch is a collection of commits (a.k.a changes). Branches are named based upon their role in the project. Github uses the name master as the branch you make commits on that are considered "the good to go" commits.
A git repository is a place you store your branches.
A git remote is the name for a repository that isn't on your computer. Heroku and Github both provide remotes for different purposes; Github uses them to collaborate on writing more code; and Heroku uses them so you can deploy code to a particular application environment.
Let's pretend you have the following:
We're also going to assume that you're not using Heroku pipelines to automatically deploy pushes to Github; not because it's a bad idea to do that, but because that won't help you understand how to use the git command line program to manipulate the projects .git/config to push your changes to github/origin, heroku/staging and heroku/production.
We're going to start with a blank git config, execute a git command, and review the resulting git config.
So! LET'S GET STARTED!
First, run git clone firstname.lastname@example.org:your-git-username/your-project.git
git clone email@example.com:your-git-username/your-project.git
Then, run cat .git/config:
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
url = firstname.lastname@example.org:your-git-username/your-project.git
fetch = +refs/heads/*:refs/remotes/origin/*
remote = origin
merge = refs/heads/origin
Let's break this down, shall we?
First, the weird-looking [a-word] syntax. This is similar to the ini file format and is how git groups configuration for a particular topic. the [core] config includes a bunch of things we're going to mostly ignore for now. That said, if you want to find out what each of those mean you run the terminal command man git-config to open the manual for git-config and then use the / key to enter "search" mode and typing core\. to jump to the first reference for what you can set as part of git's core config. You can tap n to cycle through the rest of these items.
So let's look at the [remote "origin"] section! This has two configuration settings by default: the url and the fetch options. The url configuration specifies where the remote named origin lives. As you can see, the url here is currently hosted on the github.com domain. The fetch configuration is used to configure how the git fetch and git pull commands handle retrieving information from the origin remote. You can use the same fancy-pants man git-config and /remote\. plus hammering that n key to skim what each of the options are for the remote configuration.
Right now, if you were to run git branch -a you will see that you have ~3 entries!
git branch -a
remotes/origin/HEAD -> origin/master
The one prefixed with an * is the current locally checked out branch, while the ones prefixed by remotes/origin/ are letting you know that the origin remote has a branch named master and that the origin/master branch is the primary branch for the origin remote.
Next, we'll look at the [branch "master"] section. This also has two configuration settings when you initially clone the repository. The first one, remote specifies which remote git push and git fetch and git pull will use for the master branch by default. This allows you to run git push without any arguments and be confident that the commits in the master branch will go to the origin remote (AKA github). You may also explicitly use these defaults by running git push origin master instead of git push. I'm an old curmudgeon who dislikes implicit things and convenience, so I tend to rungit push origin master (and the corresponding git pull origin master) more often than I run git push.
git push origin master
git pull origin master
The merge configuration says that "when pulling I want to merge the changes from the origin/master branch into my local master branch; and when pushing I want to merge the changes from my local master branch into the remoteorigin's master branch.
PHEW That's everything that I expect to be in your .git/config from initial clone. Now on to the next step: What about when you add your heroku environments!
Using what we've learned, we know we want to create a remote that corresponds to the application named staging on heroku. The syntax for doing so is: git remote add staging https://git.heroku.com/staging.git (As an aside, you will need to change the url to what your Heroku application says the url should be).
git remote add staging https://git.heroku.com/staging.git
Now let's look at the additions to our .git/config file:
url = https://git.heroku.com/staging.git
fetch = +refs/heads/*:refs/remotes/staging/*
Everything else should stay the same at this point; and both of the configuration options should look familiar, as they are the same options that are set on initial clone. If you were to run git branch -a you may expect to see a remotes/staging/master entry... but wait! It's not there!?
That's because adding a remote doesn't automatically provide your local computer with any information about the remote beyond that it exists. If you run git fetch --all git will reach out to both your origin and your staging remotes and update it's local cache with information about which branches the staging remote has.
git fetch --all
Now if you run git branch -a you will see an entry for remotes/staging/master.
So, we've now updated our git config with a remote named staging that points to the heroku app we have named staging; but we're not done yet! Now we need to push all the code we have in our local master branch onto the staging heroku app. I bet you can guess exactly what we need to type to do so!
If you saidgit push staging master then congratulations! You're 100% correct! If you said anything else, then still congratulations because git is confusing and complex and you've tolerated my ramblings long enough to get here.
git push staging master
Next, we repeat for our production heroku app: git remote add production https://git.heroku.com/production.git which adds the following lines to our .git/config
git remote add production https://git.heroku.com/production.git
url = https://git.heroku.com/production.git
fetch = +refs/heads/*:refs/remotes/production/*
And, once again, to push the code we have locally into the heroku remote; we run git push production master. (We don't have to run git fetch --all or git branch -a, but they are useful for getting bearings and illustrative purposes.
git push production master
So, there we go; we have 3 remotes in our git config, our master branch is set to push to origin by default, and we can use git push <remote> master to merge our local master branch into our production and staging Heroku application remotes..
git push <remote> master
Now we get to enter CHALLENGE MODE! How can we unwind your .git/config so that it matches the .git/config you want?
Now, I'm not 100% certain what the name of the app you are building on Heroku is, so I'm going to pretend the name is jess-is-an-awesome-programmer and that is the name that is used on both github for the repository and on heroku for production.For the staging heroku app I'm going to pretend it's jess-is-an-awesome-programmer-staging
First, we're going to create a remote named origin that points to your github repository:
git remote add origin email@example.com:monkeywithacupcake/jess-is-an-awesome-programmer.git
Second, we're going to make sure master is set up to default pushes to your remote named origin, instead of the one named production
git branch --set-upstream-to remotes/origin/master
When you run cat .git/config and you after this, you'll see that the section that configured your master branch now reads as follows:
remote = origin
merge = refs/heads/master
Third, we're going to remove all the Heroku remotes, because it's often easier to make these kind of changes when there isn't anything dangling from previous attempts:
git remote rm staging
git remote rm production
git remote rm heroku
Run cat .git/config between each of these to see how it changes! (OK I'll tell you, it removes the parts labeled [remote "staging"], [remote "production"] and [remote "heroku"].
Finally, we re-add those remotes:
git remote add staging https://git.heroku.com/jess-is-an-awesome-programmer-staging.git
git remote add production https://git.heroku.com/jess-is-an-awesome-programmer.git
Now, if you run git push origin master (or git push) your commits on your local master branch will be on github. When you run git push production master your commits on your local master branch will be on the jess-is-an-awesome-programmer application and so on.
Keep in mind that changing the .git/config by. hand can leave your git program in a slightly broken state. So be careful if you do change it by hand!
Good luck Jess!
Thank you for breaking this down for me. Awesomely detailed and helping me understand.
You deserve a sticker!!!
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.