Git is fast becoming the most popular versioning control system in the market today. It is open sourced and offers a number of significant advantages over svn. SVN will probably still be around for a while because a lot of people and software are still dependent on it or don’t see a need for change. I have been using GIT for a while and really liked it. Where else can I share this information except in my own blog!
For introduction to git, check out the official git user manual. For svn users, the git crash course is useful. I am using git 1.72 btw.
Centralised Model
True enough, svn works well in a centralised repo environment, ie everyone check-in and check-out from one single repository. We can simulate this in git as well.
In a typical dev environment, say we have one team lead and x developers. To simulate the centralised model in git, the team lead creates a personal repo first (I’ll be using dummy data and dir names – the idea is just to document the workflow).
[teamlead@web git]$ git config --global user.name "teamlead" [teamlead@web git]$ git config --global user.email "teamlead@test.com" [teamlead@web ~]$ mkdir git [teamlead@web ~]$ cd git [teamlead@web git]$ git init Initialized empty Git repository in /home/teamlead/git/.git/ [teamlead@web git]$ echo "test" > test [teamlead@web git]$ git add ./ [teamlead@web git]$ git commit -a -m "init" [master (root-commit) 0cd9b7c] init 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 test
Assuming I am the teamlead, I will now need to create a bare repo somewhere else so that everyone can push to it, say the dir “pub” under my home dir. I don’t want anyone updating my own repo.
[teamlead@web git]$ cd .. [teamlead@web ~]$ ll total 4 drwxrwxr-x 3 teamlead teamlead 4096 Sep 1 00:34 git [teamlead@web ~]$ mkdir pub [teamlead@web ~]$ cd pub [teamlead@web pub]$ git init --bare Initialized empty Git repository in /home/teamlead/pub/
OK the new repo is still empty, as the teamlead, I need to populate it with my data first (noticed I have one default master branch).
[teamlead@web git]$ git remote add origin /home/teamlead/pub [teamlead@web git]$ git branch -a * master [teamlead@web git]$ git push origin master Counting objects: 3, done. Unpacking objects: 100% (3/3), done. Writing objects: 100% (3/3), 207 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To /home/teamlead/pub * [new branch] master -> master
This is cool, now my master branch is replicated over to pub. I will tell my developer to grab the repo. But before I do that, lets make sure that the pub repo is really publicly editable.
[teamlead@web home]$ ll total 20 drwx------ 2 devA devA 4096 Sep 1 00:32 devA drwx------ 2 devB devB 4096 Sep 1 00:32 devB drwx------ 4 teamlead teamlead 4096 Sep 1 00:50 teamlead [teamlead@web home]$ chmod o+x teamlead [teamlead@web home]$ cd teamlead/ [teamlead@web ~]$ ll total 8 drwxrwxr-x 3 teamlead teamlead 4096 Sep 1 00:34 git drwxrwxr-x 7 teamlead teamlead 4096 Sep 1 00:37 pub [teamlead@web ~]$ chmod o+w -R pub
Making dir with permission 777 is not a good practice but good enough for this illustration. Well, now devA will try to checkout from the pub repo and pushes a change back.
[devA@web ~]$ git clone /home/teamlead/pub git Cloning into git... done. [devA@web ~]$ ll total 4 drwxr-xr-x 3 devA devA 4096 Sep 1 00:58 git [devA@web ~]$ cd git [devA@web git]$ ll total 4 -rw-rw-r-- 1 devA devA 5 Sep 1 00:58 test [devA@web git]$ cd .. [devA@web ~]$ cd git [devA@web git]$ echo "devA test" >> test [devA@web git]$ git commit -a -m "devA input" [master 0542737] devA input 1 files changed, 1 insertions(+), 0 deletions(-) [devA@web git]$ git push origin master Counting objects: 5, done. Writing objects: 100% (3/3), 237 bytes, done. Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /home/teamlead/pub 0cd9b7c..0542737 master -> master
All good. Let’s see if teamlead can get the update.
[teamlead@web git]$ git fetch origin remote: Counting objects: 5, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /home/teamlead/pub 0cd9b7c..0542737 master -> origin/master [teamlead@web git]$ git merge origin/master master Fast-forwarding to: origin/master Already up-to-date with master Merge made by octopus. test | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) [teamlead@web git]$ cat test test devA test
Got the changes… yay.
Decentralised Model
The philosophy behind the decentralised repo is to commit, branch and merge often. Imagine everyone in the team has their own sharable repo… and you can share your branches anytime. My experience with the decentralised model is that it works better with a bigger team.
Let us use the same development team as before. 1 x teamlead and x developers. The teamlead will pull the changes from the developers and then update the main repo which happens to be a staging www site.
We start by creating the staging site
[root@web html]# pwd /var/www/html [root@web html]# mkdir git [root@web html]# cd git [root@web git]# git init Initialized empty Git repository in /var/www/html/git/.git/ [root@web git]# echo "test" > test [root@web git]# git add test [root@web git]# git commit -a -m "init" [master (root-commit) e604a85] init Committer: root Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com If the identity used for this commit is wrong, you can fix it with: git commit --amend --author='Your Name ' 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 test
All good. Now to do proper development, we need to have revisions. We create ver1.
[root@web git]# git checkout -b ver1 Switched to a new branch 'ver1'
give permission to apache so that we can view it on the web
[root@web git]# cd .. [root@web html]# chown apache:apache -R git
now developer A make some branches… Branching is a good practice in git and one should do it often.
[devA@web ~]$ git clone /var/www/html/git Cloning into git... done. [devA@web ~]$ cd git [devA@web git]$ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/ver1 [devA@web git]$ git checkout -b origin/ver1 ver1 [devA@web git]$ git checkout -b ver1 origin/ver1 [devA@web git]$ git checkout -b ver1_dev ver1 Switched to a new branch 'ver1_dev'
Dev A can create any no. of branches he likes and merge them with his copy of ver1 anytime.
[devA@web git]$ git branch -a master ver1 * ver1_dev remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/ver1 [devA@web git]$ echo "test devA" >> test [devA@web git]$ git commit -a -m"test again" [ver1_dev a45566d] test again 1 files changed, 1 insertions(+), 0 deletions(-) [devA@web git]$ git checkout ver1 Switched to branch 'ver1' [devA@web git]$ git merge ver1_dev ver1 Fast-forwarding to: ver1_dev Already up-to-date with ver1 Merge made by octopus. test | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
dev A has finished his development and no longer need his dev branch, so he deletes it.
[devA@web git]$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 1 commit. [devA@web git]$ git branch -d ver1_dev Deleted branch ver1_dev (was a45566d).
dev A has no rights to push the changes because they are now using a different workflow. They no longer push, they only pull. team lead has been informed of devA changes and need to merge devA changes to his.
[devA@web git]$ mail -s "Hi pal, updated my ver1, pls pull" teamlead@test.com
upon receiving the email, team lead pulls the update from dev A and checks that everything is working
[teamlead@web ~]$ sudo chmod o+x /home/devA [teamlead@web git]$ git remote add devA /home/devA/git [teamlead@web git]$ git fetch devA remote: Counting objects: 9, done. remote: Compressing objects: 100% (3/3), done. remote: Total 7 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (7/7), done. From /home/devA/git * [new branch] master -> devA/master * [new branch] ver1 -> devA/ver1 [teamlead@web git]$ git checkout -b ver1 origin/ver1 Branch ver1 set up to track remote branch ver1 from origin. Switched to a new branch 'ver1' [teamlead@web git]$ git merge devA/ver1 ver1 Fast-forwarding to: devA/ver1 Already up-to-date with ver1 Merge made by octopus. test | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
All good. now teamlead can update the staging site using his own copy.
[root@web html]# su - apache -bash-3.1$ cd /var/www/html/git -bash-3.1$ git branch -a master * ver1 -bash-3.1$ git remote add teamlead /home/teamlead/git -bash-3.1$ git fetch teamlead remote: Counting objects: 7, done. remote: Compressing objects: 100% (3/3), done. remote: Total 5 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (5/5), done. From /home/teamlead/git * [new branch] master -> teamlead/master * [new branch] ver1 -> teamlead/ver1 -bash-3.1$ git merge teamlead/ver1 ver1 Fast-forwarding to: teamlead/ver1 Already up-to-date with ver1 Merge made by octopus. test | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) -bash-3.1$ cat test test test devA
now we can toggle between different versions in staging
-bash-3.1$ git branch master * ver1
Well, after some uat testing, we found ver1 to be stable. let’s merge it back to the trunk.
-bash-3.1$ git checkout master Switched to branch 'master' -bash-3.1$ git merge ver1 Updating e604a85..fb7bd4c Fast-forward test | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
In the mean time, devB has started working on version 2
[devB@web git]$ git branch ver2 [devB@web git]$ git branch -a * master ver1 ver2 remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/ver1 [devB@web git]$ git checkout ver2 Switched to branch 'ver2' [devB@web git]$ ls test [devB@web git]$ echo "ver2" >> test [devB@web git]$ git commit -a -m "ver2" [ver2 2a091f9] ver2 1 files changed, 1 insertions(+), 0 deletions(-)
devB is aware that master repo could be updated (someone might have fixed a bug) and he needs to integrate the master changes to his ver2. As a good practice, he tries to do this everyday so that he dont get a “big merge” when he finishes his ver2.
While still in the version2 branch,
[devB@web git]$ git pull origin master From /var/www/html/git * branch master -> FETCH_HEAD Updating 230806d..879e570 Fast-forward test | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) [devB@web git]$ git rebase master First, rewinding head to replay your work on top of it... Applying: ver2 Using index info to reconstruct a base tree... Falling back to patching base and 3-way merge... Auto-merging test CONFLICT (content): Merge conflict in test Failed to merge in the changes. Patch failed at 0001 ver2 When you have resolved this problem run "git rebase --continue". If you would prefer to skip this patch, instead run "git rebase --skip". To restore the original branch and stop rebasing run "git rebase --abort".
ah, we have a conflict. devB needs to resolve it as early as he can. After editing the file,
[devB@web git]$ git add test [devB@web git]$ git rebase --continue Applying: ver2
All good. Now devB can continue his development on version 2…. When done, team lead pull changes from devB and merge into his copy. As usual, he updates the staging site. If for some reason ver2 fails in critical occasion, he can revert back to his ver1 easily by just checking out the ver1 branch – reverting changes in production can never be easier.
Conclusion
I have only covered the basics, there are much more like reverting to past versions, taging….etc. I am sure you will enjoy git as much as I do. I hope that as Git becomes more popular, there will be better GUI and integration with development IDEs/project management software. Yes, there were efforts being made till this point but I still find them buggy when compared to the command line version.
`:: I am really thankful to this topic because it really gives great information ”`