Git使用入门

1. 什么是Git

Git是一个强调速度的分布式版本控制软件和源代码管理系统(SCM,source code anagement),免费开源,遵从GNU v2协议。最初是由Linus Torvalds为内核开发而设计的管理软件。自从Git推出以来,已经被很多开源项目所采纳。每一个Git工作目录是一个带有完全历史记录和版本信息的仓库,不依赖于网络和中央服务器。

2. 为什么用Git

  • 更顺畅的工作流程,开发过程中,完全可以离线操作;
  • 快速,Git分布式架构使得本地仓库包含所有的历史版本信息,你可以在不同的版本之间快速切换;
  • 弹性的本地分支,在svn下,你建一个分支需要把源代码复制到另外一个文件夹,而在Git下,创建分支的代价是非常小的,只需一条命令;
  • 仓库目录结构简洁,用Git复制一个项目,只会在项目根目录创建一个.git的目录,而其他目录很干净;
  • 内容按元数据方式存储,所有的版本信息都位于.git目录下;
  • 完整性好,更易于协作开发;
  • 用户群大,现在已经有成千上万个开源项目采用Git来做项目管理,github上更是有无数个代码仓库;

3. 关于Git仓库

Git做为一个资源管理和跟踪系统,如果想要把自己的文件托管在Git上,那么首先你得让Git知道你需要管理的文件在哪。比如说现在我有一个项目,它在test文件夹里,我想让Git管理这个项目,这个时候你需进入到这个目录,然后运行“git init”命令。这个时候Git就会在该目录下生成一个.git的隐藏目录,Git用来进行版本控制和内容跟踪的所有文件都在该文件夹下。

处于Git跟踪下的文件只具有三种状态:

  • Modified(working directory):被修改过的文件
  • Staged(staging area):通过git add添加到暂存区域的文件
  • Committed(git directory):通过git commit提交到仓库的文件

所以,一般的Git工作流程可能是这样:修改过某些文件,然后把这些文件添加都暂缓区,再提交到仓库中形成一个版本或快照,最后提交到git服务器上。而在中间,可能伴随着分支管理,分支切换,撤消与合并。
可能有些人会觉得很奇怪,为什么git会有暂存区域这个概念,直接提交到仓库中不就OK了。其实这是git为了做版本控制用的,试想如果没有暂存区域,每修改一个文件,就会形成一个版本,太过频繁,不易于管理。暂存区域其实就是下一个版本的文件清单,你可以自由控制该往仓库中提交什么文件,这也可以避免在一个版本中包含一些中间文件,比如编译后的文件。

4. 操作流程

原理图如下:

+========+
|        |                                                           +---------+
|        |-----------------------------pull------------------------->|         |
|        |                                                           |         |
|        |                +==========+                               |         |
|        |                |          |                               |         |
| Rmote  |                |          |------------checkout---------->|Workspace|
|        |--fetch/clone-->|          |           +========+          |         |
|        |                |Repository|           |        |          |         |
|        |                |          |<--commit--| Index  |<---add---|         |
|        |<------push-----|          |           |        |          +---------+
+========+                +==========+           +========+

4.1 初始化仓库

初始化仓库有两种情况,一种是直接在一个空目录里建立一个项目,这时候你可以这样干:

git init

另一种是从其他机器复制一个仓库,比如这样:

$ git clone git://git2.kernel.org/pub/scm/git/git.git
$ git clone https://github.com/jquery/jquery.git
$ git clone git@github.com:wengpingbo/MicroBlog.git
$ git clone /home/oss/test.git

第一次从服务器上复制一个仓库,可能比较慢,因为git要把所有的历史记录和版本全部复制下来,这也算git的一个弊端吧!
复制完后,就会在当前目录下生成一个工作目录,名字以仓库名字命名。如果你不想指定目录,那就在上面的命令后加一个目录就ok了。
比如我想把test仓库放到oss仓库中:

$ git clone /home/oss/test.git oss

之后,你就可以开始你的工作啦!

4.2 添加文件

在编辑了几个文档之后,你可能突然想起来,好像文件还没有让git跟踪。Git并不会实时的跟踪你的文件,只在你明确让它记录你的文件时,它才会把指定的文件的当前状态记录到仓库中去,然后又撒手不管了。我想这就是说git笨的原因吧。这个时候,你需要手动添加你的文件当暂存区域:

$ git add filename1 filename2

如果你懒得一个一个加,你可以试试这个:

$ git add -A

它会把当前目录下所有的文件都添加到暂存区域。

4.3 添加一个版本

在添加完文件后,你可能觉得应该创建一个commit了。

$ git commit

怎么样?是不是有点不对劲,好像这个命令并没有按你想象的那样跳出一个提交成功的提示,而是直接跑到了你在配置中指定的编辑器中了。仔细看一下,原来是让你给这个版本做一些备注,随便写点什么,然后保存退出就ok了。如果你不想这么麻烦,可以这么干:

$ git commit -m "initial version"

可能你觉得之前讲的太罗嗦了,提交一个commit还这么麻烦,其实有一个捷径可以使你跳过添加文件这个过程:

$ git commit -a -m "initial version"

大功告成,这个命令会把之前所有的已经添加的文件都加入到这个版本中。
可能你又有疑问了,之前添加的文件不是自动会加入到下一个版本中吗,问什么还加这个-a参数?
其实git add命令只是把指定文件的当前状态添加到暂存区域,并不代表一个文件一旦添加,就会一直存在每个版本中。如果你添加一个文件后对这个又进行了修改,在你commit时候,只会commit这个文件添加时的状态,不会把之后的修改也commit进去,除非你再次添加。

4.4 推送变更

在你commit完之后,你可能想把自己的代码提交到github或者其他git服务器上,与他人交流共享,这时候就需要和远程服务器打交道了。
如果你是在本地建立起的仓库,默认情况下是没有任何服务器地址的,如果你是从其他服务器复制过来的仓库,这个服务器地址会自动添加到你的仓库中,你可以这样查看:

$ git remote -v

如果只输入 git remote ,就只会列出服务器端的别名,不会列出地址来。
一个仓库可以有多个服务器地址,这就意味着,你可以从不同的人手中复制同一个仓库,但这并不会打乱你自己的分支,哪怕双方的分支名字都一样。假如你现在在和另外两个人做同一个项目中的同一个分支,你发现A的一个模块正是你想要的,你想把他的代码合并到你现在的版本中,这时候你可以这样做:

# 添加对方的地址,code_a是别名
$ git remote add code_a git://url/test.git
# 复制对方的仓库到本地,但不合并,git pull会自动合并
$ git fetch code_a
# 把对方master分支合并到自己当前版本下
$ git merge code_a/master

合并完之后,你可能想提交你的代码到其他的服务器上,这时候你可以先把要提交的服务器地址添加进来,然后这样做:

$ git push origin master

上面的命令就是把自己master的分支提交到名字为origin的服务器上

4.5 创建并管理分支

在做项目的时候,你可能会想写一些扩展性的功能,或者做一些小实验,但是你又不想影响你现在的项目。这时候,你可以创建一个分支,然后在这个分支里写东西,当觉得不好的时候,你可以把这个分支删除掉,对你之前的主分支没有任何影响。或者你觉得这个新特性超出了自己的预想,可以合并到主分支里,这时候你只要把工作转回主分支,然后合并分支,最后删除分支,然后就跟那个分支没创建一样。具体操作如下:

# 创建一个test分支
$ git branch test
# 转到test分支
$ git checkout test
# 转到master分支
$ git checkout master
# 合并test分支
$ git merge test
# 创建test2分支,并转到test2分支
$ git checkout -b test2
# 删除test分支
$ git branch -d test
# 列出分支列表
$ git branch
# 列出分支列表和当前commit
$ git branch -v

Git merge 的实质是把两个版本合在一起,然后在当前分支创建一个新的commit,如果你在两个分支的同一个文件的同一个地方都做了修改,这时候merge就会失败,git就不会自动创建一个新的commit,而是直接停住。你需要手动修改这些冲突的文件,选择这两个分支中的一个版本,或者自己重写这个部分,然后手动添加这些文件到暂存区域,再commit一下就ok了。要查看哪些文件冲突了,可以用 git status 查看。

4.6 撤消改动

是人就会犯错。当你执行某个命令之后,突然发现,自己写错了,或者漏了一个文件,这时候怎么办?
如果你提交得太早,忘了添加某些文件,你可以这样做:

$ git commit -m "add something"
$ git add file1
$ git commit --amend

最后一个命令会把你当前暂存区域最为上一次的commit。如果你commit以后,马上 amend,这时候git会直接跳到编辑commit备注里面,这样你可以修改你上次commit的备注。
如果你添加了不该添加的文件,你可以这样挽回:

# 把所有的文件都添加进去
$ git add .
# 把readme文件从暂存区域去除
$ git reset HEAD readme

如果你发现你编辑错了一个文件,你想把它恢复到上一个版本的状态,这时候你可以这样:

# 只撤消这一个文件
$ git checkout -- filename1 

如果你觉得这个版本糟糕透了,想完全回滚到上一个版本,你可以干如下事情:

$ git reset --hard HEAD^

HEAD 是指向当前版本,^指当前版本的父版本,这个操作无法撤消。你可以把 --hard 换成 --soft,这只会回退commit信息。还有一个 --mixed 默认选项,大家可以参考官方文档,查看这3个选项的具体区别。

5. Git常用命令

  • git pull:从其他的版本库(既可以是远程的也可以是本地的)将代码更新到本地,例如:git pull origin master 就是将origin这个版本库的代码更新到本地的master主枝,该功能类似于SVN的update;
  • git add:是将当前更改或者新增的文件加入到Git的索引中,加入到Git的索引中就表示记入了版本历史中,这也是提交之前所需要执行的一步,例如 git add app/model/user.rb 就会增加 app/model/user.rb 文件到Git的索引中,该功能类似于SVN的add;
  • git rm:从当前的工作空间中和索引中删除文件,例如 git rm app/model/user.rb ,该功能类似于SVN的rm、del;
  • git commit:提交当前工作空间的修改内容,类似于 SVN 的 commit 命令,例如 git commit -m "change something"-m 后面为注释内容;
  • git push:将本地提交的代码更新到远程版本库中,例如 git push origin 就会将本地的代码更新到名为orgin的远程版本库中
  • git log:查看历史日志,该功能类似于SVN的log;
  • git revert:还原一个版本的修改,必须提供一个具体的Git版本号,例如 git revert bbaf6fb5060b4875b18ff9ff637ce118256d6f20,Git的版本号都是生成的一个哈希值;

 

发表评论