Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Git basics - a general workflow

Git-workflow or feature branching

When working with Git, there are two prevailing workflows are Gitflow and feature branches. IMHO, being more of a subscriber to continuous integration, I feel that the feature branch workflow is better suited, and the focus of this article.

If you are new to Git and Git-workflows, I suggest reading the atlassian.com Git Workflow article in addition to this as there is more detail there than presented here.

I admit, using Bash in the command line with the standard configuration leaves a bit to be desired when it comes to awareness of state. A tool that I suggest using follows these instructions on setting up GIT Bash autocompletion. This tool will assist you to better visualize the state of a branch in regards to changes and being in sync with the remote repo.

Basic branching

When working in a decentralized workflow, the concepts can be simple. master represents the official history and should always deployable. With each new scope of work, aka feature, a developer creates a new branch. For clarity, make sure to use descriptive names like transaction-fail-message or github-oauth for your branches.

Although you may have a feature like 'user login and registration`, it may not be appropriate to create a feature branch at this level, there is too much work to be done. It may better to break these large deliverables down to smaller bits of work that can be continuously integrated into the project. Remember, commit early and often.

That being said, there are times when a single branch will be needed to deliver a large feature or prepare for a release. In this scenario, I'd suggest reading over the Git Commit Guidelines created by the Angular team at Google. As they put it, it is a series of '... precise rules over how our git commit messages can be formatted'. As of late, I make heavy use of these commit message guidelines in all the projects I am working on.

Before you create a branch, always be sure you have all the upstream changes from the origin/master branch.

Make sure you are on master

Before I pull, I make sure I am on the right branch. I have GIT Bash autocompletion installed, so this is clear from the prompt. Otherwise, the following command is good to know to list out the branches I have locally as well as designate which branch I am currently on.

$ git branch

The checked out branch will have a * before the name. If the return designates anything other then master then switch to master

$ git checkout master

Once on master and ready to pull updates, I use the following:

$ git pull origin master

Depending on your setup, you may even be able to run only the following:

$ git pull

The git pull command combines two other commands, git fetch and git merge. When doing a fetch the resulting commits are stored as a remote branch allowing you to review the changes before merging. Merging, on the other hand, can involve additional steps and flags in the command, but more on that later. For now, I'll stick with git pull.

Now that I am all up to date with the remote repo, I'll create a branch. For efficiency, I will use the following:

$ git checkout -b my-new-feature-branch

This command will create a new branch from master as well as check out out that new branch at the same time. Doing a git branch here again will list out the branches in my local repo and place a * before the branch that is checked out.

  master
* my-new-feature-branch

Do you have to be on master to branch from master?

No. There is a command that allows me to create a new branch from any other branch while having checked out yet another branch. WAT?!

$ git checkout -b transaction-fail-message master

In that example, say I was in branch github-oauth and I needed to create a new branch and then check out the new branch? By adding master at the end of that command, Git will create a new branch from master and then move me (check out) to that new branch.

This is a nice command, but make sure you understand what you are doing before you do this. Creating bad branches can cause a real headache when trying to merge back into master.

Some files should be ignored

In any Git project, you will find a .gitignore file. This is a simple registry file where you can list the files you DO NOT want to commit to the repo. This would be any files that contain secret information like keys and tokens or any sensitive server configurations.

To read more about the basics of a .gitignore file, read ignoring files from the Github site.

The only thing I want to mention is if a resource has already been added to Git's control, adding it later to the .gitignore file will not ignore that file. The thing to remember here is that the intention is to remove the file from Git control, but not from disk. To do this, use the following command:

$ git rm --cached <filename>

For example, say we had a directory with the filed index.html and then the file config.yml, but the config file had a lot of secret info and it was accidentally added to the Git version control.

In the .gitignore file, first we would add config.yml then in the command prompt, run the following command:

$ git rm --cached config.yml

After running the command, you should see that config.yml is still in the directory, but running the following command, you would see that it is NOT being tracked by Git:

$ git ls-tree -r master --name-only

Branch management

When I am working on my new feature branch, it is a good idea to commit often. This allows me to move forward without fear that if something goes wrong, or you have to back out for some reason, I don't lose too much work. Think of committing like that save button habit you have so well programed into you.

Each commit also tells a little bit about what I just worked on. That's important when other devs on the team are reviewing my code. It's better to have more commits with messages that explain the step versus one large commit that glosses over important details.

Commit your code

As I am creating changes in my project, these are all unseated updates. With each commit there most likely will be additions, and there will also be deletions from time to time. To get a baring of the updates I have made, let's get the status.

$ git status

This command will give you a list of all the updated, added and deleted files.

To add files, I can add them individually or I can add all at once. From the root of the project I can use:

$ git add .

In order to remove deleted files from the version control, I can again either remove individually or from the root address them all like so:

$ git add -u

I'm lazy, I don't want to think, so I make heavy use of the following command to address all additions and deletions.

$ git add --all

All the preceding commands will stage the updates for commitment. If I run a git status at this point, I will see my updates presented differently, typically under the heading of Changes to be committed:. At this point, the changes are only staged and not yet committed to the branch. The next step is to commit with a message. Here is where I lean on the Angular style commit messages linked to above. To commit, do the following:

$ git commit -m "<type>(<scope>): <subject>"

It is considered best to illustrate your comment in the tense that this will do something to the code. It didn't do something in the past and it won't do something in the future. The commit is doing something now.

A bad subject example would be:

$ git commit -m "fixed bug with login feature"

A good subject example would be:

$ git commit -m "update app config to address login bug" 

Comments are cheap. For more on how to write expressive commit messages, read 5 Useful Tips For A Better Commit Message.

Push your branch

When working with feature branches on a team, it is typically not appropriate to merge your own code into master. Although this is up to your team as to best manage, the norm is usually to make pull requests. Pull requests require that you push your branch to the remote repo.

To push the new feature branch to the remote repo, simply do the following:

$ git push origin my-new-feature-branch

As far as Git is concerned, there is no real difference between master and a feature branch. So, all the identical Git features apply.

My branch was rejected?

There is a special case when working on a team and the feature branch being pushed is out of sync with the remote. To address this is pretty simple with the following command:

$ git pull origin my-new-feature-branch

This will fetch and merge any changes on the remote repo into the local feature branch with all the changes addressing any issues with diffs in the branch's history, now allowing you to push.

Working on remote feature branches

When I am creating the feature branch, this is all pretty simple. But when I need to work on a co-workers branch, there are a few additional steps that I follow.

Tracking remote branches

My local .git/ directory will, of course, manage all my local branches, but my local repo is not always aware of any remote branches. To see what knowledge my local branch has of the remote branch index, adding the -r flag to git branch will return a list.

$ git branch -r

To keep my local repo 100% in sync with deleted remote branches, I make use of this command:

$ git fetch -p

The -p or --prune flag, after fetching, will remove any remote-tracking branches which no longer exist.

Switching to a new remote feature branch

Doing a git pull or git fetch will update my local repo's index of remote branches. As long as co-workers have pushed their branch, my local repo will have knowledge of that feature branch.

By doing a git branch you will see a list of your local branches. By doing a git branch -r you will see a list of remote branches.

The process of making a remote branch a local branch to work on is easy, simply check out the branch.

$ git checkout new-remote-feature-branch

This command will pull its knowledge of the remote branch and create a local instance to work on.

Keeping current with the master branch

Depending on how long you have been working with your feature branch and how large your dev team is, the master branch of your project may be really out of sync from where you created your feature branch.

When you have completed your update and prior to creating a pull request, you not only have to be up to date in order to merge your new code but also be confident that your code will still work with the latest version of master.

It's here where there are two very different schools of thought. Some teams don't mind if you PULL the latest version from master, by simply doing the following.

$ git pull origin master

This will fetch and merge any changes on the remote repo into the local branch with all the changes, now allowing your feature branch able to be merged. This works for the purpose of merging, but it's kind of gross on the branch's history graph.

Then there are teams who are not a fan of this process, simply because pulling from origin can really screw up the feature branch's history and make it harder to perform more advanced Git features if needed. So, in these situations, it's best to REBASE O_O.

Rebasing a feature branch is not as scary as most make it seem. All a rebase really is, is taking the updates of the feature branch and moving them to a new spot in the history as to be on top of the latest version of master. It's as if you just created that feature branch and made the updates. This creates a very consistent branch history that is easy to follow and easy to work within all situations.

To rebase your local feature branch off of the latest version of master, following these steps will be a guarantee every time.

$ git checkout master         /* ensure you are on the master branch
$ git pull                                   /* pull the latest from the remote 
$ git checkout my-feature-branch      /* checkout the feature branch
$ git push origin my-feature-branch  /* update your copy in the repo
$ git rebase master              /* rebase on the master branch
$ git push origin my-feature-branch --force   /* force update the remote

And that's it. This process will ensure that you have the latest version of master then take the commits from your feature branch, temporarily unset them, move to the newest head of the master branch and then re-commit them. As long as there are no conflicts, there should be no issues.

Force push? But ...

Yes, there are those who are not fans of force pushing, but in the case of a feature branch, this is ok. Now force pushing to master, well, that's a really bad idea.

When performing operations like rebasing you are in effect changing the branch's history. When you do this, you can't simply push to the remote as the histories are in conflict, so you will get rejected. To address this, adding the --force flag to force push tells the remote to ignore that error and replace the previous history with the new one you just created.

Conflicts

In the worst-case scenario, there is a conflict. This would happen even if you did the pull directly from master into the feature branch. To resolve a conflict, read the error report in the command prompt. This will tell you what file has a conflict and where.

When opening the file you will see a deliberate break in the file's content and two parts that are essentially duplicated, but slightly different. This is the conflict. Pick one of the versions and be sure to delete all the conflict syntax injected into the document.

Once the conflict(s) is/are resolved, back in the command line you need to add this correction.

$ git add .

Once the new updates are staged, you can't commit again as this process is inside a previous commit. So you need to continue with the rebase, like so:

$ git rebase --continue

If for any reason you get stuck inside a rebase that you simply can't make sense of, you can abort the rebase;

$ git rebase --abort

As long as there are no other issues, the rebase will continue and then complete with an output of the updates.

The Pull Request

The pull request is where the rubber meets the road. As stated previously, one of the key points of the feature branch workflow is that the developer who wrote the code does not merge the code with master until there has been through a peer review. Leveraging Github's pull request features, once you have completed the feature branch and pushed it to the repo, there will be an option to review the diff and create a pull request.

In essence, a pull request is a notification of the new code in an experience that allows a peer developer to review the individual updates within the context of the update. For example, if the update was on line 18 of header.html, then you will only see header.html and a few lines before and after line 18.

This experience also allows the peer reviewer to place a comment on any line within the update that will be communicated back to the editor of origin. This review experience really allows everyone on the team to be actively involved in each update.

Once the reviewer has approved the editor's updates, the next step is to merge the code. While it used to be preferred to merge locally and push master, Git has really grown into this feature and I would argue today it is most preferred to simply use the GUI took in Github.

Now the question is, how to merge? Github has three options; Create a merge commit Squash and merge Rebase and merge

Creating a merge commit is ok, this will simply merge in the new feature branch code into the master branch as long as there are no conflicts. Github will not allow you to merge if it already knows there will be conflicts.

The squash and merge process is interesting as this will compact all the commits to this feature branch into a single commit to the master branch. This may or may not be an issue depending on how your team want's to preserve history. If you are a user of the Angular commits, you may not want to use this feature.

Lastly, there is the rebase and merge. Showing my preference for rebasing earlier, I am definitely a fan of this merge action. This will do exactly what I explained earlier. It will take all the commits of the feature branch and reset them to the latest head of the master branch. If you did a rebase on the feature branch prior to creating a pull request, this process will be seamless and in the end, the most healthy for the project's history.

Shortcuts using aliases

There are some steps in there that we should just be doing all the time. What about making a single command alias that will cycle through all these commands just so we know things are always in good shape? Yup, we can do that.

In Bash

Using Git and Bash is like using a zipper and pants. They just go together. Creating a Bash alias is extremely simple. From your Terminal, enter

$ open ~/.bash_profile

This will open a hidden file in a default text editor. If you have a shortcut command for opening in an editor like Sublime Text or VS Code, use that to open the file. In the file add the following:

alias refresh="git checkout master && dskil && git pull && git fetch -p"

The alias dskil is useful for removing annoying .DS_Store files. You should have a .gitignore file that keeps these out of version control, but I like to keep a clean house too. To make that work, add the following:

alias dskil="find . -name '*.DS_Store' -type f -delete"

With this in your .bash_profile, you simply need to enter refresh in the command line and POW!

In Powershell

If you are on Windows and using Powershell, you can use the same aliases, but the set up is different. The article Windows PowerShell Aliases is a good tutorial of the process.

Summary

Following this simple workflow has helped keep my projects clean and Git hell free. I hope it serves you well too.

But ... the story doesn't end here. Well, the post does, but the story does not. There are many more advanced features that your team can use. Outside of that, for more in-depth learning on Git, I invite you to read the Git book, it's free and contains awesome learning.

@aao13

This comment has been minimized.

Copy link

aao13 commented Feb 22, 2016

typical Linux warped thinking - simplest of all source control operation , such as merge 2 versions of the same file , becomes convoluted to the extreme, they way the author savors the experience is asinine

@komaxx

This comment has been minimized.

Copy link

komaxx commented Mar 2, 2016

Good, concise description. Thank you!

@BroVic

This comment has been minimized.

Copy link

BroVic commented Apr 18, 2016

The article was helpful. Thanks!

@CoreyGumbs

This comment has been minimized.

Copy link

CoreyGumbs commented Oct 23, 2016

Thank you so much.

@2gingersnaps

This comment has been minimized.

Copy link

2gingersnaps commented Oct 30, 2016

Bravo!

@upendrak

This comment has been minimized.

Copy link

upendrak commented Feb 22, 2017

Nice tutorial. Thanks

@kimstone

This comment has been minimized.

Copy link

kimstone commented Mar 19, 2017

Very well written. Great breakdown.

@nitchow

This comment has been minimized.

Copy link

nitchow commented May 16, 2017

Superb article - this is pretty much my ref when I do pull requests.

@dreftymac

This comment has been minimized.

Copy link

dreftymac commented Aug 18, 2017

I wish to react with a thumbs-up emoji.

@NoashX

This comment has been minimized.

Copy link

NoashX commented Aug 28, 2017

"Make sure that the feature branch is up to date with master, while in the feature branch, execrate the following:"

I think you mean execute?

@sacredabhishek

This comment has been minimized.

Copy link

sacredabhishek commented Oct 27, 2017

helpful article @blackfalcon

@lifeF

This comment has been minimized.

Copy link

lifeF commented Nov 1, 2017

thanks

@stanlee321

This comment has been minimized.

Copy link

stanlee321 commented Nov 8, 2017

Gracias!

@boundaries-io

This comment has been minimized.

Copy link

boundaries-io commented Nov 9, 2017

Thank you sir.

@fabiomarcos70

This comment has been minimized.

Copy link

fabiomarcos70 commented Nov 24, 2017

Very smart and helpfull article. Thanks a lot

@comtechnet

This comment has been minimized.

Copy link

comtechnet commented Dec 12, 2017

I think there is a typo ...

you wrote ...

Basic branching

When working with a centralized workflow the concepts are simple, master represented the official history and is always deployable. With each now scope of work, aka feature, the developer is to create a new branch. For clarity, make sure to use descriptive names like transaction-fail-message or github-oauth for your branches.

I think you mean ....

Basic branching

When working with a centralized workflow the concepts are simple, master represented the official history and is always deployable. With each new scope of work, aka feature, the developer is to create a new branch. For clarity, make sure to use descriptive names like transaction-fail-message or github-oauth for your branches.

@SkybuckFlying

This comment has been minimized.

Copy link

SkybuckFlying commented Dec 25, 2017

Deleting anything is a big fat no-no. Certainly don't like the "delete branch concept". If for example remote server is nuked, bye bye any changes.
Deleting changes is just dumb ? Why the hell would anybody think this is a good idea is beyond me ? These branches offer a nice insight into what changes were made... Deleting that is just dumb dumb dumb dumb dumb.
At least some history is kept in the commits/merges to see what was changed ?! But what if merger decides to merge many branches before commiting ?! Again these nice smaller litte branches of changes would be lost ! Very odd idea to delete anything for a version control system. Terrabytes plenty...

A better idea would be to "close a branch" and marking it as "merged". And that's coming from a total git noob=me. I think tagging could be used for this... not sure.

Ok now that I worked with git myself a bit I see how easy it is to make a mistake... typos... and being at the wrong head... so now I understand this feature... it's still all pretty dangerous though ! =D

@tomcoders

This comment has been minimized.

Copy link

tomcoders commented Jan 26, 2018

Thank you for this concise git user guide.

@doha12

This comment has been minimized.

Copy link

doha12 commented Jan 30, 2018

Using git fetch upstream master pulled in the changes from the master branch on the upstream remote repository.

What single command would we use if we want to fetch the upstream/master changes and merge them into the master branch?


@suessflorian

This comment has been minimized.

Copy link

suessflorian commented Feb 11, 2018

Cheers

@AbdelrahmanKabel

This comment has been minimized.

Copy link

AbdelrahmanKabel commented Feb 19, 2018

@doha12
"git pull " is the same thing as a "git fetch" + "git merge"
so the single command is:
"git pull upstream master"

@Omar-Suliman

This comment has been minimized.

Copy link

Omar-Suliman commented Feb 22, 2018

thank you so much

@Jyothireddyb

This comment has been minimized.

Copy link

Jyothireddyb commented Mar 12, 2018

I couldnot get what a pullrequest is???

@Vanshika-Mittal

This comment has been minimized.

Copy link

Vanshika-Mittal commented May 28, 2018

How do I tie my GitBash to my GitHub account? Also, can you tell me where my GitBash commits go if its not tied to my account.

@eylulyurdakul

This comment has been minimized.

Copy link

eylulyurdakul commented Oct 9, 2018

This is a super useful article with very clear explanations, thank you!

@rtsuh

This comment has been minimized.

Copy link

rtsuh commented Oct 17, 2018

Very helpful - thank you!

@kushalmahajan

This comment has been minimized.

Copy link

kushalmahajan commented Nov 12, 2018

You just documented, what I would have documented myself. You haven't missed a point. In fact learnt a couple of things more. --no-ff and alias features.

@gsamaras

This comment has been minimized.

Copy link

gsamaras commented Nov 19, 2018

There is a typo in that command: $ git push origin my-new-feature-brach

Fixed it, OP can merge from here: https://gist.github.com/gsamaras/0d0b3e40100a6ff7fd68a7cbbb6d6439

@dflourusso

This comment has been minimized.

Copy link

dflourusso commented Nov 20, 2018

Great article, thank you.

@bbneo

This comment has been minimized.

Copy link

bbneo commented Dec 8, 2018

Thank you for this. If you are just working on a project by yourself and just want to use GitHub as a central synchronization for your work (pulling from one place (e.g. work) and then pushing any changes right back to GitHub and then pulling it from another place (e.g., home) and doing the same)
do you need to worry about making branches?

@jandrebu

This comment has been minimized.

Copy link

jandrebu commented Mar 21, 2019

Great and thanks!

@GEMcDermott

This comment has been minimized.

Copy link

GEMcDermott commented May 3, 2019

Thank you. Great tutorial!

@sumitchinchansure

This comment has been minimized.

Copy link

sumitchinchansure commented Jun 6, 2019

Thank you so much for this Article..!!!
Explained very nicely

@KittyBot

This comment has been minimized.

Copy link

KittyBot commented Jun 24, 2019

This is very helpful. :) Thank you!

@bhpayne

This comment has been minimized.

Copy link

bhpayne commented Aug 30, 2019

@SevCoelho

This comment has been minimized.

Copy link

SevCoelho commented Sep 23, 2019

typical Linux warped thinking - simplest of all source control operation , such as merge 2 versions of the same file , becomes convoluted to the extreme, they way the author savors the experience is asinine

Wow

@sherribooher

This comment has been minimized.

Copy link

sherribooher commented Oct 29, 2019

Thank you. Well-written tutorial. And ignore the troll @aao13

@Weewolh

This comment has been minimized.

Copy link

Weewolh commented Mar 30, 2020

Thanks !

@rd67

This comment has been minimized.

Copy link

rd67 commented May 10, 2020

Thank you

@darrentmorgan

This comment has been minimized.

Copy link

darrentmorgan commented Jul 2, 2020

This was really concise and helpful. Thankyou!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.