BlueXIII's Blog

热爱技术,持续学习

0%

Git内部培训课件

Git简介

什么是版本控制

版本控制系统(Version Control System,简称VCS)是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

按类型可以分为:

  • 本地版本控制系统

    例如RCS(至少我是从来没有用过)
    本地版本控制系统解决了版本的管理问题,再也不用时不时的把工程目录,通过手工拷贝的方式来存档了。但本地版本控制系统的缺点是,无法解决多人协作的问题。

  • 集中化的版本控制系统

    例如CVS,SVN等(公司中SVN应该用的比较多)
    有一个集中管理的服务器,所有开发人员通过客户端连到这台服务器,取出最新的 文件 或者提交更新。管理员可以掌控每个开发者的权限。
    集中化的VCS不但解决了版本控制问题,还可以多人协作。但缺点也是有的,就是太依赖于远程服务器,CVS服务器宕机后,会影响所有人的工作。版本记录只保存在一台服务器上,会有数据丢失风险。

  • 分布式版本控制系统

    例如Git
    客户端并不只提取最新版本的文件,而是把 代码仓库 完整地镜像下来。每一次的提取操作,实际上都是一次对 代码仓库 的完整备份。
    所以并没有”中心服务器”的概念,所谓的”Git服务器”,也同每个人的电脑一样,只是为了多人协作时,方便大家交换数据而已。

什么是Git

Git是目前世界上最先进的分布式版本控制系统(没有之一)
好不好用,看看它的开发者是谁就知道了:Linux之父 Linus Torvalds

小历史: Linux内核社区原本使用的是名为BitKeeper的商业化版本控制工具,2005年,因为社区内有人试图破解BitKeeper的协议,BitMover公司收回了免费使用BitKeeper的权力。
Linus原本可以出面道个歉,继续使用BitKeeper,然而并没有。。。Linus大神仅用了两周时间,自已用C写了一个分布式版本控制系统,于是Git诞生了!

为什么要使用Git

为什么要使用Git,或者说Git相比SVN有什么优势呢?

  • 分布式

  • 分支管理

  • GitHub

安装Git

  • 大多数Linux发行版已经预装了Git,系统默认自带,如果不带。。可以源码make安装或使用yum/apt等直接安装,过程不赘述了。
  • macOS下,安装Xcode后,它的CLI工具里应该会包含Git了。或者使用brew手工安装一下。
  • Windows下,可以直接下载安装 msysGit 。 或者如果你的机器上已经有Cygwin,也可以直接用在它下面安装Git。
  • 图形工具推荐使用 SourceTree,查看分支非常直观 。IntelliJ IDEA等IDE也会自带一些图形化的工具,在合并代码时很高效。

学习路径

  • 首先,忘掉SVN/CVS,不要把Git的各种操作与它们做类比,切记。
  • 刚开始不要依赖图形客户端。首先应该将精力用在理解原理上 -> 然后掌握一些基本CLI命令,动手操作实践 -> 最后在实际工作中使用GUI工具以提高效率。
  • 重度Windows用户使用Git时,与平时熟悉GUI的环境会有些违和感,毕竟Git是Linux下的产物,Git遵循Linux的哲学,Simple,简单直接,但Simple并不等于Easy。需要转换一下思维。

了解Git的工作原理

记录文件整体快照

Git和其他版本控制系统的主要差别在于,Git只关心文件数据的 整体 是否发生变化,而大多数其他系统则只关心 文件内容 的具体差异。

SVN在每个版本中,以单一文件为单位,记录各个文件的差异:

Git在每个版本中,以当时的全部文件为单位,记录一个快照:

大多数操作都在本地执行

Git的绝大多数操作都只需要访问本地文件和资源,不用连网。因为你的本机上,就已经是完整的代码库了。这样一来,在无法连接公司内网的环境中,也可以愉快的写代码了。
例如,如果想看当前版本的文件和一个月前的版本之间有何差异,Git会取出一个月前的快照和当前文件作一次差异运算,而不用每次都请求远程服务器。

时刻保持数据完整性

在保存到Git之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。
这项特性作为Git的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git都能立即察觉。
Git使用SHA-1算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个SHA-1哈希值,作为指纹字符串。该字串由40个十六进制字符组成,看起来就像是:
24b9da6552252987aa493b52f8696cd6d3b00373
Git的工作完全依赖于这类指纹字串,所以你会经常看到这样的哈希值。实际上,所有保存在 Git数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。

多数操作仅添加数据

常用的Git操作大多仅仅是把数据添加到数据库,很难让Git执行任何不可逆操作。在Git中一旦提交快照之后就完全不用担心丢失数据,特别是养成定期推送到其他仓库的习惯的话。

文件的三种状态

对于任何一个文件,在 Git 内都只有三种状态:已提交(committed) 已修改(modified) 已暂存(staged)
已提交表示该文件已经被安全地保存在本地数据库中了;
已修改表示修改了某个文件,但还没有提交保存;
已暂存表示把已修改的文件放在下次提交时要保存的清单中。

由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录,暂存区域,以及本地仓库。

每个项目都有一个名为.git的目录,它是 Git用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从Git目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。
所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。

基本的 Git 工作流程如下:

  1. 在工作目录中修改某些文件。
  2. 对修改后的文件进行快照,然后保存到暂存区域。
  3. 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。

所以,我们可以从文件所处的位置来判断状态:如果是Git目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。

创建版本库

有两种取得Git项目仓库的方法。第一种是在现存的目录下,通过导入所有文件来创建新的Git仓库。 第二种是从已有的Git仓库克隆出一个新的镜像仓库来。

在目录中创建新仓库

如果一个目录还没有使用Git进行管理,只需到此项目所在的目录,执行git init,初始化后,在当前目录下会出现一个名为.git的目录

1
2
3
$ mkdir learngit
$ cd learngit
$ git init

从已有的仓库克隆

如果Git项目已经存在,可以使用git clone从远程服务器上复制一份出来,Git支持多种协议:

1
2
3
$ git clone mobgit@134.32.51.60:learngit.git  #使用SSH传输协议
$ git clone git://134.32.51.60/learngit.git #使用Git传输协议
$ git clone https://134.32.51.60/learngit.git #使用HTTPS传输协议

版本库基本操作

检查当前文件状态

使用git status命令可以查看文件的状态

1
2
3
4
$ git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)

出现如上的提示,说明现在的工作目录相当干净,所有已跟踪文件在上次提交后都未被更改过。

现在我们做一些改动,添加一个readme.txt进去,然后再看一下状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cat>readme.txt
hello git
^C

git status
On branch master

Initial commit

Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt

nothing added to commit but untracked files present (use "git add" to track)

Untracked files显示了这个新创建的readme.txt处于未跟跟踪状态

跟踪新文件

使用git add命令开始跟踪一个新文件

1
2
3
4
5
6
7
8
9
$ git status
On branch master

Initial commit

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: readme.txt


readme.txt已 被跟踪 ,并处于 暂存状态

将本次修改暂存

现在我们再对readme.txt进行修改,添加一行,再执行git status查看状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git status
On branch master

Initial commit

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: readme.txt

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: readme.txt

可以看到readme.txt 不仅出现在了Changes to be committed,还出现在了Changes not staged for commit
由此可见,Git关心的是 Changes ,而不是文件本身。
再次执行git add,可以将 本次修改 提交到暂存区,Changes not staged for commit提示消失

提交更新

使用git commit命令将暂存区中的内容提交至版本库,工作区又是干净的了

1
2
3
4
5
6
7
8
9
10
$ git commit -m "my first commit"
[master (root-commit) 6c8912a] my first commit
1 file changed, 2 insertions(+)
create mode 100644 readme.txt

$ git status
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)
nothing to commit, working tree clean

注意:一定要使用-m参数加入注释,认真描述本次的提交具体做了些什么,这对于以后我们查询历史记录非常重要。

如果觉得使用暂存区过于繁琐,可以在commit时直接使用-a参数,Git就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add步骤。

1
$ git commit -a -m "my first commit"

查看历史

使用git log命令可以查看历史记录

1
2
3
4
5
6
7
8
9
10
11
12
$ git log
commit 43c5d337ffdd76f33ce5f5f90103d57e55474956
Author: BlueXIII <bluexiii@163.com>
Date: Thu Dec 8 14:45:59 2016 +0800

this is my second commit

commit 6c8912ad2a8e90a7ba32cc8578fd0069a205221b
Author: BlueXIII <bluexiii@163.com>
Date: Thu Dec 8 14:38:09 2016 +0800

my first commit

可以看到,每次更新都有一个SHA-1校验和、作者的名字和电子邮件地址、提交时间、提交说明。

撤消操作

撤消操作在这里这里不做重点描述了,只列出几个常用命令。
修改最后一次提交:
git commit –amend
取消已经暂存的文件:
git reset HEAD readme.txt
取消对文件的修改:
git checkout – readme.txt

远程仓库

之前介绍了在本地仓库的一些操作。但当与他人协作开发某个项目时,需要至少使用一个远程仓库,以便推送或拉取数据,分享各自的工作进展。

克隆远程库

之前已经在讲新建仓库时已经提到,如何克隆远程库,这里再重复列一遍:

1
2
3
$ git clone mobgit@134.32.51.60:learngit.git  #使用SSH传输协议
$ git clone git://134.32.51.60/learngit.git #使用Git传输协议
$ git clone https://134.32.51.60/learngit.git #使用HTTPS传输协议

查看绑定的远程库

如果之前我们使用的git clone命令直接克隆了一个远程仓库到本机,Git就已经默认绑定了一个名为origin的远程库。当然我们还可以手工绑定其它远程库,远程仓库可以有多个。
使用git remote -v命令列出我们绑定了哪些远程库:

1
2
3
$ git remote -v
origin mobgit@134.32.51.60:learngit.git (fetch)
origin mobgit@134.32.51.60:learngit.git (push)

接下来还可以使用git remote show origin来查看这个名为origin的远程库的更详细的信息,这里先不细讲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ git remote show origin
* remote origin
Fetch URL: mobgit@134.32.51.60:learngit.git
Push URL: mobgit@134.32.51.60:learngit.git
HEAD branch (remote HEAD is ambiguous, may be one of the following):
dev
master
serverfix
serverfix2
Remote branches:
dev tracked
master tracked
serverfix tracked
serverfix2 tracked
Local branches configured for 'git pull':
dev merges with remote dev
master merges with remote master
serverfix merges with remote serverfix
serverfix2 merges with remote serverfix2
Local refs configured for 'git push':
dev pushes to dev (up to date)
master pushes to master (up to date)
serverfix pushes to serverfix (up to date)
serverfix2 pushes to serverfix2 (up to date)

手工添加一个远程仓库

我们先让管理员新建一个名为learngit2的远程仓库,再使用remote add命令将它添加进来,取名为repo2

1
2
3
4
5
6
7
$ git remote add repo2 mobgit@134.32.51.60:learngit2.git

$ git remote -v
origin mobgit@134.32.51.60:learngit.git (fetch)
origin mobgit@134.32.51.60:learngit.git (push)
repo2 mobgit@134.32.51.60:learngit2.git (fetch)
repo2 mobgit@134.32.51.60:learngit2.git (push)

现在我们有origin和repo2两个远程仓库了

从远程仓库抓取数据

使用git fetch [remote-name]从远程仓库抓取数据,注意fetch命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支(关于分支稍后讲解)
例如要抓取名为origin远程仓库:

1
$ git fetch origin

推送数据到远程仓库

使用git push [remote-name] [branch-name]将本机的工作成果推送到远程仓库
例如要将本地的master分支推送到origin远程仓库上:

1
$ git push origin master

分支

也许到之前为止,大家会觉得Git和Svn除了实现原理不同以及实现了分布式之外,在日常使用上并没有什么太大的区别(甚至更繁琐)。但接下来的分支,才是Git的精髓部分。

为什么要使用分支

举个简单的例子:假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
于是你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
相比于Svn等工具,Git创建、切换分支的开销是非常小的,Git鼓励 频繁使用分支

分支的原理

要理解分支,需要继续深入一下Git的工作原理

Git如何储存数据

在Git中提交时,会保存一个提交对象(commit object),该对象包含一个指向暂存内容快照的指针,并同时包含本次提交的作者等相关附属信息,包含零个或多个指向该提交对象的父对象指针(首次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先)。

假设在工作目录中有三个文件已经 修改 过,准备将它们暂存后提交。
git add暂存操作时,会对 每一个文件 计算校验和,然后把当前版本的文件快照使用 blog对象 保存到Git仓库中(为提高性能,若文件没有变化,Git不会再次保存)。将它们的SHA-1校验和加入到暂存区域等待提交。
git commit提交操作,时,Git首先会计算 每一个子目录 的校验和,然后将这些校验和保存为 tree对象 。 然后Git会创建一个 commit对象 ,它包含指向这个树对象的指针及注释、提交人、邮箱等信息。
现在,Git仓库中有五个对象:三个blob 对象(保存着文件快照);一个树对象(记录着目录结构和blob对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。

单个提交对象在仓库中的数据结构:

多个提交对象之间的链接关系:

分支是什么

Git 中的分支,其实本质上仅仅是个指向commit对象的可变指针。Git会使用master作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次commit对象的master分支。它在每次提交的时候都会自动向前移动。

创建名为testing的新的分支,本质上就是创建一个指针,可以使用git branch命令:

1
$ git branch testing

当前工作在哪个分支

Git 是如何知道你当前在哪个分支上工作的呢?其实答案也很简单,它还保存着一个名为HEAD的特别指针。它是一个指向你正在工作中的本地分支的指针。

切换分支时发生了什么

切换分支,本质上就是移动HEAD指针。
要切换到其他分支,可以执行git checkout命令。我们现在转换到刚才新建的testing分支:

1
$ git checkout testing

分支切换的实际操作

为了更好的理解分支,我们接下来模拟实际工作中的场景,进行一系列的切换操作。
现在我们已经处于testing分支了,目前testing分支和master分支都是指向同一个commit,所以我们的工作区的内容现在还没有什么变化。
现在,我们要在testing分支上做一些文件修改,然后commit:

1
2
echo "testing branch">>readme.txt
git commit -a -m "modify on testing branch"


提交后,产生了一个新的commit对象,并且HEAD随着当前testing分支一起向前移动。而master分支则是停在原地不动。

我们可以试着使用git checkout命令切回master分支,看看发生了什么:

1
$ git checkout master


这条命令做了两件事:

  1. 它把HEAD指针移回到 master 分支。
  2. 把工作目录中的文件换成了master分支所指向的快照内容。

我们试着在master上再做一些改动并commit:

1
2
echo "testing master">>readme.txt
git commit -a -m "modify on master branch"


现在分支变成了上图所示,我们可以在master与testing间随时切换,并修改工作区的文件内容。必要时再将这两个分支合并。

分支新建与合并的实际操作

接下来,再以一个比较长的真实的工作场景进行举例

我们首先在master分支上进行工作,并提交了几次更新,测试无误后编译发布至生产系统。

之后我们决定要修补问题追踪系统上的53号问题,这时可以使用git checkout -b命令快速创建一个分支并切换过去:

1
$ git checkout -b iss53

这相当于执行了下面这两条命令:

1
2
$ git branch iss53
$ git checkout iss53

我们在iss53分支上写了一些代码,并commit

1
2
$ vi index.html
$ git commit -a -m 'fixed the broken email address'

iss53上的工作还没完成,突然接到通知,生产系统有一个紧急BUG需要立刻修复。所以我们首先切回master分支,然后在master的基础上,又新建出一个hotfix分支来修复BUG。

1
2
3
4
$ git checkout master    #回到master分支
$ git checkout -b hotfix #新建一个hotfix分支,并切过去
$ vim index.html #修改一些东西,修复BUG
$ git commit -a -m 'fixed the broken email address' #提交hotfix

在hotfix分支上搞定BUG之后,我们切回master分支,使用git merge把刚才的hotfix合并进来

1
2
3
4
5
6
$ git checkout master  #切换回master分支
$ git merge hotfix #将hotfix分支的修改,合并到当前master分支来(注意merge的方向,是从其它分支,合到当前分支)。
Updating f42c576..3a0874c
Fast-forward
README | 1 -
1 file changed, 1 deletion(-)


备注:本次合并时出现了“Fast forward”的提示。由于当前 master 分支所在的提交对象是要并入的 hotfix 分支的直接上游,Git 只需把 master 分支指针直接右移。换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进(Fast forward)。

这时hotfix分支已经没用了,可以删掉了

1
$ git branch -d hotfix    #只是删除了一个指针

现在回到之前未完成的53号问题上,继续写一些代码

1
2
3
$ git checkout iss53
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'

在问题53相关的工作完成之后,可以合并回master分支。实际操作同前面合并hotfix分支差不多,只需回到master分支,运行git merge命令指定要合并进来的分支。

1
2
3
4
5
6
7
$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)


请注意,这次合并操作的底层实现,并不同于之前 hotfix 的并入方式。因为这次你的开发历史是从更早的地方开始分叉的。由于当前 master 分支所指向的提交对象(C4)并不是 iss53 分支的直接祖先,Git 不得不进行一些额外处理。就此例而言,Git 会用两个分支的末端(C4 和 C5)以及它们的共同祖先(C2)进行一次简单的三方合并计算。


这次,Git 没有简单地把分支指针右移,而是对三方合并后的结果重新做一个新的快照,并自动创建一个指向它的提交对象(C6)。这个提交对象比较特殊,它有两个祖先(C4 和 C5)。

有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分,需要手工来处理冲突。

1
2
3
4
5
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Git作了合并,但没有提交,它会停下来等你解决冲突。

1
2
3
4
5
6
7
8
9
10
11
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified: index.html

no changes added to commit (use "git add" and/or "git commit -a")

任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出。Git 会在有冲突的文件里加入标准的冲突解决标记。

1
2
3
4
5
6
7
8
$ vi index.html
<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53

可以看到 ======= 隔开的上半部分是 HEAD,即master,下半部分是在iss53分支中的内容。
手工合并代码后,把 <<<<<<<,======= 和 >>>>>>> 这些行也一并删除。这时可以用git commit来提交了。

分支策略

实际开发中,对于分支的管理,已经有很多最佳实践,大多数情况下,我们只需要遵守一些基本原则:

  • 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面工作。
  • 平时的开发工作都放在dev分支上,也就是说,dev分支是不稳定的。到某个时候,比如测试通过,需要1.2版本发布时,再把dev分支合并到master上,在master分支编译发布1.2版本。
  • 针对新需求、修复等具体的任务,每次都在dev分支上开一个新的任务分支出来,工作完成后,再向dev分支上合并就可以了。名称没有特别的规范,可以是人名,例如:zhangsan,也可以是任务名、需求编号等,例如:iss03、feature04、hotfix。

远程分支

之前讨论过远程仓库,接着又学习了分支,当二者结合到一起时,又会产生一些有趣的东西。

远程分支的概念

远程分支(remote branch),即远程仓库中的分支。同步到本地后,与本地分支不同的是,它们 无法移动 ;且只有在Git进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支(例如origin/master)。

如果我们在本地master分支做了些改动,与此同时,其他人向远程仓库推送了他们的更新,那么服务器上的master分支就会向前推进,而于此同时,我们在本地的提交历史正朝向不同方向发展。(不过只要你不和服务器通讯,你的 origin/master 指针仍然保持原位不会移动。)

可以运行git fetch origin来同步远程服务器上的数据到本地。该命令首先找到origin是哪个服务器,然后从上面获取你尚未拥有的数据,更新你本地的数据库,然后把origin/master的指针移到它最新的位置上。

可以使用git remote命令查看远程仓库的详情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ git remote -v    #列出远程服务器清单
origin mobgit@134.32.51.60:learngit.git (fetch)
origin mobgit@134.32.51.60:learngit.git (push)

$ git remote show origin #查询某一个远程服务器的详情
* remote origin
Fetch URL: mobgit@134.32.51.60:learngit.git
Push URL: mobgit@134.32.51.60:learngit.git
HEAD branch (remote HEAD is ambiguous, may be one of the following):
dev
master
Remote branches:
dev tracked
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)

跟踪远程分支

从远程分支checkout出来的本地分支,称为跟踪分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支。
在跟踪分支里输入 git push,Git 会自行推断应该向哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来。

在克隆仓库时,Git 通常会自动创建一个名为 master 的分支来跟踪 origin/master。这正是 git push 和 git pull 一开始就能正常工作的原因。

1
2
3
$ git checkout -b serverfix origin/serverfix
或简化为:
$ git checkout --track origin/serverfix

这会新建并切换到serverfix本地分支,其内容同远程分支origin/serverfix一致。

推送本地分支

要想和其他人分享某个本地分支,你需要把它推送到一个你拥有写权限的远程仓库。
例如本地有一个serverfix分支需要和他人一起开发,可以运行 git push (远程仓库名) (分支名):

1
2
3
4
5
6
7
$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
* [new branch] serverfix -> serverfix

或者加入–set-upstream设置跟踪后,以后直接使用git push就可以推送了:

1
2
git push --set-upstream origin serverfix

GitHub

[GitHub](https://github.com)是一个面向开源及私有软件项目的托管平台,因为只支持Git作为唯一的版本库格式进行托管,故名GitHub。

GitHub本身没有什么好学的,随便看就知道怎么用了 知乎:怎样使用GitHub
重点是,GitHub上有非常多优秀的个人项目值得我们学习,我们也可以将自已的代码发布上去。可以看成是程序员的博客吧,只贴代码,不废话。
在GitHub上发布开源项目是免费的,但是私有项目收费。

GitLab

GitLab是一个用Ruby on Rails写的开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。它拥有与Github类似的功能,能够浏览源代码,管理缺陷和注释。
可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。团队成员可以利用内置的简单聊天程序(Wall)进行交流。它还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找。
GitLab是目前搭建内部Git服务器的首选,当然如果要求不高的话,我们也可以直接使用SSH协议来快速搭建Git服务端。

常用Git命令清单


更多内容请直接参考 阮一峰的网络日志

推荐文档

不要指忘2小时的培训能带来多大的收益,最简单高效的方式,还是要多看优秀的文档。
本文大量参(chao)考(xi)了以下两部文档:
廖雪峰的在线教程 适合快速上手
Pro Git中文版 中文第一版