I have been studying Git and here is what I learned.

Using a Git workflow

This tutorial about the Git branching model is awesome. Read here

I am following this simple version for branches:

  • Master
  • Develop
  • hotfixes

I start in master.

Create a branch develop to build everything.

When the tests pass or at least most of them, then switch to master and merge develop.

Create a hotfix branch to resolve tests that didn’t pass or things I broke. After merging them to master and develop. The hotfix branches are deleted.

Using a CHANGELOG to bump the versions merging develop to master and hotfix to master.

I follow the “keep a changelog” tutorial here and a light semantic version from the Semver website here. Using the Major.Minor.Patch syntax.

I use Sublime or vim for scripts or small projects. Lately I use IntelliJ Ultimate which I find amusing with the auto completion, shortcuts, and the git diff tool to see changes.

The Pro Git is a great resource to learn Git in more detail.

Working with master and develop

Initialize the repo with git init or clone the repo.

Create the CHANGELOG file with touch CHANGELOG.md.

Create the develop branch git branch develop.

Work on the develop branch.

Commit in small increments

(develop)$ git commit -am "Short summary of change"

Switch to master and merge

(master)$ git checkout master
(master)$ git merge develop -m "Short summary of merge"

Update CHANGELOG file with next version <version number>. Then commit:

(master)$ git commit CHANGELOG.md -m "Bumped version to <version number>"

Create an annotated tag for the <version number>.

(master)$ git tag -a <version number>

Push changes to remote if needed:

(master)$ git push origin master

Creating hotfixes of failed tests or things I messed up

Create a branch from master increasing the patch version number of Major.Minor.Patch. If the current version is 1.2 then:

(master)$ git checkout -b hotfix-1.2.1 master

Update the CHANGELOG to increase the version then commit.

(hotfix-1.2.1)$ git commit CHANGELOG.md -m "Bumped version number to 1.2.1"

Make the changes and commits in small increments.

Then merge with master and tag

(master)$ git checkout master
(master)$ git merge --no-ff hotfix-1.2.1
(master)$ git tag -a 1.2.1

Also merge with develop

(master)$ git checkout develop
(develop)$ git merge --no-ff hotfix-1.2.1

Then delete the branch

(develop)$ git branch -d hotfix-1.2.1

Initialize a Git repository

Initializing a git repo and creating a branch without committing to master causes this strange behavior. Also reported in this blog post.

$ git init
$ git status
On branch master

No commits yet

$ git checkout -b develop
$ git status
On branch develop

No commits yet

$ git checkout master
error: pathspec 'master' did not match any file(s) known to git

To avoid this. Make a first commit to master then create a branch

$ git init
$ touch .gitignore
$ git add .
$ git commit -m "Added gitignore"
[master (root-commit) 98c55ef] Added gitignore
(master)$
$ git checkout -b develop
(develop)$
$ git checkout master
Switched to branch 'master'
(master)$
$ git branch
  develop
* master

Other Git/Github tutorials I wrote:

My take on the Git cheat sheet

  • Set your email for all repos git config --global user.email "your email"
  • Set your user name for all repos git config --global user.name "your name"
  • Set yur email and user name for a specific repo. Do the same but remove the --global flag.
  • Using short status: git status -s
  • Setup .gitignore
  • Use git diff to see staged/unstaged changes or use an IDE
  • Removing files from Git git rm <filename>
  • Rename files git mv <filename>
  • View the commit history git log
  • Last commit git log -1
  • Ammend the last commit to change the comment git commit --amend
  • Log in one-liner git log --pretty=oneline
  • Show the last 4 commits git log --pretty=oneline -4
  • View the URLs of the remote git remote -v
  • Fetch data from origin git fetch origin
  • Merge remote with local branch git pull
  • In git push origin master the word origin is just a shortname for the remote. You can rename to homersimpson if you want.
  • Show more info about a remote git remote show origin
  • Rename a remote git remote rename origin to homersimpson
  • Check the remote again git remote -v shows homersimpson https://somerepo
  • Show the number of tags git tag | wc -l
  • With git tag show all the tags
  • Show only those that start with v2: git tag -l "v2.2*"
  • Show the details of a tag git show <version number>
  • Create a lightweight tag: git tag <version number>
  • Create an annotated tag: git tag -a <version number>
  • Create a shortcut alias like this: git config --global alias.last5 'log --pretty=oneline -5'
  • Then run git last5 to get the same output
  • Create a branch git branch newBranch
  • Switch to that branch git checkout newBranch
  • Create a branch and switch to it in one command git checkout -b anotherBranch
  • List local branches $ git branch
  • History of commits git log --all --decorate --oneline --graph
  • Last commit in each branch git branch -v

Add Git branch to the command prompt

Edit .bashrc as seen on stackoverflow here:

# Git branch name
parse_git_branch() {
        git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}

Add this to .bashrc after the above function. The enclosed \e[0;31m ....\e[m will set the command prompt to red.

PS1="\e[0;31m\h\$(parse_git_branch)\$ \e[m"

Merging conflicts

When doing git merge, the output might say CONFLICT: Merge conflict in <filename here>

Open the conflict file manually and correct the conflict.

If the file shows something like this:

<<<<<< HEAD changes2.md
Updated changes2
================
Finished feature2
>>>>>>> feature2:changes2.md

Then the master branch has the content Updated changes2 and the feature2 branch has Finished feature2. Remove any Git notes and leave the correct changes such as:

Updated changes2
Finished feature2

Then commit the conflict:

(master)$ git add changes2.md
(master)$ git commit -m "Merge conflict fixed for feature2"

More about merge tools:

  • Use an IDE
  • Using git mergetool or advanced merging in the Git book here
  • Find tools available git mergetool --tool-help
  • Using vimdiff tutorial here

Git rebase

More about rebase here

See everything done in git with git reflog

Use git reflog as seen on the Dangit Git tutorial here

(master)$ git reflog
aaaf830 (HEAD -> master) [email protected]{0}: commit (merge): Merged conflict for cities
32a6bb7 [email protected]{1}: checkout: moving from newBranch to master
37f3122 (newBranch) [email protected]{2}: commit: Added New Orleans to Cities
5cb15fc [email protected]{3}: checkout: moving from master to newBranch
32a6bb7 [email protected]{4}: commit: Added Houston to Cities
f4b7070 [email protected]{5}: commit (merge): Merge conflic fixed
3c897b2 [email protected]{6}: checkout: moving from newBranch to master
e567c27 [email protected]{26}: commit: Deleted names.txt
a715798 [email protected]{27}: commit: Added a city to cities
a1b0314 [email protected]{28}: commit (initial): Created files names and cities

Go back in time to a previous commit with this syntax:

$ git reset [email protected]{index}

Committed to the wrong branch

Commited to the wrong branch master:

(master) $ echo "England" >> countries.csv
(master) $ git commit -am "Added England to Countries"

Check the log:

(master) $ git last5

4c769 (HEAD -> master) Added England to Countries
aaaf8 Merged conflict for cities
37f31 (newBranch) Added New Orleans to Cities
32a6b Added Houston to Cities
f4b70 Merge conflic fixed

Use git reset HEAD~ --soft to remove the last commit from master but keep the changes in the working directory.

(master) $ git reset HEAD~ --soft

(master)$ git last5
aaaf8 (HEAD -> master) Merged conflict for cities
37f31 (newBranch) Added New Orleans to Cities
32a6b Added Houston to Cities
f4b70 Merge conflic fixed
5cb15 Added Rome to Cities

(master)$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   countries.csv

Use git stash to save the state of the working directory and index. More about git stash in the official docs here.

(master) $ git stash

Saved working directory and index state WIP on master: aaaf830 Merged conflict for cities

(master)$ git status
On branch master
nothing to commit, working tree clean

Switch branches and use git stash pop to remove the last state from the stash list and apply it to the current branch.

(master) $ git checkout -b addEngland
Switched to a new branch 'addEngland'

(addEngland) $ git stash pop
On branch addEngland
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   countries.csv

(addEngland)$ cat countries.csv 
France
England

(addEngland)$ git commit -am "Added England to Countries"
[addEngland 99f82cb] Added England to Countries
 1 file changed, 1 insertion(+)

Check the log in the branch:

(addEngland)$ git last5
99f82 (HEAD -> addEngland) Added England to Countries
aaaf8 (master) Merged conflict for cities
37f31 (newBranch) Added New Orleans to Cities
32a6b Added Houston to Cities
f4b70 Merge conflic fixed

Go to master branch

(addEngland)$ git checkout master
Switched to branch 'master'

(master)$ git last5
aaaf8 (HEAD -> master) Merged conflict for cities
37f31 (newBranch) Added New Orleans to Cities
32a6b Added Houston to Cities
f4b70 Merge conflic fixed
5cb15 Added Rome to Cities

Collaborating with others in Github

If you are the admin. Lock the master branch so that others cannot push directly to master.

Best Practice

  • Don’t commit directly to master
  • Make code changes on a branch
  • Push branch to central repo
  • With Github GUI merge using a Pull Request

Benefits

  • Prevents from doing a force push to master
  • Allows code reviews of pull request before merging

Working with Git branches

Check that you are in master branch

$ git checkout master

Check that you have the latest updates before start working

(master)$ git pull

Create a branch and name it using your repo’s convention

(master)git checkout -b awesome-branch

Work on the branch, commit and push to the branch

(awesome-branch)$ git add .
(awesome-branch)$ git commit -am "Commit message"
(awesome-branch)$ git push -u origin awesome-branch

When work branch is completed

  • Go to team’s Github repo.
  • Go to tab Pull requests
  • Find your branch and click Compare & pull request
  • It shows Open a pull request
    • Enter new (or default: from last commit) title and comment
    • base should be master
    • compare should be the branch
    • Add Reviewers on the right
    • Click button Create pull request

If you can merge your own pull request

  • Pull request confirmation
    • Resolve conflicts or
    • Merge pull request if it says This branch has no conflicts with base branch
  • Confirm merge
    • Click Confirm merge
  • Click Delete branch

If you cannot merge your own pull request

  • Add team member to merge your pull request
  • That team member can add themselves as a reviewer and approve the PR

After PR has been approved and merged. Then update master in local repo

(awesome-branch)$ git checkout master
(master)$ git pull

Merge master with local branch when master was updated

As seen on Stackoverflow here

If you are working on a local branch. Then master was updated. How do you update master and merge to local branch?

Say that the branch is called develop and some changes were made.

(master)$ git checkout -b develop

Then commit the changes

(develop)$ git commit -am "Awesome comment here"

Then pull changes from master into branch. One of the answer says that rebase is used only for your own local branches and not when the branch is shared with others. This will make above commit ahead of updated master into branch.

(develop)$ git pull --rebase origin master

Ask me anything on Linkedin