GIT 101 - the beginners guide

Posted by Fred C (W6BSD) on Aug 25 2015

Git is a distributed version control system supporting non linear workflows, that focuses on speed and data integrity. Version control systems are essential for any form of distributed, collaborative development. Git is developed and maintained by Linus Torval the creator of the Linux Kernel. For more information, the page Git Basics explains all the internal workings of git.

Starting with git

To work git needs a .git repository, where it will store objects, references and other metadata. To get that .git repository you can either cd into your project and call the command git init or clone a new project you have created on a public repository such as github

$ mkdir new-project
$ cd new-project
$ git init
Initialized empty Git repository in /home/fred/new-project/.git/
$ ls -a
.       ..      .git

You can create a new project on github and then use the command git clone to create the git repository on your machine. Public repositories such as github will also help you create a .gitignore file containing the working files that you don't want to be managed by git as well as a LICENCE and README file.

$ git clone https://github.com/0x9900/new-project.git
Cloning into 'new-project'...
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.
Checking connectivity... done.
$ cd new-project
$ ls -a
.           ..          .git        .gitignore  LICENSE     README.md

If you have not cloned an existing repository and want to connect your local repository to a remote server, you need to add the remote URL with git remote

$ git remote add origin https://github.com/0x9900/new-project.git

If the remote server has already been set, you will get an error.

$ git remote add origin [email protected]:0x9900/new-project.git
fatal: remote origin already exists.

$ git remote -v
origin  https://github.com/0x9900/new-project.git (fetch)
origin  https://github.com/0x9900/new-project.git (push)

You can change the remote server using the option set-url

$ git remote set-url origin [email protected]:0x9900/new-project.git

$ git remote -v
origin  [email protected]:0x9900/new-project.git (fetch)
origin  [email protected]:0x9900/new-project.git (push)

Now you are able to push your changes to the selected remote server

Note: To use a url such as [email protected]:0x9900/new-project.git you need to setup your ssh key on github. On your github profile, click on the Add SSH key button in the SSH Keys section.

Working with git

Your local repository consists of three "branches" maintained by git:

  • The Working Directory holds the actual files.
  • The Index acts as a staging area.
  • The HEAD points to the last commit.

Git is about snapshots. A series of changes (snapshots) is saved into the Index.

The first step in the git workflow it tracking changes and staging changes to files. Using the command git add will add your changes into the Index. Example: git add <directory>, git add <filename>, git add *

To actually commit these changes to HEAD use git commit

$ git add hello.go
$ git status --short
A  hello.go
$ git commit -m "Saying hello to the world"
[master 901d2d0] Saying hello to the world
 1 file changed, 7 insertions(+)
  create mode 100644 hello.go

The changes are committed from Index to the HEAD in your local working copy of the repository. These changes have not been committed to the remote repository yet.

To send those changes to the remote repository, the command is git push.

$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 353 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To [email protected]:0x9900/new-project.git
   7d19615..901d2d0  master -> master

Change master to whatever branch you want to push your changes to.

Branching

Branches are used to develop features isolated from each other. Once these features are ready, they can then be merged to the master branch.

The master branch is the "default" branch when you create a repository.

Create a new branch named "myfeature" and switch to it using

$ git checkout -b myfeature
Switched to a new branch 'myfeature'

To switch back to the master branch git checkout master. To delete a branch `git branch -d myfeature

The new branch is not available to others until you push the branch to your remote repository with: git push origin myfeature

When you are done developing your new feature you can see your changes with the command git diff

$ git diff
diff --git a/hello.go b/hello.go
index 11f7c72..7bfe586 100644
--- a/hello.go
+++ b/hello.go
@@ -4,4 +4,5 @@ import "fmt"

 func main() {
     fmt.Printf("hello, hello, hello, world\n")
+    fmt.Printf("hello and to the people of the moon\n")
 }

Once you are satisfied with your new feature you can commit them to your branch.

$ git commit -am 'do not forget the people of the moon'
[myfeature d42fdce] do not forget the people of the moon
 1 file changed, 1 insertion(+)

Update & Merge

Anytime you want to update your local repository with the changes that other developers have commited to the master branch, execute git pull

To merge another branch into your active branch, use git merge. git will try to automatically merge the changes. Sometimes, when the same part of your code has been modified in two branches you will get a merge conflict. You are responsible for resolving the conflicts manually by editing the files shown by git.

After resolving the conflicts, you need to mark them as merged with git add

Before merging changes, you can preview them by using git diff

In the following example, we show the differences between the branches myfeature and master. Then we checkout master, merge the changes from myfeature into master, and finally push these changes to the origin (github)

$ git branch
  master
* myfeature
$ git diff master myfeature
diff --git a/hello.go b/hello.go
index 11f7c72..7bfe586 100644
--- a/hello.go
+++ b/hello.go
@@ -4,4 +4,5 @@ import "fmt"

 func main() {
      fmt.Printf("hello, hello, hello, world\n")
+     fmt.Printf("hello and to the people of the moon\n")
       }
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
fred[844](master)$ git merge myfeature
Updating 901d2d0..d42fdce
Fast-forward
 hello.go | 1 +
 1 file changed, 1 insertion(+)
$ git push origin
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 367 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To [email protected]:0x9900/new-project.git
   901d2d0..d42fdce  master -> master

logs

You can always look at the repository history using: git log

Git log has a lot of options, use git log --help to configure the output the way you like it.

$ git log
commit d42fdcef527c792cf5b088a0395c4fae8de366de
Author: Fred C <[email protected]>
Date:   Sun Aug 23 15:45:52 2015 -0700

    do not forget the people of the moon

commit 901d2d018586976fac3228356d48e28408883198
Author: Fred C <[email protected]>
Date:   Sun Aug 23 12:03:29 2015 -0700

    Saying hello to the world

commit 7d1961565e19c9c06bf3218e1825f093b3d2fdd5
Author: Fred Cirera <[email protected]>
Date:   Sat Aug 22 10:34:58 2015 -0700

Initial commit

$ git log --graph --pretty=oneline --abbrev-commit --decorate --all
* d42fdce (HEAD -> master, myfeature) do not forget the people of the moon
* 901d2d0 (origin/master, origin/HEAD) Saying hello to the world
* 7d19615 Initial commit

It is possible to create an alias for long and hard to remember commands such as git log --graph --pretty=oneline --abbrev-commit --decorate --all. The aliases should be stored in the .gitconfig file in your home directory.

$ cat ~/.gitconfig
[user]
        name = Fred C
        email = fred@myamail.com
[color]
        ui = auto
[alias]
        co = checkout
        st = status
        ci = commit
        log = log --topo-order --first-parent --graph --decorate
        lgstat = log --topo-order --stat --abbrev-commit
        lg = log --graph --decorate --pretty=oneline --abbrev-commit
        lgall = log --graph --decorate --pretty=oneline --abbrev-commit --all
        hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
        wstat = log --author=$USER --format="- %B" --since=-7days --reverse

Tagging

When you want to release a new version of your software you can create a tag name.

$ git tag -a v1.0.0 -m 'The first release of our software'
$ git describe --tags
v1.0.0
$ git show v1.0.0
tag v1.0.0
Tagger: Fred C <[email protected]>
Date:   Sun Aug 23 16:22:51 2015 -0700

The first release of our software
. . .

Note: You can crypto-sign your tag with the option -u <keyid> for security reasons

Rollback changes

You have started to develop a new feature, edited some files, and after a few hours of work you realize that was wrong. You can undo the changes you've made in a file using the command git checkout -- <filename>, or git checkout -- <dirname> for undoing the changes at the directory level.

$ vi hello.go
$ git diff
diff --git a/hello.go b/hello.go
index 7bfe586..fc4f54d 100644
--- a/hello.go
+++ b/hello.go
@@ -5,4 +5,5 @@ import "fmt"
 func main() {
     fmt.Printf("hello, hello, hello, world\n")
     fmt.Printf("hello and to the people of the moon\n")
+    fmt.Printf("and bye, bye\n")
 }

$ git checkout -- hello.go
$ git diff

This will only undo the changes on files that haven't been committed yet. If you have already committed your changes you need to reset your branch to a previous state with git reset --hard.

$ vi hello.go
$ git commit -am 'say bye'
[master a1c8631] say bye
1 file changed, 1 insertion(+)

$ git log --graph --pretty=oneline --abbrev-commit --decorate --all
* a1c8631 (HEAD -> master) say bye
* d42fdce (tag: v1.0.0, origin/master, origin/HEAD, myfeature) do not forget the people of the moon
* 901d2d0 Saying hello to the world
* 7d19615 Initial commit

$ git fetch origin
$ git reset --hard origin/master
HEAD is now at d42fdce do not forget the people of the moon

$ git log --graph --pretty=oneline --abbrev-commit --decorate --all
* d42fdce (HEAD -> master, tag: v1.0.0, origin/master, origin/HEAD, myfeature) do not forget the people of the moon
* 901d2d0 Saying hello to the world
* 7d19615 Initial commit

The option reset --hard will drop any changes you have made and reset your branch to the state of origin/master. Without the --hard the command will have the same effect, your branch history is back the the state of origin/master, but all the changes in the files are still here.

# the new feature
$ vi hello.go
$ git commit -am 'say bye'
[master fcc9e67] say bye
 1 file changed, 1 insertion(+)

# the feature is committed
$ git log --graph --pretty=oneline --abbrev-commit --decorate --all
* fcc9e67 (HEAD -> master) say bye
* d42fdce (tag: v1.0.0, origin/master, origin/HEAD, myfeature) do not forget the people of the moon
* 901d2d0 Saying hello to the world
* 7d19615 Initial commit

# Ooops that feature was a mistake (roll it back)
$ git fetch origin
$ git reset origin/master
Unstaged changes after reset:
M       hello.go

# The change has been rolled back. The branch is in the state it
# was before the commit.
$ git diff
diff --git a/hello.go b/hello.go
index 7bfe586..fc4f54d 100644
--- a/hello.go
+++ b/hello.go
@@ -5,4 +5,5 @@ import "fmt"
 func main() {
     fmt.Printf("hello, hello, hello, world\n")
     fmt.Printf("hello and to the people of the moon\n")
+    fmt.Printf("and bye, bye\n")
 }

$ git log --graph --pretty=oneline --abbrev-commit --decorate --all
* d42fdce (HEAD -> master, tag: v1.0.0, origin/master, origin/HEAD, myfeature) do not forget the people of the moon
* 901d2d0 Saying hello to the world
* 7d19615 Initial commit

Git Performance

Source

Resources


 Development      Sys Admin