首先你最好把自己介绍给git系统
git config –global user.name “Your Name”
git config –global user.email “[email protected]”
现在我手头已经有了一个项目(假设只有main.c一个文件),而且这个项目的全部代码和资源都放在rocrocket目录下,我将用下面的步骤来导入这个项目:
cd rocrocket/
git init
ls -a
git add .
git commit
git init命令用于初始化当前所在目录的这个项目,shell返回的提示表明已经建立了一个.git隐藏目录来保存这个项目目
前的进展信息。我们可以用ls -a 看到它。
git add .这个命令要求git给我目前的这个项目制作一个快照snapshot(快照只是登记留名,快照不等于记录在案,git管快照叫做索引index)。快照一般会暂时存储在一个临时存储区域中。
git commit用于将快照里登记的内容永久写入git仓库中,也就是开发者已经想好了要提交自己的开发成果了。
在输入git commit并按回车时会转到一个vi窗口,要求开发者输入这次提交的版本和开发信息。意思就是说这个项目目前的版本是多少,已经完成了哪些功能,还有哪些功能是需要以后完成的等等信息.
这次我们来研究“改进代码之后怎么提交给git”。
cat -n main.c
进入编辑(vi)模式对内容修改后
接下来的两道工序主要是由开发者最后确认一下“自己的修改”:
git diff –cached
这个git diff –cached是用来查看index file 和仓库之间代码的区别的。由于我们目前只是在working tree里做了修改,
还没有报告给index file,所以使用此命令显然会输出空信息。而如果省略–cached选项的话,就是比较working tree和index file 的区别,由于我们的确在working tree里做了修改,所以使用git diff后会输出修改信息。
使用git diff了解了不同之后,还可以使用git status命令来获取整体改动的信息:
git status
可以看到提示信息“changed but not updated”,就是说git发现你有已经修改了但还未git add的内容。
如果git提示说“Changes to be committed”,那就是表明git发现了你已经git add但还未git commit的内容。
如果git提示说“Untracked files”,那么就是你增加了新文件或者在某个子目录下增加了新文件。
下面该进入提交阶段了。首先运行
git add main.c
这句是要告诉git,我已经修改了main.c文件,你(指git)去检查一下。当然,如果你新增加了一个文件,比如
new.c,也需要在这里先执行git add new.c告诉git。
提交我的工作:
git commit
至此,我的修改工作完成了,而且也顺利地提交给了git。还是不放心?来查查:
git log
总结一下
如果修改了项目代码,先git add你修改过的文件,再git diff并git status查看确认,然后git commit提交,然后输入
你的开发日志,最后git log再次确认。
现在教给你一个偷懒方法,那就是git commit -a,这个命令可以直接提交所有修改,省去了你git add和git diff和git commit的工序,可谓一条龙服务。
但是,此处有一点应该注意,那就是git commit -a无法把新增文件或文件夹加入进来,所以,如果你新增了文件或文件夹,那么就要老老实实的先git add .,再git commit但如果你觉得git log给出的信息太单薄了,可以使用git log -p,这样git不但会给出开发日志,而且会显示每个开发版本的代码区别所在。
如何管理分支:
还是接着我们之前的main.c的项目走。我想试着开发一个报时功能加入到main.c中,但我不保证这个功能一定能够实现。这个时候可以运行git branch命令来开启一个实验分支:
git branch experimental
查看分支:
Git branch
我要进行报时功能的开发,当然我就要切换到experimental分支:
git checkout experimental
修改后如下:
cat -n main.c
1 #include<stdio.h>
2 #include<time.h>
3 int main()
4 {
5 time_t mytime;
6 struct tm *mylocaltime;
7 mytime=time(NULL);
8 mylocaltime=localtime(&mytime);
9 printf(”Year:%d\n”,mylocaltime->tm_year+1900);
10 printf(”Month:%d\n”,mylocaltime->tm_mon+1);
11 printf(”Day:%d\n”,mylocaltime->tm_mday);
12 printf(”Hour:%d\n”,mylocaltime->tm_hour);
13 printf(”Min:%d\n”,mylocaltime->tm_min);
14 printf(”Second:%d\n”,mylocaltime->tm_sec);
15 printf(”Version: 0.02\n”);
16 printf(”Hello world!\n”);
17 return 0;
假设运行正常
下面的任务就是提交程序到分支项目:(注意虽然已经确认了分支的正确性,但还是不能着急报告给“主干道”,而还要先在分支上提交工作)
git commit -a
然后就可以切换到“主干道”了:
git checkout master
此时,无论使用log 或是status命令都无法看到刚才在experimental分支所进行的工作。
下面,我们就来合并“分支”和“主干道”:
git merge experimental
没有错误的话 gitk 会出来什么?
删除分支: git branch -d experimental
在这里使用的是小写的-d,表示“在分支已经合并到主干后删除分支”。
如果使用大写的-D的话,则表示“不论如何都删除分支”,-D当然使用在“分支被证明失败”的情况下喽。
合作开发:
目前我已经拥有的项目放在/home/rocrocket/git-study/rocrocket里面,其他人可以通过如下方式获得代码:
git clone /home/rocrocket/git-study/rocrocket myrepo
这样小强就克隆了我的工作成果到了他的家目录中的myrepo中。OK,小强可以对myrepo里的程序进行改进了。在修改完毕之后,小强使用git commit -a来提交他的改进成果。此后,小强告诉我他已经完成了改进工作,让我去查看一下。
我为了肯定小强的劳动成果(也就是将他的改进合并到项目中来),我是这么干的:
cd /home/rocrocket/git-study/rocrocket
git pull /home/xiaoqiang/myrepo master
这句话的意思是:将小强的工作的master合并到我的当前的分支上(通常是主干道)。当然,如果这期间,我也修改了项目,那么git pull的时候如果报告冲突,我也是需要自己来搞定的。其实pull命令完成了两个动作,首先从远端分支获取diff信息,第二个动作就是将改变合并到本地分支中。读者要记住一个小技巧,那就是“git pull .”命令,它和git merge的功能是一样的,以后完全可以用git pull .来代替git merge
git fetch /home/bob/myrepo master:bobworks //此命令意思是提取出bob修改的代码内容,然后放到我(rocrocket)工作目录下的bobworks分支中。之所以要放到分支中,而不是master中,就是要我先仔仔细细看看bob的开发成果,如果我觉得满意,我再merge到master中,如果不满意,我完全可以直接git branch -D掉。
$git whatchanged -p master..bobworks //用来查看bob都做了什么
$git checkout master //切换到master分区
$git pull . bobworks //如果我检查了bob的工作后很满意,就可以用pull来将bobworks分支合并到我的项目中了
$git branch -D bobworks //如果我检查了bob的工作后很不满意,就可以用-D来放弃这个分支就可以了
过了几天,bob如果想继续帮助我开发,他需要先同步一下我这几天的工作成果,只要在其当初clone的myrepo目录下执行git pull即可:
#git pull //不用加任何参数,因为当初clone的时候,git已经记住了我(rocrocket)的工作目录,它会直接找到我的目录来取。
对文档层次的概括:(很精彩,很简洁)
将 Current working directory 记为 (1)
将 Index file 记为 (2)
将 Git repository 记为 (3)
他们之间的提交层次关系是 (1) -> (2) -> (3)
git add完成的是(1) -> (2)
git commit完成的是(2) -> (3)
git commit -a两者的直接结合
从时间上看,可以认为(1)是最新的代码,(2)比较旧,(3)更旧
按时间排序就是 (1) <- (2) <- (3)
git diff得到的是从(2)到(1)的变化
git diff –cached得到的是从(3)到(2)的变化
git diff HEAD得到的是从(3)到(1)的变化
从独立开发者的角度进行的一次操作:
$ git-init
$ git add .
$ git commit -m “important of mypro source tree.”
$ git tag v2.43 //给这个commit起了一个简单的名字v2.43
下面我们建立分支并继续开发:
$ git checkout -b alsa-audio //-b用于建立一个新的分支,分支名称为alsa-audio,并且转移到此分支
…(开发/编译/测试)
$ git checkout — curses/ux_audio_oss.c //用于取消对curses/ux_audio_oss.c文件的修改
$ git add curses/ux_audio_alsa.c //如果你在这一阶段的开发过程中增加了新文件,那么你应该用git-add告知git仓库,当然,
如果你只是修改或删除,那么使用git-commit -a就可以让git查觉到了。
…(开发/编译/测试)
$ git diff HEAD //查看一下我们即将commit的内容
$ git commit -a -s //提交
…(开发/编译/测试)
$ git reset –soft HEAD^ //回复到上一次commit的代码。–soft选项表示不改动index file和working tree中的内容
…(开发/编译/测试)
$ git diff ORIG_HEAD //look at the changes since the premature commit we took back
$ git commit -a -c ORIG_HEAD //重新提交,-c ORIG_HEAD表示使用原有的提交信息
$ git checkout master
$ git merge alsa-audio
$ git log –since=’3 days ago’
$ git log v2.43.. curses/ //查看自从v2.43以来的curses目录下的代码变化信息
合作开发者的角度:
$git clone git://git.kernel.org/pub/scm/…/torvalds/linux-2.6 my2.6
$cd my2.6
(开发…编译…测试…)
$git commit -a -s //-s选项用于在commit信息后加上结束标志
$git format-patch origin //从本地分支生成patch,用于email提交
$git pull //从origin取出更新并合并到当前分支
$git log -p ORIG_HEAD.. arch/i386 include/asm-i386
$git pull git://git.kernel.org/pub/…/jgarzik/libata-dev.git ALL //从特定git仓库取出变更并合并。
$git reset –hard ORIG_HEAD //恢复到上一次的内容
$git gc //用垃圾回收机制清除由于reset而造成的垃圾代码
$git fetch –tags //从origin取出tags并存储到.git/refs/tags
两点提交状态时的说明:
很明显可以知道:
Changes to be committed表示已经存在于index file 里,但尚未提交。
Changed but not updated表示在working tree已经做修改,但还没有使用git add登记到index file 里。
git branch branchname 就是根据commit来建立的新分支。
ps:如果你学有余力,我再告诉你一个信息。在你git branch一个新分支后,在目录.git/refs/heads目录下会多出一个新的文件,对应于新分支的名称,用来记录新分支下对应的“最后一次commit的信息”。
ps:如果你学有余力,我还要告诉你一个信息。当你git branch一个新分支并checkout转移到这个新分支后,.git目录下的HEAD文件会相应的改变,它的内容将对应着新分支下“最后一次commit的信息”。