我们应该明确一点Git是一个分布式的版本控制系统,和SVN这种集中式的版本控制相比,它有很多好处。
首先我们介绍下什么是集中式,什么是分布式版本控制系统。
在集中式版本控制系统中版本库是集中存放在一台单独的中央服务器的,在我们开始工作的时候需要先从中央服务器取得最新的版本,然后开始干活,最后再把自己的代码推送到中央服务器。

而分布式版本控制系统没有中央服务器的概念,每个人的电脑上有一个完整的版本库,因此在工作的时候,就不需要联网了。多人协作的时候就可以把各自的修改直接推送给对方,但是在实际使用分布式版本控制系统的时候,很少使用刚刚提到的那种直接推送的方式,一般分布式版本控制系统通常也有一台中央服务器,但这个服务器的作用仅仅是用来方便交换各自的修改的,没有中央服务器也一样可以工作,只是使用起来会比较不方便而已。

下面分别是SVN 和 CVN工作方式的视图(图片来自图解Git相关工作流)

Git-GitHub_CVN

Git-GitHub_CVN

上图中很明显得看出,SVN模式下要是中央服务器挂掉的话后果是十分严重的,而分布式版本控制系统,中有个Local Repository 如果中央服务器挂掉,可以使用这个代码库对其进行恢复。

下面就以一个简单的工作流展开介绍:
Git-GitHub_CVN

安装Git

配置Git

Git配置变量可能保存在如下三个地方:
/etc/gitconfig 文件:系统中对所有用户都适用的配置(若使用git config –system 时配置的是这个文件)
~/.gitconfig文件:只适用于当前用户的配置(若使用git config –global 时配置的就是这个文件)
.git/config文件:仅仅针对当前项目有效的配置。 (若使用git config 时配置的就是这个文件)

配置指令:

git config --global user.name “xiaohai.lin”
git config --global user.email xiaohai.lin.hz@tcl.com
git config –global core.editor emacs
Git config –global merge.tool vimdiff

查询配置:

git  config --list
这里可能会看到重复的名字,这说明采用了不同的配置,Git实际采用的是最后的一个。

代码仓库的创建

首先你要有个repo,repo的创建可以有两种方式一种是从无到有,一种是通过Clone 一份现有的代码作为开发的起点。

  • 从无到有开始创建

    mkdir myrepo 创建一个本地的目录
    cd myrepo 进入到本地repo
    git init 初始化repo

    这时候当前的目录转换成一个Git仓库。它在当前的目录下增加了一个.git文件夹,所有的提交将会都记录到这个文件夹下。

  • Clone 一份现有的代码作为开发的起点

    git clone /path/to/repository (本地仓库的克隆)
    git clone username@host:/path/to/repository (通过SSH)
    git clone https:/path/to/repository.git (通过https)
  • Fork Github上的某个项目作为开发起点

    fork操作就相当于复制一份目标库(包括文件,提交历史,issues,和其余一些东西),通过fork你就可以自由得对项目进行修改而不用担心你的修改会影响到原始的项目。fork后仓库在你自己的GitHub帐号下。

    下面就以fork butterknife这个项目为例来介绍如何fork:

    • 首先进入这个项目的主页https://github.com/JakeWharton/butterknife。

    • 点击项目主页右上角的fork图标。

      这时候这个项目就会copy一份到你的github上。

    • 但是有时候你还需要经常保持与原库保持同步,所以你需要使用如下的方式来同步项目。

      git clone https://github.com/tbfungeek/butterknife.git

      进入clone到本地的代码根目录,

      git remote add upstream https://github.com/JakeWharton/butterknife.git

      这时候就可以通过如下命令查看结果:

      git remote -v

    • 同步
      从upstream 仓库 获取对应的分支和对应的提交,这些下载下来的提交存放在本地upstream/master 分支上。

      git fetch upstream

      将本地代码切换到主分支

      git checkout master

      接下来就需要将你下载下来的upstream/master分支merge到本地的master

      git merge upstream/master

      但是这仅仅是同步本地的代码库,要同步到你Github上还需要push到Github上。

  • Clone the Remote

    需要注意的是fork后代码只在你的Github上,如果需要下载到本地进行修改需要使用git clone

例子如下:

git clone https://github.com/tbfungeek/butterknife.git  

这时候本地仓库由 git 维护的三棵“树”组成。第一个是你的工作目录,它持有实际文件;第二个是 缓存区(Index),它像个缓存区域,临时保存你的改动;最后是 HEAD,指向你最近一次提交后的结果。

创建一个本地的分支

当clone完一份代码后,推荐创建一个本地分支进行开发,这样可以避免对master等其他分支产生干扰。

分支是用来将特性开发绝缘开来的。在你创建仓库的时候,master 是“默认的”。在其他分支上进行开发,完成后再将它们合并到主分支上。

  • 在git中提交时,会保存一个Commit对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,指向该提供对象直接祖先的指针。

  • Git中的分支,本质上仅仅是指向commit对象的可变指针,Git会使用master作为分支的默认名字。

  • HEAD 指向当前分支

git branch branchname 创建分支
git branch 查看分支
git branch -v 查看各分支的最后一次提交
git branch --merged 查看已经合并的分支[除了master分支外,列表中没有*的分支可以使用git branch –d删除]
git branch --no-merged 可以查看尚未合并的分支

切换到最新分支

git checkout branchname 切换到某个分支,这样HEAD就指向了指定的分支了。

本地操作

在介绍Git add/commit 之前需要先介绍下Git到底有哪些目录,各个阶段的文件分别存放在哪个地方:

三个工作区域:

  • 工作目录
  • 暂存区域(是一个简单文件,一般都放在git目录中,这个文件又可以称为索引文件)
  • git 本地代码库

三种状态:

  • 已修改(Modified):某个文件已经被修改但是还没提交保存
    (自上次取出后,做了修改但是还没放到暂存区)
  • *已暂存(staged)*:把已修改的文件放在下一次提交时要保存的清单中了。
    (在工作目录做了修改,并存放到暂存区)
  • *已提交(Committed)*:该文件已经被安全地保存在本地数据库中了
    (git 代码库中保存的特定版本)

在提交之前需要先查看所要提交的文件处于那种状态,所以一般我们提交的时候会先用git status 查看下状态:

nothing to commit(working directory clean)
没有任何跟踪着的文件,没有任何文件在上次提交后更改过,当前没有出现处于未跟踪的新文件。(没有添加,也没有改动)

Untracked files:
(代码库中添加了新的文件,还没执行add)

Change to be committed
(说明当前已经处于暂存状态,如果此时提交,那么该文件此时此刻的版本将被留存在历史记录中,)

Change but not update
(已跟踪文件的内容发生变化,但是还没放到暂存区,要暂存这次更新需要运行git add 命令)

git add 把它们添加到暂存区,使用如下命令:

git add <filename>
git add *
git commit -m "代码提交信息"
现在,你的改动已经提交到了 HEAD,但是还没到你的远端仓库。

Git commit只是将 暂存区域的内容提交到代码库。这里需要确认还有什么修改过的或者新建的文件没有git add过。否则提交的时候不会记录这些还没暂存起来的变化。

从下面图中可以很明显得看出各个操作的具体内容:

在提交的时候一般都会作两个工作就是运行git status 查看各个文件的状态,避免错误提交,
还有就是对比某个文件的内容的修改这个是使用git diff命令:

git diff: 查看 working directory 与 staging area 之间的差异
git diff --cached: 查看 repository 与 staging area 之间的差异
git diff HEAD: 查看 working directory 与 repository 之间的差异

删除文件:

  • 如果是Untracked 类型的文件可以直接使用rm
  • 如果是已经add了需要 git rm -f 但是运行这个命令后,文件直接被删除,
    如果你只是想退回到Untracked,则只需要git rm –cached test.md
  • Commit 后可以使用git rm 了 但是运行这个命令后,文件直接被删除.

回退修改:

这部分比较复杂可以根据下图的情况选择你所需要的:

这里只是介绍下如下几个简单的场景:

当你的修改还没add的时候想反悔怎么办:

git checkout -- <filename>  

此命令会使用 HEAD 中的最新内容替换掉你的工作目录中的文件。已添加到缓存区的改动,以及新文件,都不受影响。

git reset --hard <Commit ID>
  • 替换引用的指向。引用指向新的提交ID。

  • 替换暂存区。替换后,暂存区的内容和引用指向的目录树一致。

  • 替换工作区。替换后,工作区的内容变得和暂存区一致,也和HEAD所指向的目录树内容相同。

  • 这个命令就会导致所有信息的回退包括文件内容,执行后文件内容无法恢复回来。

    git reset  <Commit ID>
  • 即更改引用的指向及重置暂存区,但是不改变工作区。再次提交需要从git add做起

    git reset --soft <Commit ID>
  • 即只更改引用的指向,不改变暂存区和工作区。再次提交从git commit做起即可

假如你想要丢弃你所有的本地改动与提交,可以到服务器上获取最新的版本并将你本地主分支指向到它:

git fetch origin
git reset --hard origin/master

修补提交命令:

git commit --amend,用于对最新的提交进行重新提交以修补错误的提交说明或者错误的提交文件。

忽略某些文件:

有时候我们会有一些文件不需纳入Git管理,也不希望它们总是出现在未跟踪文件列表,我们可以创建一个名字为.gitignore的文件,列出需要忽略的文件模式。
.gitignore格式规范
所有以注释符号#开头的行都会被git忽略
可以使用标准的glob模式匹配
*表示匹配0个或者多个任意字符,
?匹配一个任意字符,
[],[-]
匹配模式最后跟/说明要忽略的是目录
要忽略指定模式以外的文件或者目录可以在模式前加上!取反

查看提交历史 :

git log
git log –p -2 最近两次的更新
git log –stat 仅仅显示简要的增改行数统计
git log –pretty=“oneline”“short”“full”“fuller”
git log –graph 简单图形,展示每个提交所在分支机器分化衍合情况。
这里还有很多属性可查看相关书籍:
下面列出常用的几个命令
git log -n 3 最近三次提交
git log --after(since) = “2014-01-05” 这个时间之后 时间可以是如下格式:2014-01-05
2.years[month][day][hours][minute]
git log --before(untill) = 2014-01-05 这个时间之前
git log -- author 仅显示指定作者相关的提交
git log --committer 仅显示和指定提交者相关的提交
git log -- 路径只关心某个路径下的提交记录
git log --author=xiaohai.lin --since=“2015-01-01” --before=“2016-01-01” —no-merges -- dir/

将代码提交到远程代码库

  • 添远程仓库(这种只会出现在第一次创建本地代码库并从零开始开发完提交到远程代码库的情况):
    如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:

    git remote add origin <server>
    git remote add repoShortName git://github.com/paulboone/ticgit.git

    repoShortName:简短仓库名可以用这个名字代替仓库地址了。一般远程服务名为origin
    如此你就能够将你的改动推送到所添加的服务器上去了。

  • 推送数据到远程仓库

    Git push origin master (这里不一定是master 可以是你本地的任何分支)
    [remote name] [branch name]

获取最新代码

Git中可以通过 git fetch 以及 git pull 获取最新的代码,但是二者之间是有所区别的:
git fetch:相当于是从远程获取最新版本到本地,不会自动merge

git fetch origin master:tmp 将代码同步到本地的tmp分支,这时候使用使用git branch 可以看到这里有多出一个分支
git diff tmp 比较分支的差别
git merge tmp 手动merge分支

这是个人比较推崇的一种方式。

git pull:相当于是从远程获取最新版本并merge到本地,相当于git fetch 和 git merge
在实际使用中,git fetch更安全一些,因为在merge前,我们可以查看更新情况,然后再决定是否合并

merge代码

当出现冲突的时候可以使用如下方式解决冲突:

git merge

这时候就会要你选择使用那种merge工具

输入meld就可以使用meld来解决冲突了。

merge 完成后记得要需要执行如下命令以将它们标记为合并成功:
git add
git commit

一般merge完成后就可以删除这个没用的临时分支了:

git branch –d newbranch 删除分支
git branch –D branchname 如果删除这些分支会出现错误,如果执意要删除这些分支可以使用

标签

在软件发布时创建标签,是被推荐的。可以执行如下命令以创建一个叫做 1.0.0 的标签:

git tag 1.0.0 1b2e1d63ff

1b2e1d63ff 是你想要标记的提交 ID 的前 10 位字符。使用如下命令获取提交 ID:


分支操作命令总结:

创建分支 			: git branch branchname
切换到某个分支 : git checkout branchname 这样HEAD就指向了指定的分支了。
新建并切换到新的分支 :git checkout –b ‘newbranchname’
切换到master主分支并合newbranch分支
git checkout master
git merge newbranch
删除分支
Git branch –d newbranch
查看分支
Git branch
查看各分支的最后一次提交
Git branch –v
查看已经合并的分支
Git branch –merged 列表中没有*的分支可以使用git branch –d删除
Git branch –no-merged 可以查看尚未合并的分支
如果删除这些分支会出现错误,如果执意要删除这些分支可以使用
Git branch –D branchname

远程代码库操作命令总结:

git remote –v查看当前配置有哪些远程仓库。
git remote add origin <server> 添加远程分支
重命名远程代码库
git remote rename pd paul 将pd命名为paul
删除远程代码库
碰到远程仓库服务器迁移,或者原来的克隆镜像不再使用,或者参与者不再贡献代码,那么需要移除对应的远程仓库。
Git remote rm paul

团队工作方式

Git 分支工作流


参考资料:

Contents
  1. 1. 安装Git
  2. 2. 配置Git
  3. 3. 代码仓库的创建
    1. 3.1. 从无到有开始创建
    2. 3.2. Clone 一份现有的代码作为开发的起点
    3. 3.3. Fork Github上的某个项目作为开发起点
  4. 4. 创建一个本地的分支
  5. 5. 切换到最新分支
  6. 6. 本地操作
  7. 7. 将代码提交到远程代码库
  8. 8. 获取最新代码
  9. 9. merge代码
  10. 10.
  11. 11. 标签
  12. 12. 团队工作方式
  13. 13. Git 分支工作流