??xml version="1.0" encoding="utf-8" standalone="yes"?> 目录[-] 创徏git服务器管理用?/p> 用户名和密码均ؓ(f)git 创徏git仓库存储目录 讄git仓库权限 初始化全局讄 在默认用L(fng)ȝ录\径下Q运行以下命令,按照提示创徏公钥和私?/p> 默认情况下,公钥和私钥会(x)保存在~/.ssh目录下,如下所C:(x) 在git客户?/p> 各个用户按照前面提到的办法生成各自的ssh公钥文g后,服务器管理员把所有h?ssh公钥文g都拿来,拯到keydir目录下。修改gitosis.conf文gQ如下所C?/p> q个配置文g表达?jin)如下含义?x)gitosis-adminl成员有aQ该l对gitosis-admin仓库有读写权限; developersl有aQb两个成员Q该l对helloworld仓库有读写权限; testl有c一个成员,对helloworld仓库有只L限? 当然目前q些配置文g的修改只是在你的本地Q你必须推送到gitserver上才能真正生效?加入新文件、提交ƈpush到git服务器:(x) 默认没有 css 加蝲Q把 gitweb 要用的静(rn)态文件连接到 DocumentRoot 下:(x) 修改配置Q?/p> ?$projectroot 改ؓ(f)git仓库存储目录(例如Q?home/git/repositories)Q保存后h览器? 修改/etc/gitweb.conf 内容Q?/p> ubuntu中默认的web目录?var/wwwQ默认的cgi目录?/usr/lib/cgi-bin/,安装完成gitweb后,gitweb的gitweb.cgi?x)自动放|到该目录下?/p> 如果你的cgi路径不是默认?usr/lib/cgi-bin/Q需要将gitweb安装?usr/lib/cgi-bin中的 gitweb.cgi复制到原来配|的cgi-bin路径Qƈ在apache的配|文?etc/apache2/apache.conf末尾加上以下?容:(x) 重新启动apacheQsudo /etc/init.d/apache2 restartQ访问http://localhost/cgi-bin/gitweb.cgi 转自Q?div>http://my.oschina.net/mercury5/blog/1461711. 准备环境Q安装更?/h1>
1
sudo
apt-get update
2
sudo
apt-get upgrade
2. 安装 openssh服务?/h1>
1
sudo
apt-get
install
openssh-server openssh-client
3. 安装 git服务?/h1>
1
sudo
apt-get
install
git-core
4. 配置 git服务?/h1>
1
sudo
useradd
-m git
2
sudo
passwd
git
1
sudo
mkdir
/home/git/repositories
1
sudo
chown
git:git /home/git/repositories
2
sudo
chmod
755 /home/git/repositories
1
git config --global user.name
"myname"
2
git config --global user.email
"myname@server"
5. 安装python的setup tool
1
sudo
apt-get
install
python-setuptools
6. 获取q安装gitosis
1
cd
/tmp
2
git clone https://github.com/res0nat0r/gitosis.git
3
cd
gitosis
4
sudo
python setup.py
install
7. 配置gitosis
1
cp
~/.
ssh
/id_rsa.pub /tmp
2
sudo
-H -u git gitosis-init < /tmp/id_rsa.pub
3
sudo
chmod
755 /home/git/repositories/gitosis-admin.git/hooks/post-update
8.创徏个h公钥和私钥,另外一台pc机(git客户端)(j)
1
ssh
-keygen -t rsa
1
id_rsa id_rsa.pub known_hosts
9. 理gitosis配置
1
cd
~
2
git clone git@
hostname
:用户?gitosis-admin.git
3
cd
gitosis-admin/
01
[gitosis]
02
03
[group gitosis-admin]
04
writable = gitosis-admin
05
members = a@server1
06
07
[group developers]
08
writable = helloworld
09
members = a@server1 b@server2
10
11
[group
test
]
12
readonly
= helloworld
13
members = c@server3
1
git add .
2
git commit -am
"add helloworld project and users"
3
git remote add origin
ssh
://git@
hostname
/helloworld.git
4
git push origin master
10. 安装apache2
1
sudo
apt-get
install
apache2
11. 安装gitweb
1
sudo
apt-get
install
gitweb
12. 配置 gitweb
1
cd
/var/www/
2
sudo
ln
-s /usr/share/gitweb/* .
1
sudo
vi
/etc/gitweb.conf
如果没有扑ֈ目Q你需要将$projectroot/*.git 的属性改?55Q让apache用户有可L限。可以只改你需要让别h通过web讉K的那个git?http://localhost/cgi-bin/gitweb.cgi01
# path to git projects (<project>.git)
02
#$projectroot = "/var/cache/git";
03
$projectroot =
"/home/git/repositories"
;
04
05
# directory to use for temp files
06
$git_temp =
"/tmp"
;
07
08
# target of the home link on top of all pages
09
$home_link = $my_uri ||
"/"
;
10
11
# html text to include at home page
12
$home_text =
"indextext.html"
;
13
14
# file with project list; by default, simply scan the projectroot dir.
15
$projects_list = $projectroot;
16
17
# stylesheet to use
18
@stylesheets = (
"/gitweb/static/gitweb.css"
);
19
20
# javascript code for gitweb
21
$javascript =
"/gitweb/static/gitweb.js"
;
22
23
# logo to use
24
$logo =
"/gitweb/static/git-logo.png"
;
25
26
# the 'favicon'
27
$favicon =
"/gitweb/static/git-favicon.png"
;
28
29
# git-diff-tree(1) options to use for generated patches
30
#@diff_opts = ("-M");
31
@diff_opts = ();
13. 配置a(chn)pache2
01
SetEnv GITWEB_CONFIG /etc/gitweb.conf
02
<Directory
"/srv/www/cgi-bin/gitweb"
>
03
Options FollowSymlinks ExecCGI
04
Allow from all
05
AllowOverride all
06
Order allow,deny
07
<Files gitweb.cgi>
08
SetHandler cgi-script
09
</Files>
10
RewriteEngine on
11
RewriteCond %{REQUEST_FILENAME} !-f
12
RewriteCond %{REQUEST_FILENAME} !-d
13
RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
14
</Directory>
Git 在Wikipedia上的定义Q它是一个免费的、分布式的版本控制工P或是一个强调了(jin)速度快的源代码管理工兗Git最初被Linus Torvalds开发出来用于管理Linux内核的开发。每一个Git的工作目录都是一个完全独立的代码库,q拥有完整的历史记录和版本追t能力,不依? 于网l和中心(j)服务器?/p>
Git 的出现减M(jin)许多开发者和开源项目对于管理分支代码的压力Q由于对分支的良好控Ӟ更鼓励开发者对自己感兴的目做出贡献。其实许多开源项? 包括Linux kernel, Samba, X.org Server, Ruby on RailsQ都已经q渡C用Git作ؓ(f)自己的版本控制工兗对于我们这些喜Ƣ写代码的开发者嘛Q有两点最大的好处Q我们可以在M地点(在上班的地铁 ?提交自己的代码和查看代码版本;我们可以开许许多多个分支来实践我们的想法,而合q这些分支的开销几乎可以忽略不计?/p>
现在q入本篇文章真正的主题,介绍一下Git的基本命令和操作Q会(x)从Git的版本库的初始化Q基本操作和独有的常用命令三部分着手,让大家能够开始用Git?/p>
Git通常有两U方式来q行初始?
git clone: q是较ؓ(f)单的一U初始化方式Q当你已l有一个远E的Git版本库,只需要在本地克隆一份,例如'git clone git://github.com/someone/some_project.git some_project'命o(h)是?git://github.com/someone/some_project.git'q个URL地址的远E版 本库完全克隆到本地some_project目录下面
git init和git remoteQ这U方式稍微复杂一些,当你本地创徏?jin)一个工作目录,你可以进入这个目录,使用'git init'命o(h)q行初始化,Git以后׃(x)对该目录下的文gq行版本控制Q这时候如果你需要将它放到远E服务器上,可以在远E服务器上创Z个目录,q把 可访问的URL记录下来Q此时你可以利?git remote add'命o(h)来增加一个远E服务器端,例如'git remote add origin git://github.com/someone/another_project.git'q条命o(h)׃(x)增加URL地址?git: //github.com/someone/another_project.git'Q名UCؓ(f)origin的远E服务器Q以后提交代码的时候只需要? origin别名卛_
现在我们有了(jin)本地和远E的版本库,让我们来试着用用Git的基本命令吧Q?/p>
git pullQ?/span>从其他的版本?既可以是q程的也可以是本地的)代码更新到本地Q例如:(x)'git pull origin master'是originq个版本库的代码更新到本地的masterLQ该功能cM于SVN的update
git addQ?/span>是将当前更改或者新增的文g加入到Git的烦(ch)引中Q加入到Git的烦(ch)引中pC入了(jin)版本历史中,q也是提交之前所需要执行的一步,例如'git add app/model/user.rb'׃(x)增加app/model/user.rb文g到Git的烦(ch)引中
git rmQ?/strong>从当前的工作I间中和索引中删除文Ӟ例如'git rm app/model/user.rb'
git commitQ?/strong>提交当前工作I间的修改内容,cM于SVN的commit命o(h)Q例?git commit -m "story #3, add user model"'Q提交的时候必ȝ-m来输入一条提交信?/span>
git pushQ?/strong>本地commit的代码更新到q程版本库中Q例?git push origin'׃(x)本地的代码更新到名为orgin的远E版本库?/span>
git logQ?/span>查看历史日志
git revertQ?/strong>q原一个版本的修改Q必L供一个具体的Git版本P例如'git revert bbaf6fb5060b4875b18ff9ff637ce118256d6f20'QGit的版本号都是生成的一个哈希倹{?/span>
上面的命令几乎都是每个版本控制工h公有的,下面开始尝试一下Git独有的一些命令:(x)
git branchQ?对分支的增、删、查{操作,例如'git branch new_branch'?x)从当前的工作版本创Z个叫做new_branch的新分支Q?git branch -D new_branch'׃(x)强制删除叫做new_branch的分支,'git branch'׃(x)列出本地所有的分支
git checkoutQGit 的checkout有两个作用,其一是在不同的branch之间q行切换Q例?'git checkout new_branch'׃(x)切换到new_branch的分支上?另一个功能是q原代码的作用,例如'git checkout app/model/user.rb'׃(x)user.rb文g从上一个已提交的版本中更新回来Q未提交的内容全部会(x)回滚
git rebaseQ用下面两幅图解释会(x)比较清楚一些,rebase命o(h)执行后,实际上是分支点从CUd?jin)GQ这样分支也具有了(jin)从C到G的功?nbsp;
git resetQ?当前的工作目录完全回滚到指定的版本P假设如下图,我们有A-G五次提交的版本,其中C 的版本号? bbaf6fb5060b4875b18ff9ff637ce118256d6f20Q我们执行了(jin)'git reset bbaf6fb5060b4875b18ff9ff637ce118256d6f20'那么l果只剩下?jin)A-C三个提交的版?/p>
git stashQ将当前未提交的工作存入Git工作栈中Q时机成熟的时候再应用回来Q这里暂时提一下这个命令的用法Q后面在技巧篇?x)重点讲?/p>
git configQ?利用q个命o(h)可以新增、更改Git的各U设|,例如'git config branch.master.remote origin'将master的远E版本库讄为别名叫做origin版本库,后面在技巧篇?x)利用这个命令个性化讄你的GitQؓ(f)你打造独一无二? Git
git tagQ?可以某个具体的版本打上一个标{,q样你就不需要记忆复杂的版本号哈希g(jin)Q例如你可以使用 'git tag revert_version bbaf6fb5060b4875b18ff9ff637ce118256d6f20'来标记这个被你还原的版本Q那么以后你x看该版本Ӟ可以? revert_version标签名,而不是哈希g(jin)
Git之所以能够提供方便的本地分支{特性,是与它的文g存储机制有关的。Git存储版本控制信息时用它自己定义的一套文件系l存储机Ӟ在代码根目录下有一?git文g夹,?x)有如下q样的目录结构:(x)
?几个比较重要的文件和目录需要解释一下:(x)HEAD文g存放根节点的信息Q其实目录结构就表示一个树(wi)型结构,Git采用q种?wi)Şl构来存储版本信息, 那么HEADpC根;refs目录存储?jin)你在当前版本控制目录下的各U不同引?引用指的是你本地和远E所用到的各个树(wi)分支的信?Q它有heads? remotes、stash、tags四个子目录,分别存储对不同的栏V远E版本库、Git栈和标签的四U引用,你可以通过命o(h)'git show-ref'更清晰地查看引用信息;logs目录Ҏ(gu)不同的引用存储了(jin)日志信息。因此,Git只需要代码根目录下的q一?git目录可以记录完 整的版本控制信息Q而不是像SVN那样根目录和子目录下都有.svn目录。那么下面就来看一下Git与SVN的区别吧
SVN(Subversion)是当前用最多的版本控制工具。与它相比较QGit最大的优势在于两点Q易于本地增加分支和分布式的Ҏ(gu)?/p>
下面两幅囑֏以Ş象的展示Git与SVN的不同之?/p>
?于易于本地增加分支,图中Git本地和服务器端结构都很灵z,所有版本都存储在一个目录中Q你只需要进行分支的切换卛_辑ֈ在某个分支工作的效果? 而SVN则完全不同,如果你需要在本地试验一些自q代码Q只能本地维护多个不同的拯Q每个拷贝对应一个SVN服务器地址。D一个实际的例子Q以前我所 在的组使用SVN作ؓ(f)版本控制工具Q当我正在试囑֢Z个模块,工作做到一半,׃?x)改变原模块的行为导致代码服务器上许多测试的p|Q所以ƈ没有提交 代码。这时候上U对我说Q现在有一个很紧急的Bug需要处理, 必须在两个小时内完成。我只好本地的所有修改diffQƈ输出成ؓ(f)一个patch文gQ然后回滚有兛_前Q务的所有代码,再开始修改Bug的Q务,{到 修改好后Q在patch应用回来。前前后后要完成多个J琐的步骤,q还不计中间代码发生冲突所要进行的工作量。可是如果用GitQ? 我们只需要开一个分支或者{回到d支上Q就可以随时开始Bug修改的Q务,完成之后Q只要切换到原来的分支就可以优雅的l以前的d。只要你愿意Q每 一个新的Q务都可以开一个分支,完成后,再将它合q到d支上Q轻松而优雅?/p>
?布式对于Git而言Q你可以本地提交代码Q所以在上面的图中,Git有利于将一个大d分解Q进行本地的多次提交Q而SVN只能在本地进行大量的一 ơ性更改,D来合ƈCq上造成巨大的风险。Git的代码日志是在本地的Q可以随时查看。SVN的日志在服务器上的,每次查看日志需要先从服务器上下 载下来。我工作的小l,代码服务器在国Q每ơ查看小l几q前所做的工作Ӟ日志下蝲需要十分钟Q这不能不说是一个痛苦。后来我们迁UdGit上,利用 Git日志在本地的Ҏ(gu),我用Ruby~写?jin)一个Rake脚本Q可以查看某个具体Q务的所有代码历Ԍ每次只需要几U钟Q大大方便我的工作。当然分布式q? 不是说用?jin)Git׃需要一个代码中?j)服务器Q如果你工作在一个团队里Q还是需要一个服务器来保存所有的代码的?/p>
ȝ
本篇介绍?jin)Git的基本概c(din)一些常用命令和原理Q大家可以尝试动手体?x)一下,下一会(x)重点介绍Git命o(h)的用技巧,Git附带的工P最后会(x)在Git Hub上创Z个开源项目?/p>
下面两幅囑֏以Ş象的展示Git与SVN的不同之?
对于易于本地增加分支Q图中Git本地和服务器端结构都很灵z,所有版本都存储在一个目录中Q你只需要进行分支的切换卛_辑ֈ在某个分支工作的?果。而SVN则完全不同,如果你需要在本地试验一些自q代码Q只能本地维护多个不同的拯Q每个拷贝对应一个SVN服务器地址。D一个实际的例子Q以?我所在的组使用SVN作ؓ(f)版本控制工具Q当我正在试囑֢Z个模块,工作做到一半,׃?x)改变原模块的行为导致代码服务器上许多测试的p|Q所以ƈ没有 提交代码。这时候上U对我说Q现在有一个很紧急的Bug需要处理, 必须在两个小时内完成。我只好本地的所有修改diffQƈ输出成ؓ(f)一个patch文gQ然后回滚有兛_前Q务的所有代码,再开始修?a title="Bug">Bug?dQ等C改好后,在将patch应用回来。前前后后要完成多个J琐的步骤,q还不计中间代码发生冲突所要进行的工作量。可是如果用GitQ? 我们只需要开一个分支或者{回到d支上Q就可以随时开始Bug修改的Q务,完成之后Q只要切换到原来的分支就可以优雅的l以前的d。只要你愿意Q每 一个新的Q务都可以开一个分支,完成后,再将它合q到d支上Q轻松而优雅?
分布式对于Git而言Q你可以本地提交代码Q所以在上面的图中,Git有利于将一个大d分解Q进行本地的多次提交Q而SVN只能在本地进 行大量的一ơ性更改,D来合ƈCq上造成巨大的风险。Git的代码日志是在本地的Q可以随时查看。SVN的日志在服务器上的,每次查看日志需要先?服务器上下蝲下来。我工作的小l,代码服务器在国Q每ơ查看小l几q前所做的工作Ӟ日志下蝲需要十分钟Q这不能不说是一个痛苦。后来我们迁Ud Git上,利用Git日志在本地的Ҏ(gu),我用Ruby~写?jin)一个Rake脚本Q可以查看某个具体Q务的所有代码历Ԍ每次只需要几U钟Q大大方便我的工作。当然分布式q不是说用了(jin)Git׃需要一个代码中?j)服务器Q如果你工作在一个团队里Q还是需要一个服务器来保存所有的代码的?
原文Qhttp://www.zzbaike.com/wiki/Git%E4%B8%8ESVN%E9%97%B4%E7%9A%84%E6%AF%94%E8%BE%83X | ![]() |
| |
|
Git | Hg | Bzr | |
---|---|---|---|
Init | 0.024s | 0.059s | 0.600s |
Add | 8.535s | 0.368s | 2.381s |
Status | 0.451s | 1.946s | 14.744s |
Diff | 0.543s | 2.189s | 14.248s |
Tag | 0.056s | 1.201s | 1.892s |
Log | 0.711s | 2.650s | 9.055s |
Commit (Large) | 12.480s | 12.500s | 23.002s |
Commit (Small) | 0.086s | 0.517s | 1.139s |
Branch (Cold) | 1.161s | 94.681s | 82.249s |
Branch (Hot) | 0.070s | 12.300s | 39.411s |
git clone git://github.com/brosner/django.git dj-git
hg clone http://hg.dpaste.com/django/trunk dj-hg
bzr branch lp:django dj-bzr
svn checkout http://code.djangoproject.com/svn/django/trunk dj-svn
Git | Hg | Bzr | Bzr* | SVN | |
---|---|---|---|---|---|
Repo Alone | 24M | 34M | 45M | 89M | |
Entire Directory | 43M | 53M | 64M | 108M | 61M |
|
|
Mercurial Helpadd add the specified files ... | Git Helpadd Add file contents to the index |
我想如果看过《Git历险记》的前面三篇文章的朋友可能已l知道怎么?a >git addQ?a >git commitq两个命令了(jin)Q知道它们一个是把文件暂存到索引中ؓ(f)下一ơ提交做准备Q一个创建新的提交(commitQ。但是它们台前幕后的一些有的l节大家不一定知晓,请允许我一一道来?/p>
Git 索引是一个在你的工作目录Qworking treeQ和目仓库间的暂存区域(staging area)。有?jin)? 你可以把许多内容的修改一h?commit)?如果你创Z(jin)一个提?commit)Q那么提交的一般是暂存区里的内? 而不是工作目录中的内宏V?/p>
一个Git目中文件的状态大概分成下面的两大c,而第二大cd分ؓ(f)三小c:(x)
看到上面的这么多的规则,大家早就头大?jin)吧。老办法,我们Z个Git试目来试验一下:(x)
我们先来Z个空的项目:(x)
$rm -rf stage_proj
$mkdir stage_proj
$cd stage_proj
$git init
Initialized empty Git repository in /home/test/work/test_stage_proj/.git/
我们q创Z个内Ҏ(gu)“hello, world”的文Ӟ(x)
$echo "hello,world" > readme.txt
现在来看一下当前工作目录的状态,大家可以看到“readme.txt”处于未被跟踪的状态(untracked fileQ:(x)
$git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# readme.txt
nothing added to commit but untracked files present (use "git add" to track)
?#8220;readme.txt"加到暂存区:(x) $git add readme.txt
现在再看一下当前工作目录的状态:(x)
$git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: readme.txt
#
可以看到现在"readme.txt"的状态变成了(jin)已暂存可以被提交Qchanges to be committedQ,q意味着我们下一步可以直接执?#8220;git commit“把这个文件提交到本地的仓库里M(jin)?/p>
暂存区(staging areaQ一般存攑֜“git目录“下的index文gQ?git/indexQ中Q所以我们把暂存区有时也叫作索引QindexQ。烦(ch)引是一个二q制? 式的文gQ里面存放了(jin)与当前暂存内容相关的信息Q包括暂存的文g名、文件内容的SHA1哈希串值和文g讉K权限Q整个烦(ch)引文件的内容以暂存的文g名进行排 序保存的?/p>
但是我不想马上就把文件提交,我想看一下暂存区Qstaging areaQ里的内容,我们执行git ls-files命o(h)看一下:(x)
$git ls-files --stage
100644 2d832d9044c698081e59c322d5a2a459da546469 0 readme.txt
我们如果有看q?a >上一文?/a>? ?庖丁解牛", 你会(x)发现“git目录“里多Z(jin)”.git/objects/2d/832d9044c698081e59c322d5a2a459da546469”q? 么一个文Ӟ再执?#8220;git cat-file -p 2d832d” 的话Q就可以看到里面的内Ҏ(gu)?#8220;hello,world"。Git在把一个文件添加暂存区Ӟ不但把它在烦(ch)引文?.git/index)里挂?jin)号Q? 且把它的内容先保存到?#8220;git目录“里面M(jin)?/p>
如果我们执行”git add“命o(h)时不心(j)把不需要的文g也加入到暂存Z话,可以执行“git rm --cached filename" 来把误添加的文g从暂存区中移除?/p>
现在我们先在"readme.txt"文g上做一些修改后Q?/p>
$echo "hello,world2" >> readme.txt
再来看一下暂存区的变?
$git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: readme.txt
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: readme.txt
#
大家可以看到命o(h)输出里多?jin)一块内容:(x)“changed but not updated ...... modified: readme.txt”。大家可能会(x)觉得很奇怪,我前面不是把"readme.txt"q个文gl添加到暂存区里M(jin)吗,q里怎么又提C我未添加到暂存? Qchanged but not updatedQ呢Q是不是Git搞错?jin)呀?/p>
Git 没有错,每次执行“git add”d文g到暂存区Ӟ它都?x)把文g内容q行SHA1哈希q算Q在索引文g中新加一,再把文g内容存放到本地的“git目录“里。如果在上次执行 “git add”之后再对文g的内容进行了(jin)修改Q那么在执行“git status”命o(h)ӞGit?x)对文g内容q行SHA1哈希q算׃(x)发现文g又被修改?jin),q时“readme.txt“同时呈C(jin)两个状态:(x)被修改但? 被暂存的文gQchanged but not updatedQ,已暂存可以被提交的文Ӟchanges to be committedQ。如果我们这时提交的话,是只会(x)提交W一?#8220;git add"所以暂存的文g内容?/p>
我现在对?#8220;hello,world2"的这个修改不是很满意Q想要撤消这个修改,可以执行git checkoutq个命o(h)Q?/p>
$git checkout -- readme.txt
现在再来看一下仓库里工作目录的状态:(x)
$git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: readme.txt
#
好的Q现在项目恢复到我想要的状态了(jin)Q下面我qgit commit 命o(h)把这个修Ҏ(gu)交了(jin)吧:(x)
$git commit -m "project init"
[master (root-commit) 6cdae57] project init 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 readme.txt
现在我们再来看一下工作目录的状态:(x)
$git status
# On branch master
nothing to commit (working directory clean)
大家可以看到“nothing to commit (working directory clean)”Q如果一个工作树(wi)Qworking treeQ中所有的修改都已提交C(jin)当前分支里(current headQ,那么p它是q净的(cleanQ,反之它就是脏?dirty)?/p>
正如Git is the next Unix 一文中所说的一PGit是一U全新的使用数据的方式(Git is a totally new way to operate on dataQ。Git把它所理的所有对象(blobQtreeQcommitQtag……Q,全部Ҏ(gu)它们的内容生成SHA1哈希串g为对象名Q根据目 前的数学知识Q如果两块数据的SHA1哈希串值相{,那么我们可以认两块数据是相?的。这样会(x)带来的几个好处:(x)
我们通过下面的例子,来验证上面所说的是否属实。现在创Z个和“readme.txt“内容完全相同的文?#8221;readme2.txt“Q然后再把它提交到本C库中Q?/p>
$echo "hello,world" > readme2.txt
$git add readme2.txt
$git commit -m "add new file: readme2.txt"
[master 6200c2c] add new file: readme2.txt
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 readme2.txt
下面的这条很复杂的命令是查看当前的提交(HEADQ所包含的blob对象Q?/p>
$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme.txt
100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme2.txt
我们再来看看上一ơ提交(HEAD^Q所包含的blob对象Q?/p>
$git cat-file -p HEAD^ | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme.txt
很明昑֤家看到尽当前的提交比前一ơ多?jin)一个文Ӟ但是它们之间却是在共用同一个blob对象Q?#8220;2d832d9”?/p>
Git 与大部分你熟(zhn)的版本控制pȝQ如Subversion、CVS、Perforce 之间的差别是很大的。传l系l用的是:(x) “增量文gpȝ” QDelta Storage systemsQ,它们存储是每ơ提交之间的差异。而Git正好与之相反Q它是保存的是每ơ提交的完整内容QsnapshotQ;它会(x)在提交前Ҏ(gu)要提? 的内Ҏ(gu)SHA1哈希串g为对象名Q看仓库内是否有相同的对象,如果没有将?#8220;.git/objects"目录创徏对应的对象,如果有就?x)重用已有? 对象Q以节约I间?/p>
下面我们来试验一下Git是否真的是以“snapshot”方式保存提交的内宏V?/p>
先修改一?readme.txt"Q给里面加点内容Q再把它暂存Q最后提交到本地仓库中:(x)
$echo "hello,world2" >> readme.txt
$git add readme.txt
$git commit -m "add new content for readme.txt"
[master c26c2e7] add new content for readme.txt 1 files changed, 1 insertions(+), 0 deletions(-)
我们现在看看当前版本所包含的blob对象有哪些:(x)
$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob 2e4e85a61968db0c9ac294f76de70575a62822e1 readme.txt
100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme2.txt
从上面的命o(h)输出Q我们可以看?readme.txt"已经对应?jin)一个新的blob对象Q?#8220;2e4e85a”Q而之前版本的"readme.txt“对应的blob对象是:(x)“2d832d9”。下面我们再来看一看这两个”blob“里面的内容和我们的预期是否相同:(x)
$git cat-file -p 2e4e85a
hello,world
hello,world2
$git cat-file -p 2d832d9
hello,world
大家可以看到Q每一ơ提交的文g内容q是全部保存的(snapshotQ?/p>
Git内在机制和其它传l的版本控制pȝQVCSQ间存在本质的差异,所以Git的里"add"操作的含义和其它VCS存在差别也不ؓ(f)奇,“git add“不但能把未跟t的文gQuntracked fileQ添加到版本控制之下Q也可以把修改了(jin)的文章暂存到索引中?/p>
同时Q由于采?#8220;SHA1哈希串值内容寻?#8220;?#8221;快照存储QsnapshotQ?#8220;Q让Git成ؓ(f)一个速度非常非常快的版本控制pȝQVCSQ?/p>
感谢上帝Ҏ(gu)的眷,让我可以有写作专栏的q样一个机?x)?/p>
感谢朋友们在写作q程的无U帮助:(x)张凯?/a>Q?a >刘炜Q?a >许晓?/a>Q?a >Fenng……
特别要感家人默默支持:(x)Q?/p>
原文Qhttp://www.infoq.com/cn/news/2011/03/git-adventures-index-commit
如果我们要把一个项目加入到Git的版本管理中Q可以在目所在的目录?span>git init命o(h)建立一个空的本C库,然后再用git add命o(h)把它们都加入到Git本地仓库的暂存区Q?span>stage or indexQ中Q最后再?span>git commit命o(h)提交到本C库里?/span>
创徏一个新的项目目录,q生成一些简单的文g内容Q?/span>
$ mkdir test_proj
$ cd test_proj
$ echo “hello,world” > readme.txt
在项目目录创建新的本C库,q把目里的所有文件全部添加、提交到本地仓库中去Q?/span>
$ git init #在当前的目录下创Z个新的空的本C?/span>
Initialized empty Git repository in /home/user/test_proj/.git/
$ git add . #把前目录下的所有文件全部添加到暂存?/span>
$ git commit -m 'project init' #创徏提交
[master (root-commit) b36a785] project init
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 readme.txt
Git目录的结?/span>
git init命o(h)在项目的层目录中徏?jin)一个名为:(x)“.git”的目录,它的别名?“Git目录”Q?span>Git directoryQ。这?#8221;Git目录”中虽然有一些文Ӟ但是没有M提交QcommitQ在里面Q所以我们叫它是IZ库(empty Git repositoryQ?/span>
?span> SVN不同Q一个Git目一般只在项目的根目录下Z?#8220;.git”目录Q而SVN则会(x)在项目的每一个目录下Z?#8221;.svn”目录Q这也我喜欢Git的原因之一Q)(j)
Git把所有的历史提交信息全部存储?#8220;Git目录”里,它就是一个Git目的仓库;你对本地的源代码q行~辑修改后创建的提交也都?x)先保存在? 里面Q然后再推送到q端的服务器。当我们我把目目录?#8220;Git目录”一h到其它电(sh)脑里Q它能马上正常的工作Q所有的提交信息全都保存在Git目录 里)(j)Q甚臛_以只?#8220;Git目录”拯也行Q但是要再签出(checkoutQ一ơ?/span>
GitZ(jin) 调试的方便,它可以指定项目的Git目录的位|。有两种办法Q一是设|?#8220;GIT_DIR”环境变量Q二是在命o(h)行里讑֮“--git-dir--git-dir”参数指定它的位置Q大家可以看一下这?span>(git(1) Manual Page)?/span>
庖丁解牛
前面的这些东东我?a >W一?/a>里也大概的讲q一些,但是今天我们想不但要开动这辆叫“Git”的跑车,q想看看它里面有些什么样的零Ӟ是怎么构成的?/span>
OKQ我们来看看“test_proj”目里的“Git目录”的结构:(x)
$cd test_proj/.git
$ ls | more
branches/ # 新版的Git已经不再使用q个目录Q所以大家看到它 #一般会(x)是空?/span>
COMMIT_EDITMSG # 保存着上一ơ提交时的注释信?/span>
config # 目的配|信?/span>
description # 目的描qC?/span>
HEAD # 目当前在哪个分支的信息
hooks/ # 默认?#8220;hooks” 脚本文g
index # 索引文gQgit add 后把要添加的Ҏ(gu)存到q里
info/ # 里面有一个exclude文gQ指定本目要忽略的文g #Q看一下这?/span>
logs/ # 各个refs的历史信?/span>
objects/ # q个目录非常重要Q里面存储都是Git的数据对?/span>
# 包括Q提?commits), ?wi)对?trees)Q二q制对象 #QblobsQ?标签对象QtagsQ?/span>
#不明白没有关p,后面?x)讲的?/span>
refs/ # 标识着你的每个分支指向哪个提交QcommitQ?/span>
我先?span>git log命o(h)来看一下这个Git目里有哪些提交Q?/span>
$ git log
commit 58b53cfe12a9625865159b6fcf2738b2f6774844
Author: liuhui998 <liuhui998@nospam.com>
Date: Sat Feb 19 18:10:08 2011 +0800
project init
大家可以看到目前只有一个提交(commitQ对象,而它的名字就 是:(x)”58b53cfe12a9625865159b6fcf2738b2f6774844”。这个名字就是对象内容的一个SHA{串|只要对象里面 的内容不同,那么我们可以认为对象的名字不会(x)相同Q反之也成立。我在用时一般不用把q个40个字W输全,只要把前面的5~8个字W输完就可以Q前提是 和其它的对象名不冲突Q。ؓ(f)?jin)方便表C,在不影响表达的情况下Q我?x)只写SHA串值的?个字W?/span>
我们可以?span>git cat-file来看一下这个提交里的内Ҏ(gu)什?
$ git cat-file -p 58b53c
tree 2bb9f0c9dc5caa1fb10f9e0ccbb3a7003c8a0e13
author liuhui998 <liuhui998@nospam.com> 1298110208 +0800
committer liuhui998 <liuhui998@nospam.com> 1298110208 +0800
project init
大家可以看到Q提?#8220;58b53c” 是引用一个名?#8220;2bb9f0”的树(wi)对象QtreeQ。一个树(wi)对象QtreeQ可以引用一个或多个二进制对象(blobQ? 每个二进制对象都对应一个文件?更进一? ?wi)对象也可以引用其他的?wi)对象Q从而构成一个目录层ơ结构。我们再看一下这个树(wi)对象QtreeQ里面有什么东东:(x)
$ git cat-file -p 2bb9f0
100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme.txt
不难看出Q?bb9f0”q个?wi)对象(treeQ包括了(jin)?jin)一个二q制对象QblobQ,对应于我们在前面创徏的那个叫 ”readme.txt”的文件。现在我们来看看q个”blob”里的数据是不是和前面的提交的内容一_(d)(x)
$ git cat-file -p 2d832d
hello,world
哈哈Q熟(zhn)的“hello,world”又回来了(jin)?/span>
想不想看看提交对象、树(wi)对象和二q制对象是怎么?#8221;Git目录“中存储的Q没有问题,执行下面的命令,看看”.git/objects”目录里的内容Q?/span>
$ find .git/objects
.git/objects
.git/objects/2b
.git/objects/2b/b9f0c9dc5caa1fb10f9e0ccbb3a7003c8a0e13
.git/objects/2d
.git/objects/2d/832d9044c698081e59c322d5a2a459da546469
.git/objects/58
.git/objects/58/b53cfe12a9625865159b6fcf2738b2f6774844
.git/objects/info
.git/objects/pack
如果大家仔细看上面命令执行结果中的粗体字Q所有的对象都用SHA{串g为烦(ch)引存储在”.git/objects”目录之下QSHA串的前两个字W作为目录名Q后面的38个字W作为文件名?/span>
q些文g的内容其实是压羃的数据外加一个标注类型和长度的头。类型可以是提交对象QcommitQ、二q制对象QblobQ??wi)对象(treeQ或者标{֯象(tagQ?/span>
如何clone一个远E项?/span>
我n边的很多朋友是因得到某个开源项目的代码Q所以才开始学?fn)用Git。而获取一个项目的代码的一般的做法是?span>git clone命o(h)q行直接复制?/span>
例如Q有些朋友可能想看一下最新的linux内核源代?/a>Q当我们打开它的|站Ӟ发现有如下面的一D|C:(x)
URL
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
URL下面的三行字W串表示三个地址Q我们可以通过q三个地址得到同样的一份Linux内核源代码?/span>
也就是说下面q三条命令最l得到的是同一份源代码Q?/span>
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
git clone http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
git cone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
我们先来看一下URLQgit://、http://、https://q些代表是传输git仓库的协议Ş式,?#8220;git.kernel.org“则代表了(jin)Git仓库存储的服务器名字Q域名)(j)Q?span>“/pub/scm/linux/kernel/git/torvalds/linux-2.6.git” 则代表了(jin)Git仓库在服务器上位|?/span>
Git 仓库除了(jin)可以通过上面?/span>git?/span>http?/span>https协议传输外还可以通过ssh?/span>ftp(s)?/span>rsync{协议来传输?span>git clone的本质就是把“Git目录”里面的内Ҏ(gu)贝过来,大家x看,一般的“Git目录”里有成千上万的各U对象(提交对象Q树(wi)对象Q二q制对象......)Q如果逐一复制的话Q其效率可惌知?/span>
如果通过git、ssh协议传输Q服务器端会(x)在传输前把需要传输的各种对象先打好包再进行传输;而httpQsQ协议则?x)反复请求要传输的不同?象。如果仓库里面的提交不多的话Q前者和后者的效率相差不多Q但是若仓库里有很多提交的话Qgit、ssh协议q行传输则会(x)更有效率?/span>
不过现在Git对httpQsQ协议传输Git仓库做了(jin)一定的优化QhttpQsQ传输现在也能达到ssh协议的效率,有兴的朋友可以看一下这里(Smart HTTP TransportQ?/span>
好的Q现在我们执行了(jin)下面q条命o(h)Q把linux-2.6的最新版源代码clone下来Q?/span>
$cd ~/
$mkdir temp
$git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
Initialized empty Git repository in /home/liuhui/temp/linux-2.6/.git/
remote: Counting objects: 1889189, done.
remote: Compressing objects: 100% (303141/303141), done.
Receiving objects: 100% (1889189/1889189), 385.03 MiB | 1.64 MiB/s, done.
remote: Total 1889189 (delta 1570491), reused 1887756 (delta 1569178)
Resolving deltas: 100% (1570491/1570491), done.
Checking out files: 100% (35867/35867), done.
当我们执行了(jin)“git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git”q条命o(h)后大家可以看到这条输出:(x)
Initialized empty Git repository in /home/user/temp/linux-2.6/.git/
q就是意味着我们在本地先Z(jin)一?#8220;linux-2.6”目录Q然后在q个目录Z(jin)一个空的Git本地仓库QGit目录Q;里面会(x)存储从网上拉下来的历史提交?/span>
下面两条输入代表服务器现在调?span> git-pack-objects 对它的仓库进行打包和压羃Q?/span>
remote: Counting objects: 1888686, done.
remote: Compressing objects: 100% (302932/302932), done.
然后客户端接收服务器端发q送过来的数据Q?/span>
Receiving objects: 100% (1889189/1889189), 385.03 MiB | 1.64 MiB/s, done.
在我们执行完上面的clone linux-2.6代码的的操作后,Git?x)?#8220;Git目录”里把最新的代码到签出(checkoutQ到“linux-2.6”q个目录里面。我们一? 把本地的“linux-2.6”q个目录叫做”工作目录“Q?span>work directoryQ,它里面保存着你从其它地方cloneQ?span>or checkoutQ过来的代码。当你在目的不同分支间切换Ӟ“工作目录”中的文g可能?x)被替换或者删除;“工作目录”只是保存着当前的工作,你可以修 攚w面文件的内容直到下次提交为止?/span>
大家q记得前面的“庖丁解牛”吗,是不是觉得只杀一头叫“hello,world”的小牛太不过瘾了(jin)。没有问题,拿v前面的那把小刀Q来剖析一下现在h在你盘里这头叫“linux-2.6”大牛看看Q我想一定很好玩?/span>
在写文章的q程中,我要感谢在那些关?j)我q提出真诚意见的朋友Q如果没有你们真诚的意见Q我也许没有q么强烈的紧q感Q也不会(x)深深的感到自q不。我是第一ơ写专栏Q?a >张凯?/a>同学l了(jin)我很大的帮助。最后还是要感谢我的家hQ是他们让我有时间来q行写作Q)(j)
原文Qhttp://www.infoq.com/cn/news/2011/02/git-adventures-local-repository
从这一开始,我就比?#8220;啰嗦”的和大家一起从零开始经历Git使用的每一步,当然Ҏ(gu)而言q也是一个重新认识Git的过E?/p>
使用Git的第一步肯定是安装GitQ因为在多数q_上Git是没有预装的。我qx主要的工作环境是windows和LinuxQubuntuQ,我想看这文章的同学多半也是在这两个q_下工作;下面我讲一下如何在q两个^C安装和配|Git?/p>
BTW:如果是苹果^台的用户的安装可以参看一下这?1,2)Q配|和命o(h)行的使用与windows、LinuxQ?nixQ^台差别不大?/p>
Linus开发Git的最初目的就是ؓ(f)?jin)开发Linux内核服务的,自然它对Linux的^台支持也是最的。在Linux下安装Git大约有几U方法:(x)
从源代码开?q种Ҏ(gu)也适合于多?nixq_)
?a >Git官网?a >下蝲面下蝲它最新稳定版?a >源代?/a>Q就可以从源代码开始编译、安装:(x)
$ wget http://kernel.org/pub/software/scm/git/git-1.7.3.5.tar.bz2
$ tar -xjvf git-1.7.3.5.tar.bz2
$ cd git-1.7.3.5
$ make prefix=/usr all ;# prefix讄你的Git安装目录
$ sudo make prefix=/usr install ;# 以root权限q行
Z(jin)~译Git的源代码Q我们还需要一些库: expat?a >curl?zlib ?opensslQ?除了(jin)expat 外,其它的库可能在你的机器上都安装了(jin)?/p>
使用安装包管理器Qapt ?yumQ?/strong>
?fedora {系l下?a >yum Q?/p>
$ yum install git-core
在debian, ubuntu{系l下?a >apt Q?/p>
$ apt-get install git-core
有时候,你系l里的安装包理器出C(jin)问题Q或是要安装Git的机器不能上|、没有编译器的话Q你可以从下面的站点M?“.deb” ?“.rpm”的安装包Q?/p>
windowsq_有两个模?nix likeq行环境的工P(x)cygwinQ?a >msysQGit?a >cygwinQ?a >msys下都有相应的UL版本。我个h觉得msysq_下的msysGit最好用Q现在我在windows下也是用的这个版本?/p>
很多同学可能要问Q现在windows下有那多Git用户Qؓ(f)什么Git不直接出一个windows native版。俺当年ȝ?jin)一?a >Git的源代码Q它里面使用?jin)大量?nixq_的native apiQ而这些api在windows下是没有的,所以必要用cygwin、msysq样的一个中间层来满Y件移植的要求?/p>
下面?#8220;啰嗦”一下如何在windows下安装msysGit?/p>
到它?a >下蝲面M载一个最新的完整安装包,W者在撰写本文时下载的?a >q个?/p>
安装的过E没有什么好说的Q一般是开始安装后Q一路的点击“下一?#8221;。由于windowsq_的换行符QCRLFQ和Linux(*nix)q_的换行符QLFQ不同,那么在windows下开发其它^台Y件的朋友有一个地方要注意Q见下图)Q?/p>
在这里一最好?#8220;Checkout as-is, commit as-is”q个选项Q这PGit׃?x)修改你代码的换行符风格?/p>
以前有个朋友因ؓ(f)选错?jin)这个选项Q以致他在windowsq_下的一{ևQcheckoutQ其它^台的代码Q就?x)显C?#8221;已修?#8220;QmodifiedQ,不过后来可能msysGit也认识到q个问题?jin),把默认选项Ҏ(gu)?jin)这个选项?/p>
BTW: 其实前面两项也是有用的,如果对windows和Linux(*nix)q_如何处理换行W?/a>很熟(zhn)的话,也可以尝试一下前面两个选项Q)(j)
在Linux下和windows下配|Git的方法差不多Q只是在Linux下,可以在命令行里直接?a >git configq行配置, 而在windows下则要先打开“Git Bash”Q进入msysGit命o(h)行界面,再用git config命o(h)q行相应的配|操作?/p>
好了(jin)Q前面安装好?jin)GitQ现在我们开始配|:(x)
W一个需要配|的是用户的用户名和emailQ因些内容会(x)出现在你的每一个提交(commitQ里面的Q像下面q样Q?/p>
$ git log #我们用git log查看当前仓库的提交(commitQ日?br /> commit 71948005382ff8e02dd8d5e8d2b4834428eece24
Author: author <author@corpmail.com>
Date: Thu Jan 20 12:58:05 2011 +0800
Project init
下面的这两行命o(h)是讄用户名和emailQ?/p>
$ git config --global user.name author #用户名设ؓ(f)author
$ git config --global user.email author@corpmail.com #用户邮p为author@corpmail.com
Git的配|信息分为全局和项目两U,上面命o(h)中带?#8220;--global"参数Q这意x在进行全局配置Q它?x)?jing)响本Z的每个一个Git目?/p>
大家看到Q上面我们用的是@corpmailQ公叔R)(j)Q但是有时候我们可能也参与?jin)一些开源项目,那么需要新的用户名和自qUh邮箱QGit 可以为每个项目设定不同的配置信息?/p>
在命令行环境Q进入Git目所在目录,执行下面的命令:(x)
$ git config user.name nickname#用户名设ؓ(f)nickname
$ git config user.email nickname@gmail.com #用户邮p为nickname@gmail.com
Git的设计哲学和LinuxQ?nixQ一P量的?a Q用L(fng)录下?gitconfigQ文件中Q?/p>
我们用cat、head命o(h)查看全局配置信息文gQƈ假设相关配置信息存储在文件的?行(当然也有可能不在?行,q里只是Z(jin)方便表示Q?/p>
$ cat ~/.gitconfig | head -3
[user]
name = author
email = author@corpmail.com
而项目配|文件是存放在Git目所在目录的".git/config"文g中,q里也像上面一L(fng)cat、head命o(h)查看一下:(x)
$ cat .git/config | head -3
[user]
name = nickname
如果大家对于Git熟?zhn)后,可以直修?#8221;~/.gitconfig”,”.git/config”q两个文件进行配|?/p>
Git里还有很多可以配|的地方Q大家可以参考一?a >git config ?定制git?/p>
q一写h有点qxE无奇Q但q是一个Git用户q出的第一步。后面我q会(x)有一pd的文章出来,都是我个Z用过E中的感(zhn)?/p>
有朋友问我:(x)“Z么把文章叫作Q?#8216;Git历险?#8217;”。这是因为在使用Git的历E中Q我到qN多的问题Q同时也觉得它有点小复杂。但是当q些问题解开后,有时不得不赞叹它设计的巧妙之处?/p>
如果大家对于我的文章有什么问题和Q欢q给我写邮gQ?img _p="true" alt="" _href="img://image1.jpg" src="http://www.infoq.com/resource/news/2011/01/git-adventures-install-config/zh/resources/image2.JPG" border="0" />
之前我徏立了(jin)一?git中文用户l?/a> Q如果大家在使用Git的过E中到什么麻?ch)事Q欢q你在这个用L(fng)里提问?/p>
参考资料:(x)