GIT


sysadmin_GIT_logo

In a DevOps environment where people use tickets and kanbans in order to organize their work, a version control system is absolutely necessary. It’s a quick, easy and safe way of isolating and keeping track of changes associated with tickets. Also, it greatly simplifies the collaboration between developers and operations.

Created in 2005 by Linus Torvalds and Junio Hamano, GIT is a distributed version control system. It features 145 commands (probably the reason why some people consider it hard to learn), but you only need to know 13 of them (add, branch, checkout, clone, commit, diff, fetch, help, init, log, merge, push, status) to start working.

Like most CVSes, GIT is based on centralized repositories that developers can clone and commit to locally. It allows for the creation of branches, which are different versions of a same project, typically a master branch (the stable version usable in production), a development branch (new features ready to be tested) and one branch per ticket.

The goal of this article is to help you understand the way GIT works, so you can start using it for your everyday work. It may also be used as a quick reference or cheat sheet (a real cheat sheet is provided at the bottom of this page).

Fundamentals

The 6 states of a file

In GIT, files can have 6 states : untracked, tracked, unmodified, modified, unstaged and staged. Tracked files are files that were in the last snapshot; Staged files are files that will be part of the next commit.


sysadmin_GIT_5_states_file

From To Command / Operation
Untracked Unmodified (Tracked) git add
Unmodified (Tracked) Modified The file is modified using the appropriate editor.
Modified Staged git add
Staged Unmodified (Tracked) git commit
Unmodified (Tracked) Untracked git rm or git rm –cached

.gitignore

Anything you don’t want GIT to track must be mentioned into this plain text file. You can read this if you need more informations.

Stash

A place to temporarily store changes while working on something else. Usually, the stash is used when you want / need to switch to another branch without committing your work in its current state. Each branch has its own stash. See Stashing for more details.

Workspace (working directory)

The workspace is where you do your actual work. It contains tracked/untracked files and a special directory named .git (this is where GIT stores the behind-the-scene stuff).

Index (Stage)

The index (also known as staging area) is used for the preparation of commits. Everything you want to commit is put into the index first using git add. Commits send the content of the index to the object store.

The object store

Basically, the files of your repo, compressed and named after their SHA-1 hash.

Local repository

Your personal version of a repository. You make your changes here and commit them so they get stored into a local snapshot. Once you think it’s time to publish your work, you push it to the remote GIT server so your colleagues can access it.

Upstream repository (remote)

A remote GIT server used as a centralized file vault (usually sources, but can be anything as long as it’s not too big) by a team of developers (and their colleagues from the Ops crew).

HEAD

HEAD is a file (.git/HEAD) which content points to the current branch. When you checkout a different branch, HEAD changes to point to the new one. The current HEAD is local to each repository and there is one for each developer. HEAD can point to any commit in any branch. Sometimes, it points to a commit that is not the last commit in a branch. In such a case, we have what is called a « detached HEAD ».

Commits

When you do a commit, GIT :

  • creates a new commit object using the staged files,
  • sets the parent to the current commit,
  • points the current branch to this new commit.

Help

git help
git help command

GIT features 145 commands. The good new is: you just have to know 13 of them to start working.

Configuration, status and logs

Configuration

GIT stores all its data in the .git folder. The configuration options for a given repository are written in .git/config. Global configuration can be found in ~/.gitconfig or ~/.config/git/config.

Name and e-mail :
git config --global user.name "Peter Pan"
git config --global user.email "peter.pan@foobar.net"

Line ending for Linux/UNIX :
git config --global core.autocrlf input
git config --global core.safecrlf true

Line ending for Windows :
git config --global core.autocrlf true
git config --global core.safecrlf true

User credentials for remote repository :
git config credential.helper store
/* Next time you type your credentials, they will be remembered. */

Define some useful aliases :
git config --global alias.st status
git config --global alias.ci commit
git config --global alias.co checkout
git config --global alias.oops 'commit --amend --no-edit'
git config --global alias.lg "log --graph --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ar)%Creset'"
git st
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

Status
cd repo_folder
git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

Logs
Show all commits :
git log
Short format :
git log --pretty=short
Patches :
git log -p
Show file commits :
git log file
Show directory commits :
git log directory/
Statistics :
git log --stats
Who is to blame ?
git blame file

Repositories

Create

Important: In a server/client setup, you can’t create a new repo on the client and send it to the server. It has to be created on the server first.

mkdir new_project
cd !$
git init
git add .

Clone
git clone /home/foobar/existing_project new_project
git clone git://github.com/user/repo.git
git clone https://github.com/user/repo.git
git clone https://github.com/user/repo.git custom_dir

Move
mv dir/ new_destinationmv ./armada210/ /home/foobar/
Delete
If you only want to delete the git-related information (branches, versions) :
rm reponame/.git
Delete everything (git-data, code, etc) :
rm reponame/
Rename
Local repo :
mv old_reponame new_reponame
Remote repo :
/* On the server : */
mv old_reponame new_reponame
/* On the client : */
Edit .git/config ; set the remote URL to the new value (new_reponame).

Branches

Create
Brand new branch :
git branch branchname
Brand new branch & switch to it automatically :
git checkout -b branchname
Copy
git branch new_branchname existing_branchname
List
git branch
Switch
git checkout branchname
Delete
Local branch :
git branch -d branchname
Remote branch :
git push remote_name :branchname
Rename

Important: If you want to rename a branch, the best option is probably to create a copy.

Current branch :
git branch -m newname
Any branch :
git branch -m oldname newname

Files and folders

Add / Start tracking
git add file1 file2 ... fileN
git add file1 folder1 file2

Rename
git mv oldname newname
Delete / Untrack
Remove a file from the filesystem :
git rm filename
Tell GIT to stop tracking a file (untrack) :
git rm --cached filename

Files can have 6 states in GIT : untracked, tracked, unmodified, modified, unstaged and staged.

Tags

List
git tag
git tag -l 'v4.1.2*'

Create

Git uses two types of tags: lightweight and annotated. Lightweight tags are pointers to a specific commit while annotated tags are stored as full checksummed objects in the Git database. They contain the tagger name, his e-mail, the date, a message and can be signed and verified with GNU Privacy Guard (GPG).

Lightweight (« simple tag ») :
git tag v2.1
Annotated (« information-rich tag ») :
git tag -a v2.1 -m 'Version 2.1: now waterproof.'
Tag a specific commit :
git log --pretty=oneline

17357957951b64cf882c3557a0f3547bd83b3eb3 Merge branch 'development'
b7c5c97498bd301d84096da251c98a07c7322e62 XBox pad management
1e52aaab4479697da7686c15f77a3d64d9165190 Stack overflow bug
7e52a271eda8725415634dd79daabbc4d9b6008e Bluetooth headset OK
1c7434d86859cc7b8c3d5e1dddfed66ff7422846 New timestamp format
5782c3261057305bdd616e23b64b0857d832d7e9 Added a log file
276ae0c4d3f420721acbb115cc33848dfcc22bee Copperlist corrected
ffceb02d0ae598e95dc970b74767f19372d62050 Status properly updates
344f16d36dfccde844893cac5b347e7b3d44bcc2 Quest list added
3c5cbc430f1a9c3d00faaeffd0779850842291e0 Updated README

git tag -a v3.1 276ae0c
Delete
Local tag :
git tag -d tagname
Remote tag :
git push --delete origin tagname
Modify
git tag new old
git tag -d old
/* If you want to apply the changes to the remote */
git push origin :refs/tags/old
git push --tags

Synchronize
Push one tag on a GIT remote / server :
git push origin v3.1
Push all local tags on a GIT remote / server :
git push origin --tags
Retrieve all tags from a remote server :
git fetch --tags

Stashing

Stash your work in progress

Remember: Each branch has its own stash.

git stash
List stashed items
git stash list
Restore last stashed elements

Restored / applied stash elements aren’t removed; they remain available until you explicitly delete it (see Delete stashes).

git stash apply
Restore older stashed elements
git stash list
/* A list of the stashes you have stored appears. */
git stash apply stash_name

Example :
git stash list
stash@{0}: WIP on master: 059d098 Added new popup
stash@{1}: WIP on master: d364062 Revert "Added string_length"
stash@{2}: WIP on master: 32d80b6 Added separate error log
git stash apply stash@{1}

Apply a stash and immediately drop it from your stack :
git stash pop
Delete stashes
git stash drop stash_name
Example :
git stash list
stash@{0}: WIP on master: 059d098 Added new popup
stash@{1}: WIP on master: d364062 Revert "Added string_length"
stash@{2}: WIP on master: 32d80b6 Added separate error log
git stash drop stash@{1}

Un-applying a stash
git stash show -p stash_name | git apply -R
Examples :
git stash show -p stash@{1} | git apply -R
/* If no stash is specified, the last one is used. */
git stash show -p | git apply -R

Restored / applied stash elements aren’t removed; they remain available until you explicitly delete it.

Commit

Prepare

Let’s say you modified 2 files, one tracked, one untracked, named respectively LICENSE and config. Now you want to commit both of them to your current branch; if you run git status, you will see something similar to this :

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: LICENSE
Untracked files:
(use "git add ..." to include in what will be committed)
config
no changes added to commit (use "git add" and/or "git commit -a")

Untracked files are not managed by GIT (i.e. they don’t have a SHA1 hash and are not included in snapshots). All you have to do is to git add them in order for them to become tracked. Before every commit, you have to tell GIT which files you want included in the snapshot using git add :

git add ./LICENSE ./config
git commit -m "New LICENSE and SMTP server in config file."
[master 1c4c633] New LICENSE and SMTP server in config file.
2 files changed, 5 insertions(+), 1 deletion(-)

Before every commit, you have to tell GIT which files you want included in the snapshot using « git add ».


Commit
With a comment :
git commit -m "New LICENSE and SMTP server in config file."
git push

Auto add tracked files and add a comment :
git commit -a -m "New LICENSE and SMTP server in config file."
git push

If you work with a remote server, you don’t have to push every time you commit. For the sake of readability, it’s better to push only the most important changes.


Cancel
Undo a local commit :
git reset --hard @{u}
Undo a public commit :
git revert HEAD
git commit -m "Fixing my mistake..."
git push

Modify / Amend the last commit

Important : Amended commits are entirely new commits. The previous commit is removed from the project history. For this reason, never amend commits that have been pushed to a public repository.

git commit --amend
Example :
/* We modify config and LICENSE */
/* and forget to add the former before the commit. */
vi ./config
vi ./LICENSE
git add ./LICENSE
git commit -m "Cleaned LICENSE up."
/* Ooops ! We forgot to add config ! */
git add ./config
git commit --amend --no-edit
/* If you want to apply the changes to the remote */
git push

Amended commits are entirely new commits. Never amend commits that have been pushed to a public repository.

Merge and Rebase

When should I merge ? When should I rebase ?

Often, merge and rebase are considered to be quite the same, which is only true if you limit yourself to a result-centric view. Behind the scenes, it’s a completely different story (commit) history: while git merge generates entirely new commits, git rebase alters existing commits. This is why you should use merge when you are ready to include all the features of a branch into another and rebase to refresh a local base rendered obsolete by some changes on the remote.


sysadmin_git_merge

Merge

git merge takes one or more branches as parameter; these branches will be merged with the current branch.

Merge branch hotfix into master :
git checkout master
git merge hotfix

Merge branch JB-007 into OSS-117 :
git checkout OSS-117
git merge JB-007

Merge branches CR-404 and CR-407 into development :
git checkout development
git merge CR-404 CR-407

Cancel a merge
git merge --abort

GIT merge takes 2+ branches and fuses them into one brand new commit.



sysadmin_git_rebase

Rebase
git checkout development
git rebase master

Cancel a rebase
git rebase –abort

GIT rebase allows for a clean commit history, but at the cost of safety and traceability.

Sources

Video tutorial



Cheat sheet


vmalkani_git-flow.600
GIT workflow