git综合

看了git突然就感觉这些人真滴蛮厉害,整天就是完善这类代码,真的已经做了大量的工作。和绣花业没有什么区别。


Git的功能特性:

从一般开发者的角度来看git有以下功能:

1:从服务器上克隆数据库(包括代码和版本信息)到单机上。

2:在自己的机器上创建分支,修改代码。

3:在单机上自己创建的分支上提交代码。

4:在单机上合并分支。

5:新建一个分支,把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。

6:生成补丁(patch),把补丁发送给主开发者。

7:看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。

8:一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。


git工作原理


  • 工作区(Working Directory):仓库文件夹里除.Git目录以外的内容。

  • 版本库(Repository):.git目录,用于存储记录版本信息。

  • 暂缓区(stage):也叫暂存区。

  • 分支(master):.git自动创建的第一个分支。

  • HEAD指针:用于指向当前分支。

  • git add 和 git commit 原理:

  • git add:把文件修改或者新添加文件添加到暂缓区(暂存区)。
  • git commit:把暂缓区(暂存区)中的所有内容提交到当前分支。

git大概运作流程.gif

什么是分支?


  • git 可以创建无数个分支,且在创建一个 git仓库 的同时会自动创建第一个分支。

  • 分支的作用:举个栗子 ==> 一个已经上架的项目,发现有重要Bug,为了不影响主分支的开发进度,一般会开个分支出来专门修复 Bug用,等到Bug修复后,再与主分支进行合并操作,这样在不影响主分支的开发进度的同时,可以进行 Bug 的修复。

HEAD头指针运作原理


  • HEAD指针 就是为了在各个分支间进行切换,我们开发哪个分支,它就会指向哪个分支

git中HEAD原理.gif

git初始化和访问配置(命令行方式)

  • 初始化一个本地版本库
git init

 
 
  • 1
  • 2
  • 1
  • 2
  • 设置用户名和邮箱
git config user.name "用户名"
git config user.email "xxxxxx@qq.com"
 
 
  • 1
  • 2
  • 1
  • 2
  • 设置全局用户名和邮箱(优先级较高,如果同时设置,优先使用全局)
git config --global user.name "用户名"
git config --global user.email "xxxxxx@qq.com"
 
 
  • 1
  • 2
  • 1
  • 2
  • git 仓库项目初始化操作

  • 在 git工作区(同级目录和同级目录的子目录)中创建一个文件 main.m

touch main.m
 
 
  • 1
  • 1
  • 查看文件状态(查看文件是否被管理)
git status
 
 
  • 1
  • 1

译注:

  • 如果文件的颜色为红色,说明该文件并没有被添加到git的暂缓区中
  • 如果文件颜色为绿色,说明该文件已经被添加到git的暂缓区中
  • 添加文件到暂缓区中,查看文件状态
git add main.m
git status
 
 
  • 1
  • 2
  • 1
  • 2
  • 提交到本地版本库中
git commit -m "创建了main.m文件" main.m
 
 
  • 1
  • 1

译注:

  • 可以 直接 在后面跟上文件,表示把该文件提交到本地版本库(不推荐
  • 不跟任何文件名称,表示把当前所有暂缓区中的内容都提交到 本地版本库 中
  • 修改文件,重新提交更改到本地版本库(git中如果修改了文件,需要重新将文件添加到暂缓区中才能提交)
git add main.m
git commit -m "修改了main文件" main.m
 
 
  • 1
  • 2
  • 1
  • 2

注意: git 中和 svn 不一样的地方在于 —— 不管是创建文件还是修改文件都需要添加到暂缓区中然后再提交。

git 好用的功能 —— 别名


  • git 流行起来还有个原因,就是它贴心的功能 —— 别名,它支持我们对某些命令进行 别名 设置,让使用者使用起来更加顺手,更加效率,这边我们通过下面的例子来看看怎么给 git 起别名:

  • 我们来给status起别名 st

  • 基本方式:(只能在当前文件夹内的git工作区内使用)

git config alias.st "status"
 
 
  • 1
  • 1
  • 这样以后要查询文件状态就可以直接使用
git st
 
 
  • 1
  • 1
  • 高级方式:(只能在当前文件夹内的git工作区内使用)
// git commit -m "注释" 
git config alias.ci "git commit -m"
 
 
  • 1
  • 2
  • 1
  • 2
  • 这样以后要提交文件就可以直接使用
git ci "注释"
 
 
  • 1
  • 1
  • 你以为只能是 局部的别名?错!它还支持 全局的别名,是不是感动到泪奔,怎么可以这么贴心,赶紧来看一下示例:

  • 全局别名:(设置一次,电脑中所有的地方都可以使用)

git config --global alias.st "status"
 
 
  • 1
  • 1

译注:全局别名和局部别名的使用区别就在于有没有跟上 –global,所以其他方式就不再赘述!

git删除文件


  • 作为一款主流的 版本控制,怎么可以少了删除功能,马上来看下面示例:

  • 先创建一个新的文件:

touch test.m
 
 
  • 1
  • 1
  • 将文件提交到暂缓区中:
git add test.m
 
 
  • 1
  • 1
  • 提交删除操作到本地版本库:
git commit -m "删除了test.m"文件
 
 
  • 1
  • 1
  • 删除test.m文件
git rm test.m
 
 
  • 1
  • 1

查看 git 版本信息


  • 版本信息也是重要的一部分,一定要会,知道么!告诉我,你知道。

  • 首先肯定要进到我们需要查看的 git 工程目录下,接着 git 给我们提供了下面2种方式查看版本信息:

  • 基础方式:

git log
 
 
  • 1
  • 1
  • 高级方式:(可查看版本回退操作)
git reflog
 
 
  • 1
  • 1
  • 因为 git 是分布式版本控制,有 多个git本地版本库,为了使版本号之间不互相冲突,所以 git版本号是通过SHA1加密得到的密文

git分布式版本控制示意图.png

版本回退操作


  • 开发中,经常会发生我们或者其他开发人员因修改不当造成 软件运行不正常等情况,这时候就需要用到版本回退功能,将代码退回到之前正常运行的版本,再继续进行开发,根据情况不同,git 提供了下面 2 种方式:

  • 修改了本地版本库文件,但还未提交

git reset --hard HEAD
 
 
  • 1
  • 1
  • 修改了本地版本库文件,且已经提交(这种情况只能回退到正常版本了),git 提供 3 种方式:
  • 方式一(推荐,但如果需要回退多个版本时不推荐):
//  一个“^”号表示一个版本,下面表示退回到上个版本
// 上上个版本:^^,以此类推
git reset --hard HEAD^
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 方式二(不推荐,连续回退多个版本):
//  ~号后面表示回退几个版本(如10就是回退10个版本)
git reset --hard HEAD~1                    ```
- 方式三(推荐,回退到指定版本):

```git
// 查看版本信息,看需要回退到哪个版本
git reflog
// 指定回退的版本号
// 复制需要的版本号,然后跟上版本号即可
git reset --hard 版本号(5位)




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

创建共享版本库并初始化项目


  • 创建共享版本库方式:

  • 自己搭建服务器,自己在服务器上配置(复杂)。

  • 在U旁上创建共享版本库。

  • 把共享版本库创建到文件夹中。

  • 托管到github、oschina网站:

  • 初始化一个空的git共享版本库(和本地版本库不同的是,共享版本库不会生成.git文件夹)

git init --bare




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 管理者连接仓库并下载仓库到本地
git clone 共享版本库路径




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 初始化项目

  • 在初始化项目前,我们需要先忽略一些不必要的文件

  • 创建一个忽略文件.gitignore

cd git工程路径
touch .gitignore




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 到github上搜索gitignore并拷贝Objective-OC文件中的内容到我自己创建的忽略文件内保存(文件中里面包含了我们需要忽略的文件配置),也可以自己自定义,语法在下面会提到!

  • 添加并提交忽略文件到本地版本库

git add .gitignore
git commit -m "新建.gitignore忽略文件" .gitignore




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 创建我们的Xcode工程

  • 项目保存路径选择我们本地的版本库(这是下面的 git选项就会变灰不可点击,这是正常的,因为我们当前的目录下就有了 git 仓库)

  • 提交到本地版本库:创建完成后点击XCode上面的Source Control –> Commit –> 添加注释 –> 提交

  • 提交到远程版本库(共享版本库):Source Control –> Push –> 选择远程版本库 –> Push

译注:

  • 怎么查看使用的版本库是共享版本库还是本地版本库,我们可以打开git 中的config配置文件查看里面的“bare”字段,如果显示为true表示为共享版本库。

.gitignore 语法


  • 上面提到了 .gitignore 文件,那它到底是什么东西呢?—— .gitignore 是 git 中常见想配置文件,它里面包含了一些文件规则(如:忽略哪些文件等等)这边就来说说其语法:

  • 这边先介绍下怎么配置 .gitignore

  • /:表示目录
  • *:为通配多个字符
  • ?:通配单个字符
  • []:包含单个字符的匹配列表
  • !:表示不忽略匹配到的文件和目录
// 示例
// 忽略ios文件夹下的所有内容
/ios/*

// 忽略所有文件,除ios文件夹和.gitignore文件
/*
!.gitignore
!/ios/





<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 知道了 gitignore 语法,我们就可以轻松控制文件的提交方式,感兴趣的可以试一下。

XCode 使用 git(添加第三方框架)


  • 开发中我们或多或少会使用到第三方框架,通常框架会使用 cocoapods 进管理,但偶尔会有公司与公司之间内部使用的框架,而我们将框架拖入工程的时候会发现git无法识别的情况,这个原因是因为我们拖入的是整个文件夹,如果我们直接拖入的文件不是包含在文件夹下,那么就可以识别。

  • git中内部第三方框架(静态库)的处理方式:

  • 方式一:直接创建和框架相对应的文件夹,然后将框架文件夹内的文件拖入到工程中

  • 方式二:直接右击需要添加的文件 –> source control –> add

  • 方式三:点击需要添加文件所属的文件夹 –> Source Control –> Add “xxxx”

  • 方式四(命令行):

cd 文件所在路径
git add .




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Xcode 回退操作


  • 在 XCode 中,如果还没提交到远程版本库中,我们要进行回退操作很简单:Source Control –> Discard All Changes(取消所有的更改)就可以了 。

Xcode 多人开发冲突解决


  • 在多人开发中,很容易出现版本之间的冲突问题,一般我们都是先提交代码到本地仓库,然后一段时间提交一次到远程版本库中,这段时间可能别的开发者已经提交过文件,并且修改了和你同一文件的同一行,那么此时要提交就会报错。

  • 处理方式:

  • Source Control –> Pull –> 修改冲突的代码 —-> 确定
  • Source Control –> push –> 提交

创建新成员的共享版本库


  • 有时候开发过程中会出现新的团队成员,而考虑到新成员和老成员使用git方式不一样或者不熟悉,为了保证代码的安全性,会新建一个专门给新成员的共享版本库,以供观察!

  • 创建新成员共享版本库(步骤):

  • 在远程共享版本库中新建一个新成员文件夹

  • 打开远程共享版本库

cd 远程共享版本库中新成员文件夹的路径




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 初始化新的共享版本库
git init --bare




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 为确保拷贝到新成员共享版本库中的工程为最新版,操作之前先pull一次工程

  • Source Control –> 鼠标移至工程分支选项 –> 选择弹出菜单中的Configure … –> 在弹出窗口中选择 –> Remotes(远程) –> 点击左下角”+” –> Add Remote… –> name:分支标记 Address:需要添加的仓库地址 –> Done –> Source Control –> Push –> 选择新成员分支 –> push

  • 到此,新成员拿到仓库地址,进行下面步骤:

  • 打开本地仓库

cd 本地仓库路径




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 下载远程仓库中的工程
git clone 远程仓库路径




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 如果要检查新成员的工作成果,那么也需要下载新人共享版本库的工程:

  • 先在本地创建一个存储目录

  • 然后打开该目录

cd 本地存储目录路径




<div class="se-preview-section-delimiter"></div>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 下载工程
git clone 新人共享版本库的路径
 
 
  • 1
  • 1

Xcode 查看提交日志


  • Source Control –> History…
  • 在日志中可以点击对应日志中右边的 show 查看修改的情况

将工程托管到 github 或 osChina 平台


github篇

  • 使用github有个比较不好的地方 – 要想免费必须开源,如果不开源只能付费使用

  • 肯定要有账号(步骤就不演示了)

  • 创建远程git版本库

  • 点击右上角 “+” 号 –> New repository

  • repository name(一般为项目名称) –> Description(项目描述) –> 开源选择“Public”(免费)不开源选择”Private”(收费,7美元/1个月) –> 选择是否生成README说明文档 –> 忽略文件选择(选择Objective-OC)–> 选择协议(一般选择 Apache License 2.0协议)–> 创建

  • 连接远程版本库配置

  • 打开XCode –> XCode –> Preferences –> Accounts –> 左下角 “+” –> Add Repository… –> Address:远程版本库的HTTPS地址 Type:管理工具类型 Authentication:认证方式 User Name:用户名 Password:密码 –> Add

  • 连接远程版本库:

  • Source Control –> Chack Out –> 在弹出的窗口中找到我们需要的版本库并选择 –> Next –> 在弹出窗口中选择需要存储的位置 。

  • 创建工程:将项目添加到版本库的目录下 –> Source Control –> Commit –> 提交。

  • 将本地版本库的工程提交到远程版本库:Source Control –> Push –> 提交。

oschina篇:这边就不写了,因为oschina可以看成是github的中文版,排版和操作都差不多,但是他比较好的一点就是全免费,而且服务器在国内,速度比较稳定且快,所以推荐使用。

github 删除代码仓库


  • 登录github –> 找到仓库 –> Settings –> 最下面有个Delete this repository – 输入需要删除的仓库名 – 提交。

将项目托管到 github 等平台 命令行 方式


  • 这边我们在来说说命令行的方式将项目托管到 github 平台。

    • 首先,电脑需要有 git 客户端,否则会提示找不到命令等提示,(mac自带)

    • 其实方式有很多,这边我比较习惯下面的步骤做:

      • 在 github 上创建仓库

      • 复制 仓库 网址

      • 打开终端,输入:

          git clone 远程仓库网址
           
           
      • 1
      • 1
      • 将下载下来的项目文件夹中所有的文件以及文件夹拷贝到需要托管的项目根目录

      • 进入 需要托管的项目根目录,添加所有文件为管理

          cd 项目根目录地址
          git add .
           
           
      • 1
      • 2
      • 1
      • 2
      • 提交到本地仓库
          git commit -m "注释"
           
           
      • 1
      • 1
      • 提交到远程仓库
          git push -u origin master
           
           
      • 1
      • 1
      • 输入 github 账号和密码,等待提交完成即可。

      • 要确定是否成功提交,登陆github查看下就知道了!

背景

Git 是一个开源的分布式版本控制软件。在英式英语中,git 指一个愚笨或者不开心的人,恐怕与 Git 发明人——Linux 教父 Linus Torvalds 当时的自嘲心理不无关系吧。2002 年之前,linux 内核维护工作的绝大部分时间都浪费在提交补丁与保存归档等繁琐事务上。启用版本控制工具 BitKeeper 管理 Linux 内核成了当务之急。不过,BitKeeper 毕竟是一款商业软件,在经历了 3 年免费使用之后,Linux 社区不得不寻求它的替代品,以便继续托管 Linux 内核源代码。2005 年,迫于无奈,Linus Torvalds 自行开发了一套开源版本控制工具,并命名为 Git。

自诞生以来,Git 就以其开源、简单、快捷、分布式、高效等特点,应付了类似 Linux 内核源代码等各种复杂的项目开发需求。如今,Git 已经非常成熟,被广泛接受与使用,越来越多的项目都迁移到 Git 仓库中进行管理。以 Eclipse 社区为例。据称,目前 80% 的 Eclipse 基金会项目已经完全使用 Git 管理,CVS 访问权限已经切换成只读状态。并且,在 Eclipse 基金会官网中,针对项目管理的介绍中已将"CVS"三个字符划掉,而且很萌地写道,"Ding dong, the witch is dead.",意思是"叮咚,那个老巫婆已经挂了"。

不仅如此,笔者最近也收到了全球最大开源代码托管平台——SourceForge 的升级通知。其中,笔者的一个较为简单的项目已经从 CVS 被系统默认自动升级到了 Git。而对于另一个较为复杂的 CVS 项目Toolbox for Java/JTOpen,SourceForge 并没有自动升级,估计是等待笔者做升级前的最后准备工作。笔者希望通过分享自己的 Git 学习体验与实践经验,对 Git 初学者有所裨益,这也是本文之意义所在。

为什么选择 Git

实际上,相对于 CVS、SVN 等主流版本控制软件,Git 的学习成本甚至会更高。比如,对于 Subversion 用户而言,如果能理解什么是文件、工作目录、资源库、版本、分支和标签等概念,差不多就够用了。而对于 Git 用户,需要理解更多更复杂的概念,包括文件、快照、工作树、索引、本地资源库、远程资源库、远程、提交、分支和 Stash 等。那么,为什么软件开发者对 Git 还是趋之若鹜呢?相比于 CVS 与 SVN,Git 的优势到底体现在哪里?

关于 Git 的各种优势,互联网以及各种 Git 书籍都给出了自己的答案。笔者认为,存储快照与分布式的设计思想是 Git 的 2 大看点,理由如下:

第一,Git 底层自行维护的存储文件系统是一大亮点。CVS、SVN 底层采用的为增量式文件系统,如图 1 所示。增量式文件系统的特点是:当文件变动发生提交时,该文件系统存储的是文件的差异信息。

图 1. CVS、SVN 记录文件内容差异
图 1. CVS、SVN 记录文件内容差异

同样是文件变更提交,Git 底层文件系统存储的则为文件快照,即整个文件内容,并保存指向快照的索引,如图 2 所示。考虑到性能因素,如果文件内容没有发生任何变化,该文件系统则不会重复保存文件,只是简单地保存文件的链接。

图 2. Git 记录整个文件快照
图 2. Git 记录整个文件快照

Git 之所以选择这样的底层存储数据结构,主要是为了提高 Git 分支的使用效率。实际上,Git 分支本质上是一个指向索引对象的可变指针,而每一个索引对象又指向文件快照,如图 3 所示。

图 3. Git 分支对应的数据结构
图 3. Git 分支对应的数据结构

这样一来,创建分支可以瞬间完成,几乎不需要花费太多代价。换句话说,Git 分支是廉价的、轻量级的。我们看看各种 CVS、SVN 项目,分支通常意味着源代码的完整拷贝,其代价是昂贵的、重量级的。而对于大型项目来说,创建各种分支又是十分必要的,这与 Git 鼓励频繁创建与合并分支的理念相吻合。

第二,Git 版本控制系统的设计思想是"去中心化"。传统的 CVS 、SVN 等工具采用的是 C/S 架构,只有一个中心代码仓库,位于服务器端。而一旦由于服务器系统宕机、网络不通等各种原因造成中心仓库不可用,整个 CVS 、SVN 系统的代码检入与检出就瘫痪了。即便考虑到高可用性,通过迁移另一个中心仓库继续代码提交操作,相应的运营维护成本也会随之上升。

为了摆脱对中心仓库的依赖,Git 的初始设计目标之一就是分布式控制管理。我们给出一个样例,如图 4 所示。假如我们成立一个项目组,开发者主要由 Alice、Bob、Clair、David 四名成员组成。其中,除了中心仓库 origin(Git 默认远程仓库名称)之外,每一名成员各自负责一个本地仓库。从分布式的观点来看,David 可看成是 Alice 的远程仓库,反过来也是一样。Git 分布式的设计理念有助于减少对中心仓库的依赖,从而有效降低中心仓库的负载,改善代码提交的灵活性。

图 4. Git 分布式工作示意图
图 4. Git 分布式工作示意图

Git 分布式设计思想所带来的另外一大好处是支持离线工作。离线工作的好处不言而喻,对于 CVS、SVN 这种严重依赖网络的 C/S 工具而言,没有了网络或者 VPN ,就意味着失去了左膀右臂,代码检入与检出操作就无法正常进行。而一旦使用 Git ,即便在没有 WIFI 的飞机或者火车上,照样可以频繁地提交代码,只不过先提交到本地仓库,等到了网络连通的时候,再上传到远程的镜像仓库。

有关 Git 更多详细信息,请参考 Git 官方网站:http://git-scm.com/‎。

工欲善其事,必先利其器。在理解 Git 灵活的快照存储与分布式设计理念之后,我们介绍 Git 针对不同操作系统的安装过程。需要指出的是,这里仅仅粗线条地介绍 Git 的安装方法,至于 Git 安装前提条件、安装过程出现的问题诊断等更加详细的内容描述,均不在本文的讨论范围。

如何安装 Git

总结起来,Git 安装方式通常分为两种:一种是选择 Git 源码编译安装;另一种使用针对特定平台的二进制安装包,又可以细分为 Linux、Mac、Windows 等,其安装说明如下。

1. 源码编译安装

从 Git 源码安装至少可以保证版本是最新的。在安装 Git 之前,需要安装其依赖的软件包,包括 curl、zlib、openssl、expat、libiconv 等。根据不同类型的 Linux,读者可以选择不同的软件包安装工具,这里以 yum 为例,其安装命令如下:

$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel

接下来,读者可以从 Git 官方站点 http://git-scm.com/download 下载最新 Git 源代码(由于时间的差异,笔者无法保证本文所述 Git 为最新版本),执行以下命令编译安装。

$ tar -zxf git-1.7.6.tar.gz
$ cd git-1.7.6
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install

最后,敲入 git 命令,检验安装是否成功,如图 5 所示。可以看到,我们已经成功安装 Git 了。

图 5. 通过源码安装 Git
图 5. 通过源码安装 Git

2. 在 Linux 上安装

要在 Linux 上安装预编译好的 Git 二进制安装包,可选择系统支持的软件包管理器。对于红帽 Linux,使用 yum 命令安装:

$ yum install git-core

而对于 Ubuntu 这类 Debian 体系的 Linux 系统,使用 apt-get 命令安装:

$ apt-get install git-core

由于此种安装方式非常简单,这里不做贴图展示了。

3. 在 Mac 上安装

Mac 系统支持 Git 安装的方式分为两种:编译安装与图形安装。其命令行式的编译安装与 Linux 大同小异,这里不再介绍。相比之下,Mac 图形安装 Git 更加简单,其安装截图如图 6 所示。读者可去 http://code.google.com/p/git-osx-installer 下载最新支持 Mac 系统的 Git 版本。

图 6. 从 Mac 上安装 Git
图 6. 从 Mac 上安装 Git

4. 在 Windows 上安装

与之前所述的 Mac 安装 Git 一样,在 Windows 上安装 Git 也同样轻松。根据用户的使用习惯,我们又可以大致分为三类:

习惯命令行的用户,可选择 msysGit 安装包,安装截图如 7 所示。msysGit 的官方下载地址为:http://code.google.com/p/msysgit

图 7. 从 Windows 上安装命令行 Git 工具——msysGit
图 7. 从 Windows 上安装命令行 Git 工具——msysGit

对于习惯 Tortoise 风格的用户,可以选择 TortoiseGit 安装包,安装后的右键截图如图 8 所示。TortoiseGit 的下载地址为:http://code.google.com/p/tortoisegit/。

图 8. 从 Windows 上安装“右键”Git 工具——TortoiseGit
图 8. 从 Windows 上安装“右键”Git 工具——TortoiseGit

而对于习惯 Eclipse 风格的用户,可以选择 Eclipse 插件——EGit 方式安装,其 Git Repositories 视图截图如图 9 所示。EGit 的下载地址为:http://download.eclipse.org/egit/updates。

图 9. 从 Windows 上安装 Eclipse 的 Git 插件——EGit
图 9. 从 Windows 上安装 Eclipse 的 Git 插件——EGit

无论是哪一种安装方式,如果是第一次使用 Git,均需要配置用户信息,包括用户名与 Email(如下所示),以便以后每次 Git 提交时都可以自动引用这两条信息,说明是谁更新与提交了代码。

$ git config --global user.name "Pi Guang Ming"
$ git config --global user.email piguangming@gmail.com

到此为止,我们已经介绍了 Git 的分布式模型、快照模型、针对不同操作系统平台的 Git 安装之后,接下来是本文的主题内容,即 Git 的使用。

如何使用 Git

前面提到,这一部分是本文的重点。我们将主要精力集中在 Git 底层的工作原理、以及实际工程中较为实用的 Git 分支、标签、补丁、CVS 与 SVN 针对 Git 的迁移等,关于 Git 的各种基础命令语法、解释说明,以及本文没有涉及到的内容,均可参见 Git 相关使用指南。

创建 Git 项目仓库

在正式使用 Git 之前,我们至少需要创建一个 Git 代码仓库(简称 Git 仓库)。通常而言,取得一个 Git 仓库的方法有两种。第一种是在现存的目录下,通过导入所有文件来创建新的 Git 仓库;第二种是从远程 Git 镜像仓库直接克隆到本地仓库。

针对第一类 Git 仓库,我们可以使用 git init 命令创建一个崭新的 Git 项目仓库,如下:

$ git init

初始化 Git 后,在当前目录下会出现一个名为 .git 的隐藏目录,如图 10 所示。

图 10. .git 目录
图 10. .git 目录

之所以特意强调 .git 目录,是因为它十分重要。对于一个 Git 仓库来说,其 .git 目录保存了整个 Git 项目的所有数据与资源。关于 .git 目录中各种文件的简要解释说明,如表 1 所示。如果需要了解详细信息,请参见 Git 官方网站:http://git-scm.com/‎。

表 1 .git 目录简要说明
子目录名 简要描述
branches Git 项目分支信息,新版 Git 已经不再使用该目录。
config Git 项目配置信息
description Git 项目描述信息
HEAD 指向 Git 项目当前分支的头指针
hooks 默认的"hooks"脚本,被特定事件发生前后触发。
info 里面含一个 exclude 文件,指 Git 项目要忽略的文件。
objects Git 的数据对象,包括:commits, trees, blobs, tags。
refs 指向所有 Git 项目分支的指针

针对第二类 Git 仓库,我们不需要 git init 初始化仓库,取而代之的是,使用 git clone 直接将远程镜像克隆到本地仓库。这里,我们以下载 Git 软件本身的源代码为例,其 git clone 命令如下:

git clone git://git.kernel.org/pub/scm/git/git.git

通过 ls git 命令,我们可以查看 Git 仓库中的内容,如图 11 所示。需要说明的是,针对远程仓库的镜像,实际拷贝的就是 .git 目录下的数据,然后根据元数据恢复成原来的整个项目结构,也即是图 11 所示的内容。

图 11. 克隆 Git 源代码
图 11. 克隆 Git 源代码

此外,除了 git:// 协议,针对不同的使用场景,git clone 还支持 ssh://、http(s):// 等各种不同协议。

Git 对象模型

应该说,Git 对象模型是整个 Git 设计思想中最核心的部分。理解 Git 对象模型是理解整个 Git 的关键。简单来说,每个 Git 对象包含三部分:类型,大小和内容。其中,对象的类型又分为 commits, trees, blobs, tags,其简要说明如下:

  • blob 对象:一块二进制数据,用来存储文件数据,通常是一个文件。
  • tree 对象:指向 blob 对象或是其它 tree 对象的指针,一般用来表示内容之间的目录层次关系。
  • commit 对象:一个 commit 对象只指向一个 tree 对象,用来标记项目某一个特定时间点的状态,如时间戳、父对象、作者、提交者等。
  • tag 对象:与 CVS、SVN 标签的概念类似。

接下来,我们结合一个示例来解释不同 Git 对象之间的关系。图 12 展示的是一个样例 Ruby 项目,可以看出,这个例子非常简单,仅作示意。

图 12. Ruby 项目的目录层次结构
图 12. Ruby 项目的目录层次结构

如果我们把该项目提交到 Git 仓库中,那么它的 Git 对象关系就如图 13 所示。其中,3 个 blob 对象分别对应 README、mylib.rb、yourlib.rb 三个文件的内容快照。而 3 个 tree 对象指针则完整描述了项目的整个目录结构,包括目录树内容、文件与 blob 对象的对应关系,各个文件对应 blob 对象索引等信息。而每一次提交都会生成一个 commit 对象指针,指向 tree 对象树的根节点,不仅如此,commit 对象还包含作者、提交人等详细信息。

图 13. Ruby 项目的 Git 对象关系图
图 13. Ruby 项目的 Git 对象关系图

不难看出,众多 tree 对象与 blob 一起,作为内容节点(目录或文件),构成了一个有向无环图。在任何时候,通过与 commit 对象关联的根节点,就可以遍历出整个项目在本次提交时的所有内容。而前面提到,Git 分支本质上是指向 commit 对象的指针。两个 Git 分支的合并,实质上是等价于两个有向无环图的合并,而有向无环图可以让 Git 更加高效判断分支共同的父节点。因此,Git 对象模型设计赋予了开发人员最大的灵活性来任意创建分支,并在自己的分支上进行开发。

尽管以上几种对象的类型不同,每一种对象都拥有同一长度的唯一标识,以 40 位字符串表示。实际上,图 13 的对象标识均为简写,其中,commit 对象完整的标识如下 :

98ca9e0acb0be0321191a59e1d34ba5c867fa3

为保证对象标识的唯一性,Git 采用了 SHA1 哈希算法。这样做,起码有三大好处:

  • Git 只要比较对象名,就可以很快的判断两个对象是否相同。
  • 由于每个仓库中"对象名"的计算方法都完全一样,因此,如果同样的内容存在两个不同的仓库中,就会存在相同的"对象名"下。
  • Git 还可以通过检查对象内容的 SHA1 哈希值与"对象名"是否相同,来判断对象内容是否正确。

总结一下 Git 对象模型,blob 对象即项目中的所有实体文件,包括源代码、图片资源、xml 配置信息等内容。特别需要强调的是,blob 对象记录的仅仅是文件内容,而关于文件所在目录、名字大小等信息,则统统记录在关联它的 tree 对象上。我们每次提交文件,都会产生一个 commit 对象,并更新改动文件所关联的 tree 对象。

Git 三种状态

在理解 Git 对象模型之后,我们的焦点转向 Git 文件的检入与检出。Git 仓库模型大致分为三个工作区域,分别为工作目录(Working Directory),暂存区域(Stage 或 Index),以及本地仓库(History),相应的检入与检出命令如图 14 所示:

图 14. Git 三种状态之间的转换(1)
图 14. Git 三种状态之间的转换(1)

相关命令的简要说明如下:

  • git add files:把当前工作文件拷贝到暂存区域。
  • git commit:在暂存区域生成文件快照并提交到本地仓库。
  • git reset -- files:用来撤销最后一次 git add files,也可以用 git reset 撤销所有暂存区域文件。
  • git checkout -- files:把文件从暂存区域覆盖到工作目录,用来丢弃本地修改。

作为示例,图 15 演示了如何通过 git add 与 git checkout 分别在工作目录与暂存区之间来回复制,读者可以自行尝试 git commit 与 git reset 命令。首先,我们在工作目录创建一个内容为"hello git"的 test 文件,通过 git add 命令将 test 文件复制到暂存区。然后,在工作目录修改 test 文件,添加一行"hello git branch"。此时,暂存区的内容依然为"hello git",没有改变。最后,通过 git checkout 将暂存区的 test 文件覆盖工作目录,即放弃了本地修改,最终文件内容为"hello git"。

图 15. git checkout -- files 示例
图 15. git checkout -- files 示例

实际上,工作目录与仓库之间的复制也可以一步到位,如图 16 所示。

图 16. Git 三种状态之间的转换(2)
图 16. Git 三种状态之间的转换(2)

其中,git commit -a 等价于 git add 与 git commit,即先把文件从工作目录复制到暂存区,然后再从暂存区复制到仓库中。git checkout HEAD -- files 的过程刚好相反,即回滚到最后一次提交。

为了查看工作目录,暂存区域,以及本地仓库的文件有哪些不同,可以使用 git diff 命令,如图 17 所示:

图 17. Git 三种状态之间的比较
图 17. Git 三种状态之间的比较

git diff 命令相关的简要说明如下:

  • git diff:查看尚未暂存的文件更新了哪些部分。
  • git diff --cached:查看已暂存文件和上次提交时的快照之间的差异。
  • git diff HEAD:查看未暂存文件与最新提交文件快照的区别。
  • git diff <index1> <index2>:查看不同快照之间的区别。

作为示例,图 18 演示了 git diff 的用法。可以看到,通过 git diff 比较,知道工作目录比暂存区多了一行"hello git tag";而通过 git diff HEAD 比较,知道工作目录又比仓库最新提交文件多了两行,分别是"hello git branch"与"hello git tag"。由此推断,暂存区比仓库多了一行"hello git branch",而这恰好与 git diff –cached 的结论相吻合。

图 18. Git 三种状态之间的比较——示例
图 18. Git 三种状态之间的比较——示例

以上是关于 Git 检入与检出操作的基础用法,关于更详细命令以及语法说明,可参见相关 Git 学习指南。

接下来,我们介绍 Git 更加高级的功能与特性。

Git 分支模型

前面提到,Git 中的分支本质上是一个指向 commit 对象的可变指针。Git 会维护一个默认分支——master。每一次提交之后,master 指针都会自动向前移动。而如果要创建一个新的分支,可以使用 git branch 命令:

$ git branch bugFix

这会在当前 commit 对象上新建一个分支指针,如图 19 所示。

图 19. 新建分支 bugFix
图 19. 新建分支 bugFix

那么,Git 是如何知道当前在哪个分支上工作的呢?其实答案也很简单,它保存着一个名为 HEAD 的特别指针,它是一个指向当前工作分支的指针。我们可以将 HEAD 想象为当前分支的别名。在这一点上,它和 CVS、SVN 的 HEAD 概念大不相同。

运行 git branch 命令,仅仅是建立了一个新的分支,但不会自动切换到这个分支中去,所以在这个例子中,我们依然还在 master 分支里工作。要切换到其他分支,可以执行 git checkout 命令。

$ git checkout bugFix

这样 HEAD 就指向了 bugFix 分支,见图 20 所示。

图 20. 切换到 bugFix 分支
图 20. 切换到 bugFix 分支

实际上,我们可以将分支的创建与切换两步合二为一。要新建并切换到该分支,运行 git checkout 并加上 -b 参数:

$ git checkout -b bugFix

接下来,再提交一次:

$ vi test.rb
$ git commit -a -m 'update copyright'

图 21 展示了提交后的结果。非常有趣,现在 bugFix 分支向前移动了一格,而 master 分支仍然指向原先 git checkout 时所在的 commit 对象。

图 21. 在 bugFix 分支提交文件
图 21. 在 bugFix 分支提交文件

我们再切换到 master 分支:

$ git checkout master

其结构如图 22 所示。这条命令做了两件事。第一,它把 HEAD 指针移回到 master 分支;第二,把工作目录中的文件替换成了 master 分支所指向的快照内容。也就是说,从现在开始,基于该文件的一系列提交都将始于一个较老的版本。它的主要作用在于,可以将 bugFix 分支里作出的修改暂时取消,隔离 bugFix 分支对 master 分支的影响。在实际项目中,我们经常有这样的需求,即采用 developer 分支开发主要版本,bugFix 分支负责修复 bug,彼此互相隔离,最后合并。

图 22. 切换成 master 分支
图 22. 切换成 master 分支

我们作些修改后再次提交:

$ vi test.rb
$ git commit -a -m 'made other changes'

现在我们的项目提交历史产生了分叉,如图 23 所示,原因是刚才我们创建了一个分支,进行了一些工作,然后又切换到主分支进行了另一些工作。我们可以在不同分支里反复切换,并在时机成熟时将它们合并到一起。

图 23. 在 master 分支提交文件
图 23. 在 master 分支提交文件

git merge 命令把不同分支合并起来。合并前,HEAD 必须指向当前最新的提交。按使用场景不同,git merge 操作又分为三种情况:

  • 如果另一个分支是当前提交的祖父节点,那么 git merge 命令将什么也不做。
  • 反过来,如果当前提交是另一个分支的祖父节点,就导致 fast-forward 合并。指向只是简单的移动,并生成一个新的提交。
  • 否则就是一次真正的合并。默认把当前提交 (ed489 如下所示 ) 和另一个提交 (33104) 以及他们的共同祖父节点 (b325c) 进行一次三方合并。结果是先保存当前目录和索引,然后和父节点 33104 一起做一次新提交,如图 24 所示。
图 24. 合并分支
图 24. 合并分支

可以看到,git merge 命令把两个父分支合并进行一次提交,但提交历史不是线性的。相比之下,分支衍合命令 git rebase 在当前分支上重演另一个分支的历史,从而保证提交历史是线性的,如图 25 所示。

图 25. 衍合分支
图 25. 衍合分支

作为示例,我们演示关于 git merge 与 git rebase 的区别,见图 26 所示。

图 26. 合并分支 vs 衍合分支
图 26. 合并分支 vs 衍合分支

有时候合并操作并不会如此顺利,如果在不同的分支中都修改了同一个文件的同一部分,会造成合并冲突,Git 就无法干净地把两者合到一起。此时,Git 仅作合并,但不提交,它会停下来等人为地解决冲突,如下:

$ cat test.rb
init
master update1
master update2
bugFix update1
<<<<<<< HEAD
master updated3
=======
bugFix update2
>>>>>>> bugFix

要查看哪些文件在合并时发生冲突,可以使用 git status :

$ git status
# On branch master
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: test.rb
#
no changes added to commit (use "git add" and/or "git commit -a")

待修补发布以后,bugFix 分支已经完成了历史使命,我们可以使用 git branch 的 -d 选项执行删除操作:

$ git branch -d bugFix

以上,我们介绍了 Git 分支的创建,切换,合并(线性与非线性),冲突,以及删除。

Git 标签

与 CVS、SVN 等其它版本控制系统一样,Git 也支持打 Git 标签 。在程序开发到一个阶段后,我们需要打个标签,发布一个版本,如 0.1.2,v0.1.2 等。

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签实际上就是个指向特定提交对象的引用;而含附注标签实际上是存储在仓库中的一个独立 Git 对象。相比之下,含附注标签包含信息更多,包括自身校验信息,标签名字,Email,标签日期,以及标签说明等。含附注标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证,因此我们推荐使用含附注的标签,以便保留相关信息。

要打上标签,可执行以下 Git 命令:

$ git tag -a v0.1.2 -m "Release version 0.1.2"

相应地,要查看标签,执行下列 Git 命令:

$ git tag –l

当然,也可采用 git show 命令查看标签版本与提交对象等详细信息。

$ git show v0.1.2

删除标签的 Git 命令如下:

git tag -d v0.1.2

如果我们有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 -a 改为 -s,如下

$ git tag -s v0.1.2 -m "My signed 0.1.2 tag"

要验证已经签署的标签,可以先取到对应的公钥,然后使用 git tag –v 命令验证,如下:

$ git tag -v v0.1.2

需要注意的,默认情况下,git push 并不会把标签传送到远端仓库上。我们只能通过显式命令才能分享标签。其命令格式如下:

$ git push origin v0.1.2

如果希望一次性推送所有本地新增的标签,可以使用 --tags 选项:

$ git push origin --tags

如此一来,其他人克隆共享仓库或拉取数据同步后,也会看到这些标签。

Git 补丁

UNIX 世界中,补丁(Patch)的概念非常重要,几乎所有大型 UNIX 项目的普通贡献者,都是通过补丁来提交代码。对于 Linux 内核项目而言,普通开发者先从 Git 项目仓库克隆下代码,然后写入代码,做一个补丁,最后用 E-mail 发给 Linux 内核的维护者就可以了。

Git 提供了两种简单的补丁生成方案。一是使用 git diff 生成的标准补丁,二是使用 git format-patch 生成的 Git 专用补丁。这里,我们重点介绍第二种方式,关于第一种 git diff 方式,比较简单,这里不做介绍。

假设我们有一个项目 myproj,其工作目录里最初有一个文件 test,内容是"hello git",默认提交给 master 分支。这里,我们创建一个新分支 bugFix 用于代码修改,如图 27 所示:

图 27. 创建分支
图 27. 创建分支

接下来,我们在 test 文件里面追加一行"fix",并使用 git format-patch 生成一个 patch,如图 28 所示,其中,git format-patch 的 -M 选项表示这个 patch 要和哪个分支比对。

图 28. 生成补丁
图 28. 生成补丁

可以看到,补丁文件 0001-fix.patch 包含各种信息,不仅有 diff 的信息,还有提交者,时间等等。仔细一看你会发现,这是个 E-mail 的文件,可以直接发送。

接下来,可以使用 git am 来应用补丁,如图 29 所示。可以看到,相比于原来的 test 文件,打上补丁后,多了一行"fix"。

图 29. 应用补丁
图 29. 应用补丁

关于以上两种生成补丁方式的比较,很明显,相比于 git diff 生成的通用补丁,git format-patch 生成的 Git 专用补丁兼容性较弱。不过,Git 专用补丁中含有补丁开发者的名字,在应用补丁时,这个名字会被记录进版本库。因此,目前使用 Git 的开源社区往往建议大家使用 format-patch 生成补丁。

Git 远程仓库操作

前面提到,Git 是分布式版本控制系统。对于一个分布式节点来说,其它节点的 Git 仓库都可以作为本地仓库的远程仓库。要查看当前配置有哪些远程仓库,可以使用以下命令:

$ git remote

在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,Git 默认使用这个名字来标识你所克隆的原始仓库。

项目进行到一个阶段,要同别人分享目前的成果,可以使用 git push 命令将本地仓库中的数据推送到远程仓库。

$ git push origin master

而要将远程仓库抓取数据到本地,可以使用 git fetch 命令,从而获取所有本地仓库中还没有的数据。

$ git fetch [remote-name]

如果设置了某个分支用于跟踪某个远端仓库的分支,可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。从这个角度,git pull 等价于 git fetch + git merge 的功能。

$ git pull [remote-name]

关于以上几种 Git 远程仓库的相关操作,其关系见图 30 所示。要了解 Git 远程仓库的更多命令,如删除与重命名等,可参阅相关 Git 操作指南。

图 30. Git 远程仓库的操作
图 30. Git 远程仓库的操作

CVS 迁移到 Git

对于想要从 CVS 迁移到 Git 的用户,可以使用 git cvsimport 工具解决迁移问题,前提是安装相关工具 git-cvs 或 cvsps。

关于 git-cvs 工具,可以使用 yum 或者 apt-get 命令安装。以 yum 为例,其安装命令如下:

$ yum install git-cvs

如果是源码编译安装 Git,则需要安装 cvsps,下载地址:http://www.cobite.com/cvsps/

$ tar -zxvf cvsps-2.1.tar.gz
$ cd cvsps-2.1
$ make && make install

作为示例,我们新建一个目录 jt400.cvs,并将文章开头提到的 SourceForge 托管的 CVS 项目 Toolbox for Java/JTOpen 的源码导入到 Git 中来,操作过程如下:

$ mkdir jt400.cvs
$ cd jt400.cvs
$ export CVSROOT=:pserver:piguangming@jt400.cvs.sourceforge.net:/cvsroot/jt400
$ cvs login
$ git cvsimport -C src src

其中,-C src 是要往 git 仓库里创建的项目名称,最后那个 src 是 cvs 中要导入的模块。

SVN 迁移到 Git

同样,Git 也提供了 git svn 相关工具,提供 SVN 项目到 Git 的迁移,前提是安装相关工具 subversion-perl。

$ yum install install subversion-perl

作为示例,我们新建一个目录 photon-android.svn,并将 googlecode 托管的 SVN 项目 photon-android 导入到 Git 中来,操作过程如下:

$ mkdir photon-android.svn
$ cd photon-android.svn
$ git svn clone http://photon-android.googlecode.com/svn/

总结

本文系统性地介绍了分布式版本控制工具——Git,包括为什么使用 Git,Git 的安装,Git 的工作原理,Git 的使用方法,CVS 与 SVN 向 Git 迁移等。有关 Git 更全面的使用方法,请参见文档:https://github.com/progit/progit




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值