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:
A local repository where you commit changes directly to master. We'll call this local for now.
A Github repository which we will call origin;
A Heroku application which we will call staging;
A Heroku application which we will call production
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 git@github.com:your-git-username/your-project.git
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!
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.
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).
Now let's look at the additions to our .git/config file:
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.
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.
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
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.
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..
FINALLY! WOOOOOOOOOOOoooooooooooooo!
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:
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"].
git remote add production https://git.heroku.com/jess-is-an-awesome-programmer.git
cat .git/config (yay! three more added lines!)
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!
Hey Jess!
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
, andremote
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:
master
. We'll call thislocal
for now.origin
;staging
;production
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 togithub/origin
,heroku/staging
andheroku/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 git@github.com:your-git-username/your-project.git
Then, run
cat .git/config
: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 commandman git-config
to open the manual forgit-config
and then use the/
key to enter "search" mode and typingcore\.
to jump to the first reference for what you can set as part of git'score
config. You can tapn
to cycle through the rest of these items.So let's look at the
[remote "origin"]
section! This has two configuration settings by default: theurl
and thefetch
options. Theurl
configuration specifies where the remote namedorigin
lives. As you can see, the url here is currently hosted on thegithub.com
domain. Thefetch
configuration is used to configure how thegit fetch
andgit pull
commands handle retrieving information from theorigin
remote. You can use the same fancy-pantsman git-config
and/remote\.
plus hammering thatn
key to skim what each of the options are for theremote
configuration.Right now, if you were to run
git branch -a
you will see that you have ~3 entries!The one prefixed with an
*
is the current locally checked out branch, while the ones prefixed byremotes/origin/
are letting you know that theorigin
remote has a branch namedmaster
and that theorigin/master
branch is the primary branch for theorigin
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 remotegit push
andgit fetch
andgit pull
will use for the master branch by default. This allows you to rungit push
without any arguments and be confident that the commits in themaster
branch will go to theorigin
remote (AKA github). You may also explicitly use these defaults by runninggit push origin master
instead ofgit push
. I'm an old curmudgeon who dislikes implicit things and convenience, so I tend to rungit push origin master
(and the correspondinggit pull origin master
) more often than I rungit push
.The
merge
configuration says that "when pulling I want to merge the changes from theorigin/master
branch into my localmaster
branch; and when pushing I want to merge the changes from my localmaster
branch into the remoteorigin
'smaster
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).Now let's look at the additions to our
.git/config
file: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 aremotes/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 yourorigin
and yourstaging
remotes and update it's local cache with information about which branches thestaging
remote has.Now if you run
git branch -a
you will see an entry forremotes/staging/master
.So, we've now updated our git config with a remote named
staging
that points to the heroku app we have namedstaging
; but we're not done yet! Now we need to push all the code we have in our local master branch onto thestaging
heroku app. I bet you can guess exactly what we need to type to do so!If you said
git 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.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
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 rungit fetch --all
orgit branch -a
, but they are useful for getting bearings and illustrative purposes.So, there we go; we have 3 remotes in our git config, our
master
branch is set to push toorigin
by default, and we can usegit push <remote> master
to merge our localmaster
branch into ourproduction
andstaging
Heroku application remotes..FINALLY! WOOOOOOOOOOOoooooooooooooo!
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'sjess-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 git@github.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 namedorigin
, instead of the one namedproduction
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 yourmaster
branch now reads as follows: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
cat .git/config
(to see the added lines!)git remote add production https://git.heroku.com/jess-is-an-awesome-programmer.git
cat .git/config
(yay! three more added lines!)Now, if you run
git push origin master
(orgit push
) your commits on your local master branch will be on github. When you rungit push production master
your commits on your local master branch will be on thejess-is-an-awesome-programmer
application and so on.Keep in mind that changing the
.git/config
by. hand can leave yourgit
program in a slightly broken state. So be careful if you do change it by hand!Good luck Jess!
Zee
Thank you for breaking this down for me. Awesomely detailed and helping me understand.
You deserve a sticker!!!