I felt like I totally understood when I set up my git config for an app running on heroku to finally have a staging and production environment separately. However, it has been a few months since I did it, and now things are not working as I thought they would. Clearly, I did not understand at all.
Essentially, I wanted to have a heroku staging app (generic heroku url) and my heroku production app (at my url) where I can get logs for both and manage both through the heroku CLI on my terminal. This seems like a fairly typical need, and I expect I'm not alone in getting confused.
Here's where I'm at:
- I followed Heroku dev guidance to setup my environment... I think
- Everything was working smashingly. My master pushes went to my github repo and were automatically deployed on my production heroku app. My staging pushes went directly to my heroku staging app.
- I was concerned about an issue and ran
heroku logs
and then a series of heroku commands on my master branch only to continuously get information about my staging environment. No amount ofheroku logs -r production
would change this situation. - In an attempt to get logs from my production app, I ran
heroku git:remote -a appname -r production
. Sadly, that changed my production pointer from my github repo back to heroku (which expected to get code from github). - I manually changed the
url
for my production remote back to github in my git.config. Now, changes push properly to github again, but I cannot see production heroku logs using the CLI.
How is this so confusing? I tried a ton of googling for guidance git-config but encountered explainer after explainer just going step by step through typing shell commands to set things up and not really discussing WHAT IN THE WORLD is going on inside the .git/config file.
I appeal to my amazing DEV.TO friends for help here. Explain this whole .git/config to me like I'm 5.
FWIW - mine looks something like:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[branch "master"]
remote = production
merge = refs/heads/master
[remote "heroku"]
url = git@heroku.com:myapp.git
fetch = +refs/heads/*:refs/remotes/heroku/*
[remote "production"]
url = https://github.com/me/myapprepo.git
merge = refs/heads/master
[remote "staging"]
url = https://git.heroku.com/adjective-noun-number.git
fetch = +refs/heads/*:refs/remotes/staging/*
[heroku]
remote = staging
[push]
default = tracking
Top comments (5)
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!!!
Hi, Jess.
Judging from file you have three remotes set: heroku, github/production and github/staging
Your branch is tracking the master branch of the github/production repo.
Heroku points to your staging environment.
Going back to the config file, there's nothing magical to it. It's what the name say: the configuration of your git repository.
Every repo has one, you sometimes also have a global one in
~/.gitconfig
Thanks for the response. I know it is not magical.
What is not clear to me is how I would be confident what each of the things means.
For example,
What commands can be in a
.git/config
file?What are the options under each item?
What is the difference between merge and fetch?
Keep in mind that practically always you don't edit the gitconfig file manually, but trough
git config
.This is a good tutorial.
If you want to know each and every option the gitconfig can have you have to read the documentation.
git fetch
means "go to the remote repository and download the new changes to our local repository".git merge
means "integrate this set of changes into this other branch".git pull
means more or less "go to the remote repository, download the new changes, merge such changes in the branch" (git fetch + git merge)