Sunday, 10 November 2013

Intro to Git Version Control

This is a simple story to illustrate how to do basic revision control with Git. I've had a couple of nightmares with Git repositories - it has been a bit of a git getting my head around how it works.


If you don't know what Git is, it's distributed revision control and source code management (SCM) system.  It's widely used to share open source code and is great for managing your code base with speed and, if you pay at github.com private.
This story begins, after you have installed git, by grabbing a repository of code from github.com and putting it in a new local directory in the name of the repository and copies all the files into it from the remote (aka origin).
$: cd <local code destination folder>
git clone https://url/to/repository
The url to use is shown on the github page.  Once it's downloaded check out the new local git instance and then run some git commands:
$: git status
# On branch master
nothing to commit (working directory clean)
I editted a file README.md and saved my update in the git folder what happens with git?
$: git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   README.md
#
no changes added to commit (use "git add" and/or "git commit -a")
A commit is a snapshot of changes recorded in a branch's history for ever.
Prepare for a snapshot by making a list of changes you want to include. It doesn't need to be all your changes at once.
E.g if you edit two files (Tom.txt & Jerry.txt), you can 'git add Tom.txt' and then 'git commit -m' to snapshot that one change. Then you can 'git add Jerry.txt' then 'git commit -m' again to take another snapshot.

You can use wildcards in <filename> or include everything with 'git add .', or 'git commit -am' to stage all changes (-a for add, -m for message see below)
A nice way to see where you are is with the short status command git status -s.
$: git add README.md
$: git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   README.md
#
$: git reset HEAD -- README.md
$: git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   README.md
#
To repeat from initial clone with modified README.md
$: git status -s
 M README.md

$: git add RE*
$:git status -s
M  README.md

$: git reset HEAD -- README.md
$: git status -s
 M README.md
NB git config --global color.ui true adds colour to the status message.  It is useful to set up user name and email address by using git config --global user.name 'Kylie Minogue' and git config --global user.email 'kylie@minogue.com. You can check them with git config --get user.name and git config --get user.email.
$:git commit
This has no effect because nothing is staged for commit (git added). So.
$: git add -a
$git commit -m 'MM: Changed README.md to add git process'
[master 157cb67] MM: Changed README.md to add git process
 1 file changed, 71 insertions(+), 1 deletion(-)
 rewrite README.md (100%)
$: git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
nothing to commit (working directory clean)
You can undo commits:
$: git reset --soft HEAD~
$: git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   README.md
#
To see different files in your working directory you need to use branches.
$:git branch
* master

$:git branch mattlocal
$:git branch
* master
  mattlocal

$:git checkout mattlocal
  M README.md
Switched to branch 'mattlocal'

$: git branch
  master
* mattlocal

$:touch newmattfile.txt
$:git status -s
 M README.md
?? newmattfile.txt

$:git add README.md
$:git add newmattfile.txt

$:git status -s
M  README.md
A  newmattfile.txt

$:git commit -m 'Added extra file newmattfile.txt to demo mattlocal branch'
[mattlocal 274f225] Added extra file newmattfile.txt to demo mattlocal branch
 2 files changed, 93 insertions(+), 1 deletion(-)
 rewrite README.md (100%)
 create mode 100644 newmattfile.txt

$:git branch
  master
* mattlocal

$: ls -l
AndroidManifest.xml
README.md
ic_launcher-web.png
libs
lint.xml
newmattfile.txt
proguard-project.txt
project.properties
res
server
src

$:git checkout master
Switched to branch 'master'

$:ls
AndroidManifest.xml
ic_launcher-web.png
lint.xml
project.properties
server
README.md
libs
proguard-project.txt
res
src
As you can see from the shell above (you may have to scroll down in the box to see it) newmattfile.txt can only be see when you're on the mattlocal branch. This is great for keeping a working master safe from any changes you make in your next batch of coding all done on another branch. This branch could be discarded if it all goes wrong, or merged back into master branch once you're sure it's working. To merge this branch back into the master, checkout master and merge the mattlocal changes into the 'mainstream' as follows.
$:git checkout master
$:git branch
* master
  mattlocal
$: git merge mattlocal
Updating 61dd87d..d7a9f02
Fast-forward
 README.md       | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 newmattfile.txt |   1 +
 2 files changed, 150 insertions(+), 1 deletion(-)
 create mode 100644 newmattfile.txt
Finally, when synchronising your code back to the remote repository you do this with
git push origin <branch>

If the remote repository has changed you can use git fetch and git merge to update your local repository to the latest. Git pull combines fetch and push, but the separate command approach is recommended by git.