Simple database that keeps all changes to files under version control.
Example system: RCS
RCS
keeps patch sets and recreates what any file looked like at any point in time by adding up all the patches.
SVN
, CVS
.Users fully mirror repository.
A failed server can be restored from any client's mirror.
Example systems: git
, mercurial
.
To install git on Linux:
apt-get install git
yum install git
pacman -S git
To install git on Mac:
brew install git
http://git-scm.com/download
To install git on Windows:
http://git-scm.com/download
To setup user name:
$ git config --global user.name "Absi Gittawi"
To setup email:
$ git config --global user.email absi.gittawi@jordanopensource.org
To list all configurations:
$ git config --list
Repository: in simple words, a project's folder. A repository contains all of the project files, each file's revision history, user configuration, and git data.
To initialize a repo (short for repository) in an existing folder:
$ git init
To initialize a repo in a new folder:
$ git init <project-folder>
$ git status
On branch master
nothing to commit, working directory clean
To add a single file:
$ git add <file-name>
To add all files in current folder and subfolders:
$ git add .
To add hunks (tracked files only):
$ git add -p
To reset a single file:
$ git reset <file-name>
To reset all changes files in current folder and subfolders:
$ git reset .
To reset hunks:
$ git reset -p
To see what changed but not has not yet staged:
$ git diff
To see what is staged so far:
$ git diff --cached
$ git diff --staged
To commit:
$ git commit
This launches a text editor (can be set using core.editor config).
To commit and enter commit message on command-line:
$ git commit -m "<commit-message>"
To skip staging area, add and commit every tracked file:
$ git commit -am "<commit-message>"
$ git log
commit <commit-sha1>
author: Absi Gittawi <absi.gittawi@jordanopensource.org>
date: fri feb 13 20:19:08 2015 +0300
<commit-message>
$ git log -p
Branches in git:
Creating a new branch:
$ git branch <branch-name>
What branch am I on?
Switching branches:
$ git checkout <branch-name>
Merging branches in git:
Switch to destination branch:
$ git checkout <destination-branch-name>
Merge the commits from the other branch:
$ git merge <branch-name>
Occasionally conflicts happen:
$ git merge <bug-fix>
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
<<<<<<< HEAD:index.htmlcontact : absi.gittawi@jordanopensource.org=======please contact us at gittawi.absi@jordanopensource.org>>>>>>> iss53:index.html
After you resolve the conflicts, add and commit the files
Remote repositories are versions of your project that are hosted on the Internet or network somewhere.
Adding a remote repository
$ git remote add [remote_name] [remote_address]
Removing a remote repository
$ git remote remove [remote_name]
Pushing your local changes to a remote repository
$ git push -u [remote_name] [branch_name]
Fetch
$ git fetch [remote_name]
checkout
$ git checkout -b [local_branch_name] [remote_name]/[branch_name]
Merge
$ git merge [branch_name]
pull => fetch and merge
$ git pull [remote_name] [branch_name]
Clone a repository into a new directory
$ git clone [remote_name] [direcotry_name]
$ mkdir test_repo.git $ git init --bare --shared
For more info go to: http://git-scm.com/book/en/v2/Git-on-the-Server-Getting-Git-on-a-Server
$ git checkout <commit>
$ git checkout B-SHA
$ git reset <commit>
$ git reset --soft B-SHA
$ git revert <commit>
E
is a new commit that undoes the changes made in commit D*
Tags are pointers to commits. They don't move with time.
Typical usage is to mark release points and versions.
To create a new tag:
$ git tag <tag-name>
To list available tags:
$ git tag
Problem: you are in the middle of something, and Absi, the QA team head, comes rushing. "There is an urgent bug that needs to be resolved now!".
You need to stop what you're doing and make a change on the production version.
You try to checkout to the latest release tag, but stupid git is yelling:
error: Your local changes to the following files would be overwritten by checkout: index.html Please, commit your changes or stash them before you can switch branches. Aborting
Stashing takes the dirty state of your working directory and saves it on a stack of unfinished changes that you can reapply at any time.
Result is an empty working directory that you can restore when you are ready.
To stash your changes:
$ git stash
$ git stash save
$ git stash save <message>
To restore stashed changes:
$ git stash pop
$ git stash apply
To see available stashes:
$ git stash list
Cherry picking is applying the changes from only one commit rather than having all the changes from some branch.
The need for this arises in different situations:
To cherry-pick commit C
:
$ git cherry-pick C
Absi is a lazy programmer and he likes making efficient use of his finger movements.
Git aliases enable having shortcuts for git commands.
To add a new alias:
$ git config --global alias.<alias> '<command>'
Or you can make the changes in ~/.gitconfig
file:
$ vim ~/.gitconfig
Examples:
$ git config --global alias.st 'status'
$ git config --global alias.co 'checkout'
$ git config --global alias.br 'branch'
$ git config --global alias.unstage 'reset HEAD --'
Some more interesting examples:
$ git config --global alias.lg 'log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit'
$ git config --global alias.visual '!gitk'
Absi has been debugging a problem since the morning, and has just found the source of it in the code.
He is wondering why this code exists there and who put it in.
Git blame is a command to point out who changed which lines in a file, and in what commit.
To see blame on some file in git:
$ git blame <file>
Example:
<commit> (<author> <date>) file line
84d32bf7 (Ramsay Allan Jones 2013-04-27 20:19:47 +0100 617) int main(int argc, char **av) 8e49d503 (Andreas Ericsson 2005-11-16 00:31:25 +0100 618) { 84d32bf7 (Ramsay Allan Jones 2013-04-27 20:19:47 +0100 619) const char **argv = (const char **) av; 4dd47c3b (Steve Haslam 2009-01-18 13:00:10 +0100 620) const char *cmd; 231af832 (Linus Torvalds 2006-02-26 12:34:51 -0800 621) e37c1329 (Nguyễn Thái Ngọc Duy 2010-08-05 21:40:35 -0500 622) startup_info = &git_startup_info; e37c1329 (Nguyễn Thái Ngọc Duy 2010-08-05 21:40:35 -0500 623)
gitignore is a way to intentionally specify files not to track.
To use it, create a .gitignore
file in a folder under a git repo and a line for each pattern you want to ignore. Then commit it and push it for all users to use it.
Files already tracked by git are not affected.
To stop tracking a file that is currently tracked, use git rm --cached
.
Example file for C/C++ based projects:
$ cat .gitignore bin/ debug/ *.o *.sln
Example file for Java based projects:
$ cat .gitignore *.class *.jar *.war # you can negate the rule to include a file despite previous rules !stable.v1.1.jar
A rebase is a different way to integrate changes from one branch to another.
A merge command uses three-way merge between the two latest branch snapshots and the most recent common ancestor of the two, creating a new commit (the merge commit).
A rebase, on the other hand, integrates each change introduced by a commit as a new commit in the target branch.
$ git rebase -i master
$ git checkout master
$ git merge feature
To rebase:
$ git rebase <branch>
An interactive rebase gives you the opportunity to alter individual commits in the process rather than blindly moving all of the commits to the new base.
$ git rebase -i <branch>
pick 9b6dbf3 Added rebasing pick 2106a4e Continue rebasing # Rebase aced1c4..2106a4e onto aced1c4 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Merging produces commits which can help you in tracking history more, but can also be noisy.
Rebasing provides a way to have a linear line of history.
Rebasing can be a destructive tool.
Rebase should not be used on public branches.
Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that.
Git bisect is a tool to do a binary search on your codebase to find a buggy commit.
To start the bisect operation:
$ git checkout <bad-branch>
$ git bisect start
$ git bisect bad
$ git bisect good <good-branch>
Clone this repo for exercise: https://github.com/rbast/bisect-me
You can automate git bisect using a script.
You need a script to exit with a zero for a good state and a non-zero for a bad state.
$ git bisect start <bad-branch> <good-branch>
$ git bisect run <script-file>
Submodules are git repositories within git repositories!
You use submodules to include different projects that are dependant on each other.
To create a submodule:
$ git submodule add <repository>
When you first clone a repository, you need to initialize submodules and update:
$ git submodule init
$ git submodule update
Hooks are little scripts that are triggered by git at specific events.
Some of the most interesting events are:
Pre-commit hook example to check php files syntax:
<?php $path = "*** THE ROOT DIRECTORY OF YOUR PHP FILES ***"; $output = array(); exec("find {$path} -name '*.php' | xargs -n1 php -l | grep -v 'No syntax errors detected'", $output); exit(count($output) > 0 ? 1 : 0);
Prepare-message-commit hook example to include issue number in commit message:
#!/usr/bin/env python import sys, os, re from subprocess import check_output # Collect the parameters commit_msg_filepath = sys.argv[1] if len(sys.argv) > 2: commit_type = sys.argv[2] else: commit_type = '' if len(sys.argv) > 3: commit_hash = sys.argv[3] else: commit_hash = '' print "prepare-commit-msg: File: %s\nType: %s\nHash: %s" % (commit_msg_filepath, commit_type, commit_hash) # Figure out which branch we're on branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip() print "prepare-commit-msg: On branch '%s'" % branch # Populate the commit message with the issue #, if there is one if branch.startswith('issue-'): print "prepare-commit-msg: Oh hey, it's an issue branch." result = re.match('issue-(.*)', branch) issue_number = result.group(1) with open(commit_msg_filepath, 'r+') as f: content = f.read() f.seek(0, 0) f.write("ISSUE-%s %s" % (issue_number, content))