Git(本地)仓库实际就是项目文件夹下的.git目录。通常,人们都说添加文件到版本库分两步:先是保存到暂存区,随后的提交(commit)才算真正的保存到版本库。然而事实上,添加到git仓库的文件、目录都被当作对象用其SHA-1值作为文件名保存在.git/objects下,而暂存区(.git/index文件)只是保存了这些文件和文件夹的SHA-1值而已。提交的时候,会生成一个commit对象,新生成的commit对象包含了用暂存区的内容生成的一个tree对象的SHA-1值 、上一个commit对象的SHA-1值以及提交时的其他一些信息。一次一次的提交形成一个链式结构,称为分支(branch)。一开始只有一个默认的master分支(事实上,完成第一次提交后,master分支才真正创建出来),分支可以分叉,也可以合并,master分支或者别的分支其实只是一个指针,指向该分支的最近一次提交。HEAD也是一个指针,指向当前分支。所以,分支对于Git来说是非常轻量的,最主要的是commit对象。

基本的操作如图所示,当然,git的命令远远不止这些,这个图只是简单的表示了文件在不同存储区域的转化过程。其中,好像没有命令把文件从仓库提取到暂存区,其实并不是这样,checkout命令的另一种形式就可以填充暂存区,同时还有其他的命令也可以填充暂存区。况且,暂存区也是仓库的一部分,从仓库的这一部分取出来放到另一部分意义并不大,重要的是工作区里面的文件能够恢复成以前提交的版本。

使用git,理解分支的概念,熟悉分支的操作比较重要。哪怕是一个人使用git,但是如果要从不同的电脑上推送到远程仓库,也可能会不成功。因为git并不是简单的保存文件,而是将历次提交组织成了一条链。如果推送到服务器上的分支内容不能构成一条和本地分支一样的链,推送就不会成功。比如,已经在别的地方修改并推送过。这种情况下就需要将远程分支抓取(push)下来与本地分支合并后再推送。所以至少还要掌握简单的分支操作才能用好git。

以下是常用的git命令及用法:

一、仓库

1.git init [directory]

2.git clone

clone命令会抓取所有远程仓库的内容,包含远程仓库所有分支,自动生成一个叫origin的引用,同时自动生成本地master分支并跟踪远程master分支。可以用git remote show [origin]或git ls-remote [origin]来查看远程仓库包括远程分支的信息。

3.git remote add

添加远程仓库。这个命令实际上仅仅是为远程仓库添加了一个别名而已。

4.git fetch [remote-name]

从远程仓库抓取所有内容,包含远程仓库所有分支,但并不会自动合并任何分支到本地,不会改变工作目录和暂存区内容。

5.git pull [ ]

抓取远程分支内容并合并到当前分支,如果当前分支已经设置有跟踪分支则可以省略 参数。设置远程跟踪分支可以用git branch --set-upstream-to=origin/ 进行设置。

6.git push [ […​]]

推送内容到远程仓库,省略远程仓库名称,则默认为origin(可配置)。格式为:是任意提交的引用,甚至可以是SHA-1的前几个字符。还可以省略只保留“:”部分,这样的话表示将null推送到远程,即是删除远程分支。还可以加参数-f强制推送。强制推送的话,本地分支是什么样,远程分支就是什么样。连历史都一样,非常危险,不过可以用来删除一些不小心放到服务器上的文件。 不过,如果只是想删除文件的话,应该用git filter-branch命令。

二、配置

1.git config [–system|–global|–local] user.name “"

git config [–system –global –local] user.email “"

配置分为几个级别,默认是本地配置,配置文件以“节.名称”的形式保存变量,user.name、user.email是访问远程仓库所需要的。

三、暂存区

1.git add //添加文件到暂存区,可以用通配符;

2.git rm [-f] [–cached] //删除文件;带--cached则只删除暂存区中的文件,否则会同时删除工作目录下的文件,-f选项强制删除修改过并且添加到暂存区的文件。

3.git commit [–amend]//提交,–amend选项表示修订提交,亦即用这一次提交代替上一次提交,事实上修改了提交历史,如果已经推送到远程,则再次推送会出现问题。如果担心忘记提交,可以多次提交,使用–amend参数可以使多次提交也能保存提交历史很干净,当然,中间的版本是找不回来的,所以这个参数通常只是用来修改提交说明。

四、分支操作

1.git branch //创建分支。git中,分支只是一个指针,这个命令除了创建一个指针,什么也没干。

2.git checkout //切换到某一分支,这个命令会重置工作目录、暂存区内容与目标分支最后一次提交时的状态一致,方便于在目标分支上工作。因此在切换分支前,应该保持工作树是干净的。也就是说没有未暂存的修改,暂存区也没有待提交的内容,否则切换不能成功,当然,可以使用-f选项,但很显然使用-f是危险的。当然,如果是切换到新建分支又不一样,因为新建分支实际与HEAD一致,就不存在重置工作目录和暂存区的事,这样就很方便把已经修改甚至已经暂存的内容提交到新分支上。所以,可以用git checkout -b 直接新建并切换到新分支上。

3.git merge [...]

merge命令用于将几个提交合并到当前分支HEAD指针上,由于合并时的情况非常复杂,所以这个命令的使用也非常复杂,一般用来合并分支,例如:git merge develope。因为一个分支只不过是一个指向commit对象的指针,所以语法上没毛病。但是,合并时可能会有冲突,git会把冲突标记在源文件里,所以这时冲突的文件就处于modify状态,解决冲突后,将文件add到暂存区,然后运行git merge –continue继续合并,当然,也可以用git merge –abort中止合并。最理想的合并称为快进式合并,也就是待合并分支超前当前分支若干个提交,这种合并只是修改一下指针,恢复一下暂存区而已,不会有冲突发生。同样地,远程分支也只是一个指针,当采用git fetch抓取数据后就需要用git merge origin/branch进行合并,这样分步操作与直接用pull相比的好处就是,如果有冲突的话,可以手工解决冲突后继续合并,而pull则会直接失败。

4.git rebase

变基操作相当于是“录像”然后“回放”,它查找当前分支与目标分支的共同祖先,然后依次记录之后历次提交的变化,然后再目标分支的基础上依次重现,最后当前分支与目标分支将没有分叉,就可以执行快进式合并了。变基相当于改变了提交历史,所以,不应该针对已经推送到远程仓库的分支进行变基,而应该是先变基再推送,因为这样的话,别人就可以很愉快执行快进式合并了。

五、恢复数据

1.git checkout

checkout(检出)可以指定任意提交的指针。用于恢复到指定的版本,包含工作区、暂存区、分支指针以及HEAD指针,也就是所有的一切都恢复到指定的提交时的状态。如果带文件路径参数,那么分支指针、HEAD指针将不变,只是提取文件出来。而git checkout – 表示空提交,亦即是从暂存区提取文件。

2.git reset [] []

reset用于重置指针。mode主要有三种–soft、–mixed、–hard,–soft只是重置HEAD指针,–hard不但重置HEAD指针,还恢复暂存区和工作区,跟checkout功能差不多,但是checkout会检查是否存在修改的、暂存的文件,要安全得多。而–mixed则处于–soft和–hard的中间,重置HEAD指针和恢复暂存区,但是工作区不受影响,默认参数为–mixed。如果是带文件名的用法,则指针不动,但是会提取文件到暂存区,所以git reset HEAD 从最近的提交取出文件到暂存区,相当于恢复到git add 之前的状态,这时,工作区对文件的修改还在,只是处于未暂存的状态。