Setup a MEAN.JS project. Learn some advanced git.

10 May 2014 - - -

or, how I took my git to the next level.

I used git for some time. Happily branching, pushing, and pulling on my own repositories. I understood and used git flow and topic branches on my projects. I even made a few pull requests on GitHub.

But I recently ran into a git problem that was beyond my understanding of git. I dug in, and over a day or so learned some things I wish had been written down when I started.

This is a post on git:

  • remotes: The links between git repositories for pushing and pulling changes.
  • upstreams: Pointers to remotes that set context for push , pull, and status
  • GitHub: Mixing Public/Private repositories: How to make pull requests to public repo, while saving project code in a private repos

Setting up a new MEAN.JS project makes a great case study. We need a new project stored in a private git repository, yet forked from a public GitHub repository, so we can pull updates, and can submit pull requests back.

> This works for any Public/Private repo setup on GitHub, not just our MEAN.JS project.

If you're not interested in MEAN.JS, no problem. There isn't a line of JavaScript here.

Take a look at this gist of how we'll set it up. This blog explains the purpose and context of each of these commands, so you'll be able to read and know them by the end.

A follow-up post Fast, Cheap, and MEAN: Dev/Stage/Prod server setup with Heroku extends this configuration, creating a fast and free dev/stage/prod environment for your project on the Heroku hosting service.

> Hey! There's alot of different ways to do something like this. > Know a better way?> Please leave a comment below!

About MEAN.JS

MEAN.JS is a full-stack application framework for Node.js. It brings together some great technologies, so you don't need to reinvent the boilerplate code.

Simplest thing that could possibly work

The MEAN.JS project is improving and maturing rapidly. To start a new project we have a couple options:

Option: Download Zip. To simply evaluate MEAN.JS, grab a zip of the master branch and start hacking. Easy peasy.

But, building a professional web application requires version control. A fair amount of code will be written by us. Bug fixes will be written by the MEAN.JS developers. If we're nice, we'll contribute some code back.

We could drop this code in a private repository, but two issues arise:

  1. Updates are hard. Separating core and custom code is non-trivial.
  2. No Pull Requests. We can't contribute code to MEAN.JS.

Option: Yoeman App generator.Cooler, but same issues.

Option: Commit project in Public fork. Fixes problems:

1) Updates
2) Pull requests

But, creates a new problem

3) Project code is public

Even if your project is open source, be careful not to commit your API keys and app secret (config/all.js) to a public repo.

Option: Commit project in Private GitHub fork. Unfortunately, GitHub depricated this feature.

Best Option: Update from Public fork. Commit project to private repo.

Let's get git to do the work for us:

  1. Get updates and bug fixes from MEAN.JS repo.
  2. Make GitHub Pull Requests (2b) by pushing code to our fork (2a).
  3. Collaborate with your team in a private repo.

Big Picture

To accomplish this, we'll have 3 branches locally:

  • master and dev for development
  • meanjs for pulling updates and making pull requests.

This setup might look complicated, but once you grok what's happening it's second nature.

> This tutorial uses git 1.8.3+. Use > git --version> to see your current version. Need an > upgrade> ?

How to start our MEAN.JS project

If you're like me, the what made sense, but the how wasn't immediately obvious. Here it is, step by step:

1) Fork MEAN.JS on github

> GitHub concepts: > Forks> and > Pull Requests

  • > Fork> is a GitHub feature. > Fork> copies someone elses repo, to a copy in your GitHub account. From there, you can make changes, then create > pull requests> .

> E.g. Fork > github.com/mean/meanjs> to > github.com/YOU/meanjs * > Pull requests> are a GitHub feature. They package code, to submit back to the fork's original repository.

> The repo owner can then accept or reject the pull request. This let's the original owner moderate commits back to their repository. * > Fork> doesn't setup any special magic in your local repo for > git push> or > git pull> . Fork set's up a repo for pull requests.

> Other sites like > BitBucket> also offer similar fork and pull request features.

2) Clone your fork locally

Great, we made our fork, now let's clone it locally so we can work with it.

# Clone it
git clone git@github.com:YOU/mean.git PROJECT  
cd PROJECT

# Make your dev branch
git checkout -b dev

> Git Concepts: > Branches> and > Remotes

  • > Branches> . Branches point > inside> a repository. They name and track different commit histories - like a tree. > git branch> , > commit> , and > merge> use > branches> .
  • > Remotes> point > outside> a repository, creating relationships between different repositories. > git push> and > fetch> use > remotes> .
  • > git pull> is a shorthand for > git fetch; git merge FETCH_HEAD> It gets data from an outside remote, then merges into the current branch.

There are now three repositories:

To see your local repository's branches and remotes use git branch -a and git remote -v $ git remote -v origin git@github.com:MichaelJCole/mean.git (fetch)
origin git@github.com:MichaelJCole/mean.git (push)

The origin remote was created automatically for our local repostory when we cloned it.

$ git branch -a
* dev
  master
  remotes/origin/0.3.1
  remotes/origin/HEAD -> origin/master
  remotes/origin/gh-pages
  remotes/origin/master

The branch remotes/origin/gh-pages is a standard way to host a website with GitHub Pages.

Notice: Our local repo has no awareness it was cloned from a forked repository. There is no remote to the original MEAN.JS repository!

These relationships need to be setup manually - they are not created by GitHub's fork feature, or git clone.

3) Configure to pull updates from MEAN.JS.

Our first requirement was to get updates and fixes from the Official MEAN.JS repository.

Let's configure that now.

#  Make a remote called 'upstream' to the Official repo

git remote add upstream https://github.com/meanjs/mean.git

# list remotes
git remote -v  
# notice upstream branches not shown
git branch -a       

# fetch upstream repo data.
git fetch upstream

# now upstream branches are shown
git branch -a       

#  Branch for MEAN.JS code - track upstream 'master' for changes

git branch -a -vv  
git checkout -b meanjs --track upstream/master  
git branch -a -vv  

Notice that git branch -a -vv gives some more info. Check the 2nd column between the [ ]'s.

$ git branch -a -vv
  dev         9503720 Fixing some typos
  master      9503720 [origin/master] Fixing some typos
* meanjs      9503720 [upstream/master] Fixing some typos
  remotes/origin/0.3.1      ...
  ...

> Git Concept: > Upstream Tracking

> Upstream Tracking> is a > git> feature.

> Upstream tracking> connects a > local branch> to a > remote branch> , through a > remote> .

> This offers several benefits:

  • > git status> and > git branch -vv> show relation to remote branch.
  • > Automatic context for > git push> and > git pull> .

    $ git status

    On branch meanjs

    Your branch is ahead of 'upstream/master' by 1 commit.

    (use "git push" to publish your local commits)

    # nothing to commit, working directory clean

    $ git branch -vv dev 9503720 Fixing some typos master 9503720 [origin/master] Fixing some typos

    • meanjs 7cb86aa [upstream/master: ahead 1] This is going to be great!

After configuring the remote upstream, as the upstream to the branch meanjs we still have our three repos:

Task: Update project from Official MEAN.JS:

MEAN.JS is in active development. Let's get any bug-fixes, then merge them to our dev branch.

# Fetch upstream.  Merge upstream/master -> current branch
# Syntax: git pull [remote] [remote branch]

git checkout meanjs  
git pull upstream master

# Or because we used --track to configure branch

git checkout meanjs  
git pull

# Switch to our dev branch and merge changes

git checkout dev  
git merge meanjs  

Note: changes from meanjs/mean won't automatically push up to our GitHub fork.

To merge those changes into our fork, we have to do a push. Like this...

4) Configure for pull requests.

When we git clone'd our fork. It automatically setup a remote called origin. We're going to re-use the name origin later, so let's rename origin to fork

git remote rename origin fork  

Task: Contribute code back to MEAN.JS

This takes three steps:

a. Make a change
b. Use git to push changes to our fork.
c. Use GitHub to make a pull request.

a. Make a change

We want our private code to always stay private. To accomplish this, we created branch meanjs to track the public repo.

If we make changes in dev and merge them to meanjs, we may get funky pull requests, or accidentally submit private code to the repo.

Instead, we always make changes in meanjs, then merge to dev.

# start in branch local/meanjs
git checkout meanjs

# commit a change
touch awesome_feature.js  
git add awesome_feature.js  
git commit -am "This is going to be great! "

# merge change into local/dev and test
git checkout dev  
git merge meanjs  
# Testing...

b. Use git to push changes to our fork.

We can't git push to the original upstream/mean repo because we're not a collaborator.

Instead we push to fork/meanjs. This pushes our changes and the changes we downloaded from mean/meanjs.

# Syntax: [remote] [branch]
git push fork meanjs  

> Gotcha:> To keep your code private, don't push any branch but > meanjs> to > fork> .

> If you do, there are ways to > unwind the push> .

> However, it may be simplest to just delete your fork from GitHub (make sure your pull requests have been satisfied, or be prepared to recreate them), and refork.

c. Use GitHub to make a pull request.

Last, visit your fork with a browser, click "Pull requests", and make your pull request from branch meanjs. Please don't click "Send pull request" with test code.

We complete our 2nd requirement with the same three repos:

5) Setup our private repo

Now, anything we push to our fork on github will become public code.

To keep our private code private:

  1. Make public code changes ONLY in local/meanjs branch.
  2. Only merge FROM local/meanjs TO local/dev (or topic branch)
  3. Only push local/meanjs to fork with git push fork meanjs

All our private code will be in our other branches dev, master, etc.

We can use any private repo. I use bitbucket because I'm cheap and they offer free private repo's. (Don't forget to setup your ssh key).

By convention, a project's main remote is named origin.

git remote add origin git@bitbucket.org:YOU/PROJECT.git  
git branch -a -vv

# Push local branches to origin.  
# Set upstream so they push/pull to origin automatically

git push --set-upstream origin master  
git push --set-upstream origin dev  
# leave meanjs's upstream as remote 'upstream'
git push origin meanjs  

git branch -a -vv  

git branch and git push both offer options to configure Upstream Tracking. Notice that the word "upstream" is overloaded as both a concept (--set-upstream) and a standard name for a remote (git remote add upstream ).

We finish with four repos:

This completes last 3rd requirement: Private code. We can commit, push, and pull to this private repo, using the basics.

> Hurray! Our work is now backed up! > Not so fast...

> Our local repo has 'remotes' configuration that is not transferred via > push> , > pull> , or > clone> . This needs to be documented and configured manually by the people we collaborate with.

If we add a new collaborator to our private repo, they:

  1. can commit and push code to any branch in our private repo.
  2. can accidentally commit private code their meanjs - locally and in origin.
  3. cannot to push to your GitHub fork github.com:YOU/mean (unless you add them in "Access Management")
  4. cannot make pull requests from your fork. (unless you add them as a "Collaborator")

Good job!

  1. Your code is private.
  2. Your getting updates from the official repo
  3. Your can contribute back to the project.

Here's a gist summarizing what we did.

> Hey! There's alot of different ways to do something like this. > Know a better way?> Please leave a comment below!

Next steps: Dev/Stage/Prod

Now that we've got our version control setup, how about a quick Dev/Stage/Prod system?

My blog post Fast, Cheap, and MEAN: Dev/Stage/Prod server setup with Heroku extends this configuration with Heroku to give you a $free and easy hosted dev/stage/prod environment.