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 logsand then a series of heroku commands on my master branch only to continuously get information about my staging environment. No amount ofheroku logs -r productionwould 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
urlfor 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, andremoteand 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
masteras 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
remoteis 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 thislocalfor now.origin;staging;productionWe'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
gitcommand line program to manipulate the projects.git/configto push your changes togithub/origin,heroku/stagingandheroku/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.gitThen, 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-configto open the manual forgit-configand 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'scoreconfig. You can tapnto cycle through the rest of these items.So let's look at the
[remote "origin"]section! This has two configuration settings by default: theurland thefetchoptions. Theurlconfiguration specifies where the remote namedoriginlives. As you can see, the url here is currently hosted on thegithub.comdomain. Thefetchconfiguration is used to configure how thegit fetchandgit pullcommands handle retrieving information from theoriginremote. You can use the same fancy-pantsman git-configand/remote\.plus hammering thatnkey to skim what each of the options are for theremoteconfiguration.Right now, if you were to run
git branch -ayou 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 theoriginremote has a branch namedmasterand that theorigin/masterbranch is the primary branch for theoriginremote.Next, we'll look at the
[branch "master"]section. This also has two configuration settings when you initially clone the repository. The first one,remotespecifies which remotegit pushandgit fetchandgit pullwill use for the master branch by default. This allows you to rungit pushwithout any arguments and be confident that the commits in themasterbranch will go to theoriginremote (AKA github). You may also explicitly use these defaults by runninggit push origin masterinstead 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
mergeconfiguration says that "when pulling I want to merge the changes from theorigin/masterbranch into my localmasterbranch; and when pushing I want to merge the changes from my localmasterbranch into the remoteorigin'smasterbranch.PHEW That's everything that I expect to be in your
.git/configfrom 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
stagingon 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/configfile: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 -ayou may expect to see aremotes/staging/masterentry... 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 --allgit will reach out to both youroriginand yourstagingremotes and update it's local cache with information about which branches thestagingremote has.Now if you run
git branch -ayou will see an entry forremotes/staging/master.So, we've now updated our git config with a remote named
stagingthat 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 thestagingheroku app. I bet you can guess exactly what we need to type to do so!If you said
git push staging masterthen 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
productionheroku app:git remote add production https://git.heroku.com/production.gitwhich adds the following lines to our.git/configAnd, 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 --allorgit 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
masterbranch is set to push tooriginby default, and we can usegit push <remote> masterto merge our localmasterbranch into ourproductionandstagingHeroku application remotes..FINALLY! WOOOOOOOOOOOoooooooooooooo!
Now we get to enter CHALLENGE MODE! How can we unwind your
.git/configso that it matches the.git/configyou 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-programmerand 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-stagingFirst, we're going to create a remote named
originthat points to your github repository:git remote add origin git@github.com:monkeywithacupcake/jess-is-an-awesome-programmer.gitSecond, we're going to make sure
masteris set up to default pushes to your remote namedorigin, instead of the one namedproductiongit branch --set-upstream-to remotes/origin/masterWhen you run
cat .git/configand you after this, you'll see that the section that configured yourmasterbranch 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 staginggit remote rm productiongit remote rm herokuRun
cat .git/configbetween 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.gitcat .git/config(to see the added lines!)git remote add production https://git.heroku.com/jess-is-an-awesome-programmer.gitcat .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 masteryour commits on your local master branch will be on thejess-is-an-awesome-programmerapplication and so on.Keep in mind that changing the
.git/configby. hand can leave yourgitprogram 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
~/.gitconfigThanks 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/configfile?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 fetchmeans "go to the remote repository and download the new changes to our local repository".git mergemeans "integrate this set of changes into this other branch".git pullmeans more or less "go to the remote repository, download the new changes, merge such changes in the branch" (git fetch + git merge)