My Recommended Git Workflow

From Public wiki of Kevin P. Inscoe
Revision as of 20:08, 23 October 2020 by Ah2l671Liu8Hah$K2eit (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Note: This was written and taught at my previous employer where Github Enterprise was used. However the principles will work just fine with public Github or ay Git server really.

Initial forking

The first thing I do is fork the project I want to contribute to. 

Go into Github Enterprise (GHE) and fork the original project.

This will likely cause a fork to a new repo (under your username) for example:

If you look under Forks for original project you should see your forked project listed under your Github username.

Once the project on Github is forked, I clone the fork on my machine:

$ git clone

Git automatically names my remote forked project origin.

$ git remote -v
origin (fetch)
origin (push)

Then I need to add an upstream reference to the original project:

$ git remote add upstream
$ git remote -v
origin (fetch)
origin (push)

Now my local repository references my fork under the name origin and the original project under the name upstream.


In this workflow, I never work on the master branch. So, when I need to fix a bug for example, I create a new branch:

$ git checkout -b bugfix

To see what is your current branch look for the asterisk "*":

$ git branch
* Pistons-1580

I can then make changes, test my code, make sure everything is ok, stage and commit my changes:

$ git add .
$ git commit -m "commit message"

Now I need to push this local branch to my repository on Github:

$ git push -u origin bugfix

Working on more than one branch at a time

In the same repo perhaps you are making multiples changes due to multiple tickets or issues. If you use a new branch all modified code will be tracked in the new branch causing confusion.

I used to just clone the fork into a separate directory say and switch directories.

However I now recommend using worktrees. See and

Worktrees are implemented using hard links (so this only works on unix or unix like systems only which include Mac or Cygwin). Worktrees are lightweight and fast (whereas the git clone command copies down a whole nether copy of the repo). You can share changes between worktrees as long as they are committed to the local repo). With multiple clones you would have to push commits to the remote and then pull to the other. You can also use worktrees to do ‘what-if’s’ or testing things.

Note that you must have Git 2.5 (or your IDE must support) or higher to support worktrees. 

Say you have already checked out a branch called 'Pistons-1580' and made changes however for whatever reason work on this branch/ticket has stalled. Now you want to work on a new ticket say 'Pistons-2204'.

$ git branch
* Pistons-1580
$ git branch Pistons-2204
$ git branch
* Pistons-1580
$  git worktree list         
/Users/inscoek/Projects/Pistons/  f51e918 [Pistons-1580]
Note: You can create this directory anywhere on the same filesystem (remember it’s a Unix [ hardlink]), but it should be somewhere outside of your main repository directory!
$ git worktree add /Users/kevin/Projects/Pistons/ Pistons-2204
Preparing worktree (checking out 'Pistons-2204')
HEAD is now at f51e918 Updated remote-states file updated due to Atlantis migration
$ git worktree list
/Users/inscoek/Projects/Pistons/               f51e918 [Pistons-1580]
/Users/inscoek/Projects/Pistons/  f51e918 [Pistons-2204]

Now to start working on code in issue Pistons-2204 you switch branches now by changing directories. Git automatically know by the worktree directory which branch to track code in. We no longer use git branch command when switching while using worktrees. 

$ pwd
$ git branch
* Pistons-1580

Now to work on Pistons-2204

$ cd /Users/inscoek/Projects/Pistons/
$ git branch
* Pistons-2204

Follow normal workflow at this point.

After your worktree branch is committed and merged you can remove it

$ git worktree list
/Users/inscoek/Projects/Pistons/               f51e918 [Pistons-1580]
/Users/inscoek/Projects/Pistons/  f51e918 [Pistons-2204]
$ git worktree remove /Users/inscoek/Projects/Pistons/

Conflicts and rebasing


Eventually everyone will come into conflicts with multiples changes on the same repo. These conflicts will always be on different branches (or should be).

"In general the way to get the best of both worlds is to rebase local changes you’ve made but haven’t shared yet before you push them in order to clean up your story, but never rebase anything you’ve pushed somewhere." See also

$ git rebase master
$ git push origin Pistons-3389
 ! [rejected] Pistons-3389 -> Pistons-3389 (non-fast-forward)
error: failed to push some refs to ''
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

In this case it is ok to use --force option if the commits are only your own.

$ git push origin Pistons-3389 --force
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 1.36 KiB | 1.36 MiB/s, done.
Total 7 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), completed with 5 local objects.
 + f78b146...57f5039 Pistons-3389 -> Pistons-3389 (forced update)

Pull request

Since I forked the original project, Github knows that origin and upstream are linked. If there are no conflicts, github will show me a big green button to create a pull request. Once the pull request is created, I just have to wait for the maintainer to merge it in upstream’s master branch. Then, I need to sync both my local copy and my fork on github with the original project. In order to do that, on my local copy, I checkout my master branch, fetch upstream’s changes, and merge them.

Examples of good pull requests:

Working with someone else's branch

Check out the (pull request) PR in question

$ git fetch upstream pull/197/head:DAP-942
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Total 11 (delta 8), reused 8 (delta 8), pack-reused 3
Unpacking objects: 100% (11/11), 2.05 KiB | 261.00 KiB/s, done.
 * [new ref]         refs/pull/197/head -> DAP-942
$ git checkout DAP-942

This will checkout the upstream PR number 1234 and create a branch called PR-1234 locally


Merge pull request on Github via button.

Bringing fork up to date again

Then to bring my local repo (and then fork) current with upstream.

$ git checkout master
$ git fetch upstream
$ git rebase -i upstream/master

Now my local master branch (Forked) is ahead of origin’s master branch, so I push those changes to Github to the forked repo:

$ git push


I don’t need the bugfix branches (the local one and the Github one), so I can delete those :

This is optional as you can continue to use the same branch but I personally prefer to clean up afterwards.

$ git branch -d bugfix
$ git push origin -d bugfix
And now, my local repository is even with both origin and upstream, and I can start again.

Squashing commits

$ git rebase -i HEAD~4 (or the number of commits to squash)

Change pick to squash on lines unnecessary

$ git push --force origin branch

Tagging commits as releases

See and for futher.

To tag a commit (annotated):

$ git add <some files>
$ git commit -m "This is release V1.5"
$ git tag -a v1.5 -m "Release V1.5"

By default, the git push command doesn’t transfer tags to remote servers. You will have to explicitly push tags to a shared server after you have created them. This process is just like sharing remote branches — you can run git push origin <tagname>.

$ git push origin v1.5


To summarize, here’s the complete workflow :

$ git checkout -b myawesomefeature
$ git add .
$ git commit -m "Awesome commit message"
$ git push -u origin myawesomefeature

Create a pull request, wait for the maintainer to merge it.

$ git checkout master
$ git fetch upstream
$ git merge upstream/master
$ git push
$ git branch -d myawesomefeature
$ git push origin -d myawesomefeature