本文大部分来自对廖雪峰Git教程 的学习,结合自己的体会,初步掌握Git的使用方法和Github的工作方式。
不会很难,但是琐碎,所以多实践,否则记不住。
1. 版本管理系统
为什么要管理版本,有实际开发经历的人都有体会,就不需要多说了。这就是水和空气一样,那么重要,那么天然。
1.1 集中式
从中央服务器获取文件,修改后送回
集中式是比较自然和容易理解的一种方式。大家都从一个服务器下载文档和代码,修改后再送回去,这样就完成了版本控制和多人协作。
早起比较广泛使用的大部分是集中式的版本管理工具:
- CVS
- SVN
- MKS
那么集中式版本管理工具的短板是什么呢。
- 必须联网,否则不能工作
- 性能很差
性能很差这个没有异议,确实很慢,以前在老东家使用MKS作为版本管理工具,性能瓶颈非常恼人。SVN的性能强于MKS,但是仍然不能令人满意。
必须联网这一条并不是说不联网不能修改文件,意思是不联网的话,只能修改本地保存的文件,不能进行版本控制,也就是说如果1天都连不上网,那么这一天的修改都只能一股脑的在下次能联网的时候推上去。
1.2 分布式
每个人的电脑多有一份版本的拷贝,中央服务器不是必须的。
分布式与集中式比较起来,我感觉最显著的差别是本地修改的时候可以有自己的版本迭代,并不需要经常的与中央服务器交互。
分布式版本管理工具并不是说完全不用中央服务器。中央服务器可以起到交换各自修改的作用,本地完成修改以后,可以推送到远程服务器上,与其他人协作工作。
分布式版本管理工具比如
- BitKeeper
- Mecurial
- Git
Git太流行,主要的有点是
- 性能好
- 分支管理优秀
这篇文章就是介绍Git的基本用法和一个流行的远程库托管机构Github。
2. Git的安装
2.1 Linux
Linux下是否有安装Git,简单敲一下命令就知道了
git
根据提示信息就知道是否安装了,如果没有安装,我使用的ubuntu,那么apt-get一下就可以:
sudo apt-get install git
2.2 Mac
我没有Mac,所以也没有关注过怎么安装。自行baidu吧。
2.3 Windows
Windows下也是可以安装Git的,安装以后会提供一个Git的shell环境。
Win下的Git软件叫msysgit
下载安装即可,用的不多,这里不讨论。
2.4 初次配置
Git使用,需要使用用户名和邮箱作为个人的ID
第一次使用的时候,需要设置一下个人的信息,包括用户名和邮箱,通过命令:
git config --global user.name XXX
git config --global user.email YYY@ZZZ
这里–global 参数表明这是全局的设置,这台电脑上所有的Git仓都会使用这个用户名和邮箱。当然也可以位不同的Git仓使用不同的用户名和邮箱。
3. Git使用
3.1 初始化仓库
Git的文件仓库叫做Repository
在需要建立仓库的文件夹下,输入以下命令建立一个Git仓:
git init
看到提示信息,仓库建立成功。
3.2 添加文件
有了仓库以后,可以向其中添加文件。没有手动添加过的文件,虽然存在与Git仓库文件夹内,也不会受到Git的管理。
添加文件通过以下命令:
git add [file name]
git add *.c
git add .
如果要提交不止一个文件的修改,也是没有问题的,可以反复使用git add,添加多个文件到暂存区,然后一次性的提交到仓库里。或者在一次add中带有多个文件名作为参数,或者通过通配符,“.”等参数添加多个文件进去。
git add 还有其他参数,可以参考:
git add只是提交了修改到暂存区,关于暂存区的概念,下边“Git的几个分区”会详细介绍。文件的提交并没有结束,想要真正提交到库上,还需要以下命令
git commit -m "This is a description of the commit"
提交成功后有提示:
- 4个文件有修改
- 创建了4个文件
3.3 修改文件
3.3.0 Git的几个分区
Git 一共有3个区域的概念
- 工作区 working directory
- 暂存区 stage
- 仓库分支
这3个区域,按照层级结构,逐层关联。即工作区和暂存区可以相互影响,暂存区和仓库分支可以相互影响,但是工作区和仓库分支并不能直接相互影响。下文的修改撤销,会对这个问题进行描述。
工作区
工作区就是本地修改文件的工作目录。工作区并不受到Git仓库的管理,可以任意的修改和增删文件。
暂存区
暂存区已经受到Git的管理,所有文件的修改,都需要首先提交到暂存区。前文提到的git add命令,就是将工作区的修改提交到暂存区的命令。
仓库分支
Git的分支比较灵活,可以有一个远程的分支,一般命名位origin,在中央服务器上;本地一个初始的分支,命名为master。本地还可以创建任意多个分支。每次git commit,都是把暂存区的修改,提交到当前的分支上。
关于分支的创建,合并和推送,下文会有说明。
3.3.1查看Git仓库状态
最经常用的一个Git 命令,就是查看当前Git仓库的状态:
git status
看到的结果可能如下
这里提示的信息挺全的
- 有一个文件修改了,1.c
- 目前可以做的动作有
- git add 把这个文件的修改添加进暂存区
- git checkout [file name] 或者 git checkout – [file name] 恢复这个文件,取消修改
现在用git add添加这个文件的修改,再次用git status查看状态
提示信息发生了变化,提示到
- git reset HEAD [file] 将暂存区的修改取消,恢复到Git仓中的文件状态
- git commit 提交这个修改到Git仓中,使这个修改在Git仓中生效
3.3.2 查看文件改动
上面的git status只能看到哪个文件被修改了,如果向看具体怎么改的,需要用到另一个命令:
工作区和暂存区的文件比较
git diff [file]
工作区和版本库的文件比较
git diff HEAD [file]
是的,这是Unix标准的diff格式查看命令。
上面改动的1.c文件,已经加入到暂存区中,所以用git diff查看是没有结果的,工作区和暂存区文件一样。应该用git diff HEAD:
3.3.3 查看修改记录
想要查看之前提交的各个修改都是什么,需要用到
git log
git log --pretty=oneline
git log --graph
其中能够看到的信息是这个提交的id,commit id,提交者,时间,提交的时候 -m 后面双引号括住的提交信息。
commit id是一串哈希值,可以用这个哈希值,或者只是前面若干字符,代表这个commit。
git log的其他参数,可以自己试试。
3.3.4 撤销之前的修改
撤销修改,包括撤销3个区的改动,工作区,暂存区和仓库分支的改动,下面分别说明
所谓修改撤销,就是恢复到上一个已经保存过的文件状态,取消从上一个状态到当前所做的改动。
- 工作区
- 工作区的修改,如果想要撤销,只能通过暂存区保存的该文件的上一个状态来恢复。使用的命令是git checkout [file], git checkout – [file]。这个命令执行以后,工作区的文件,就可以恢复到与暂存区一致的状态。
- 暂存区
- 暂存区的修改,如果想要撤销,只能通过仓库分支保存的该文件的上一个状态来恢复。使用的命令是git reset HEAD [file]。这个命令执行以后,暂存区的文件,就可以恢复到与仓库分支一致的状态。
- 仓库分支
- 仓库分支的修改,如果想要撤销,只能通过再次提交一个修改。暂存区通过git reset HEAD^,可以恢复成仓库分支上一个版本的状态,更多细节参考下文的版本回退。
3.3.5 删除文件
删除文件命令
git rm [file]
注意 本地删除文件,参考git add命令表格,可以通过git add的某些选项和git rm来完成。
这些命令执行以后,文件的删除信息就提交到暂存区了,还需要通过git commit来提交到仓库分支,最终生效。
工作区删除了,但是是误删除,想要恢复怎么办
上一节撤销修改提到,恢复工作区的方法,恢复被删除的文件同样适用。git checkout [file] 即可。
3.4 版本回退
版本的表示
-HEAD
-HEAD^
-HEAD^^
-HEAD~100
这里的版本回退,指的是仓库分支版本的回退。上面的几个版本表示方法,都是仓库分支的版本表示方法。当前版本,前一个,前两个,前100个。
版本回退的命令是
git reset HEAD^
git reset [commit id]
git reset --hard HEAD^
git reset --hard [commit id]
–hard的作用
一并将工作区和暂存区都恢复到目标版本的状态。如果没有这个选项,将只有仓库分支指向的版本发生变化,工作区和暂存区都不动,通过git status能看到版本间的差异。
回退错了,怎么恢复到最新的版本
回退版本之后,git log查看到最新的就是回退之后的版本,更新的版本已经在git log中看不到了。这样就不能通过git reset -hard [commit id]来恢复到之前的新版本。
HEAD也变成了这个稍老一些的版本,不能通过git reset –hard HEAD来恢复。
比如已经进行了几次git commit,版本库增加了几次提交,用git log查看版本库:
此时回退版本库,回退到最开始的状态,最后2个提交放弃,那么通过命令:
git log HEAD^^
查看log:
可以看到,最后2此提交已经看不到了。那么如果这个时候反悔了,还是想要包括最后的2次提交怎么办呢,在git log里边已经看不到ID号了。还是有办法的,通过以下命令:
git reflog
这里边记录了所有的log,可以在这里找到丢失的commit id,通过git reset –hard [commit id]恢复到之前的新版本。
git reset a9af60c
那么就恢复到“update 3.txt”这个版本了。
3.5 分支管理
3.5.0 多分支工作方式
Git上多分支的工作方式如图
- 主线master,仅用来发布版本
- dev分支,大家日常工作的分支
- 个人分支,每个人都工作在自己的分支上,阶段性的merge到工作分支dev中
- 个人还可以根据需要,创建更多的分支,修改完成后,merge到个人分支上
3.5.1 创建分支
git checkout -b [branch name]
创建分支,同时切换到这个分支上,相当于2条命令
git branch [branch name]
git checkout [branch name]
3.5.2 查看分支
git branch
查看所有分支的信息,包括有多少个分支,当前处在哪个分支上。
3.5.3 修改分支
切换到哪一个分支,当前就在哪个分支上。
所有的修改,都提交到当前的分支上了。
可以在不同的分支之间随意切换,通过
git checkout [branch]
比如上图
- 切换到dev,那么dev的HEAD就是当前的HEAD
- 切换到master,master的HEAD就是当前的HEAD
3.5.4 合并分支
不同的分支用于不同的修改,最终还是需要合并到一起的。
分支合并的方式是
- 切换到主分支(或者相对主分支)
- 通过命令git merge合并
git merge [branch name]
分支合并的FastForward模式
- ff模式,合并进来的分支的HEAD,作为主分支的新的HEAD
- 非ff模式,产生一个新的HEAD,需要参数-m 提供描述信息
ff模式和非ff模式的区别,是在log中能不能看出来曾经有过一个分支。ff模式是看不出来的。
所以为了保留完整的log,建议都使用非ff模式合并分支。
git merge --no-ff -m "description" [branch name]
3.5.5 删除分支
合并以后的分支,即可以删除了。
删除分支的命令是
git checkout -d [branch name]
3.5.6 冲突解决
发生冲突的场景一般是,多人共同工作在一个分支上,分别做了各自的修改,然后需要merge到一起。
比如下图
图中,feature1分支从master分出来,之后master又merge了别的分支,增长了版本。
那么feature1如何合并入分支master呢。
- 在feature1上提交merge
- 会提示有冲突,比如下图是一个文本文件,在master上和在feature1上的版本是不同的,图中所示就是这个文件被Git修改标记成了这个样子
- 此时需要手动修改这个文件,将最终的修改结果保留到文件中
- 再次提交merge,会在master上再增长一个版本
在log中如何查看分支合并信息
git log --graph --pretty=oneline
3.5.7 保护现场
什么是保护现场,为什么要保护现场
从前面的分支操作过程可以看到,任意的切换分支,是可以的。
那么问题来了,切换分支以后,仓库分支是切换了,工作区和暂存区是否随着切换?
- 答案是否定的,工作区和暂存区只有一份,所有的仓库分支共用
如果所有的仓库分支共用一份工作区和暂存区,那么切换到新分支的时候,工作区和暂存区的状态,会影响新的分支,造成混乱。
解决工作区和暂存区在不同分之间共享的方法–保护现场
- 保护现场
- 列出保存过的现场
- 恢复现场
git stash
git stash list
git stash pop
pop以后,该现场就无效了,清理保存过的现场还有其他方式,请自行查看帮助文档
3.5.8 多人协作
前面提到过多人协作的方式。
使用中央服务器作为交换修改的服务器,平时自己工作在本地仓库中,需要时推送修改到远程仓库。
本地仓库是不需要联网的,远程仓库需要联网。
那么远程仓库相关操作:
查看远程仓库信息
git remote
git remote -v
拉取远程仓库的最新状态
git pull
推送本地分支的修改到远程仓库
git push [remote branch] [local branch]
master和dev是需要协同工作的,所以应该部署在远程仓库,作为远程仓库的两个分支
- origin/master
- origin/dev
如果远程有dev分支,那么本地建立对应分支的命令:
git checkout -b dev origin/dev
本地分支,不需要在远程仓库中建立分支
3.6 标签
标签的作用,是给某个特定的版本节点,添加一个可读的名字。否则所有的版本节点都用commit id标识,可读性很差。
比如master上的各个release版本,如果用标签标明,就很容易了解和做相应的处理。
3.6.1 添加
切换到想要添加标签的分支,通过以下命令
git tag [tag name] //为当前版本添加标签
git tag [tag name] [commit id] //为某个commit id版本添加标签
git tag -a [tag name] -m "tag information" //添加描述信息
git tag -s [tag name] -m "tag information" //用PGP签名
3.6.2 查看
查看标签的命令
git tag //查看有多少个标签
git show <tag name> //查看某个标签的说明信息
3.6.3 删除
删除标签的命令
git tag -d [tag name] //本地的
git push origin :refs/tags/[tag name] //远程的
3.6.4 推送
推送标签到远程的命令
git push origin [tag name]
git push origin --tags
3.7 Git配置
Git的配置一般通过Git配置命令外完成,最主要的有以下几个
- 颜色
git config --global color.ui true
- 忽略文件
- Git工作区根目录下建立文件.gitignore
- 具体配置方法,见说明
- 别名
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
/* 配置了这个以后,使用git lg 就可以了,就会使用上述配置来列出log */
- 配置信息作用域
- 有–global的,用户域有效
- 没有–global的,仓库域有效
- 具体反映在不同层级目录的.git/config
3.8 自己搭建Git服务器
- 使用ubuntu或者debian系统的电脑
- 安装git
- 创建一个名位git的用户,运行git服务
sudo addusr git
- 搜集公钥,每行一个写入
/home/git/.ssh/authorized_keys
- 创建裸仓,仓库目录下运行
sudo git init --bare <repo name>.git
sudo chown -R git:git sample.git
- clone远程仓库
git clone git@<server>:<path>/<repo name>.git
4. Github
4.1 环境准备
4.1.1 秘钥对
远程仓库登陆和使用需要用户的公钥,用户的签名需要私钥。所以需要生成一对公钥和私钥。
检查以下目录位置,是否已经生成了秘钥对
/*用户目录下 .ssh*/
.ssh/id_rsa //私钥
.ssh/id_rsa.pub //公钥
/*如果没有,通过以下命令生成秘钥对*/
ssh-keygen -t rsa -C "youremail@example.com"
Github配置
上传公钥
可以上传多个公钥,不同的电脑各生成一个,关联到同一个账户
4.2 远程仓库操作
4.2.1 关联
首先在Github上创建新的repository,然后在本地目录下通过下面命令,关联远程仓库到当前文件夹:
git remote add origin git@github.com:[username]/[repository].git
其中git@githu.com:xxx,是在github的repository页面找到的地址:
origin是远程仓库的分支名称。
4.2.2 clone
如果本地还没有任何相关的文件,那么从远程仓库直接克隆一个到本地比较干净。
通过以下命令:
git clone git@github.com:xxxx/yyyy.git
github地址参考4.2.1节的说明。
4.2.3 推送
推送命令:
git push -u origin master //第一次,用-u,后续不用
git push origin master //如果origin下还有分支,用origin/master, origin/dev
第一次推送,会有提示,需要确认以下对端是否可信
4.2 仓库间关系
github上,协作的工程有它的原是仓库,参与的人自己fork一份自己的仓库,在自己的仓库上修改。
4.1 使用步骤
- 找到项目页面
- 点fork复制到自己的页面
- git clone到本地
- 提交修改到自己的github仓库
- 提出pull request,发给项目原始页面,建议其包含这个修改