Git使用教程

一、前言

1、简介

git仓库每个节点都是一个独立的版本管理节点,当不需要和其他节点同步时,可以在本地仓库独立开发。而当需要和其他节点同步信息时即多人协作开发,就需要一个中心仓库(Github、Gitee、Gitlab等),合并其他节点的提交,另外的节点再去中心仓库拉取自己需要的其他节点的提交,简而言之,git是一个分布式版本管理系统。

2、名词解释

  • 工作区( Working Directory)

    工作区也就是你系统的文件目录,所有修改的东西首先存在你的工作区。这时候的状态是untracked(未追踪)。

  • 暂存区(stage/index)

    暂存区是一个添加到版本之前的缓冲区,通过git add 把工作区的修改加到暂存区,状态为Changes to be committed(要提交的变更)。如果已经添加到版本控制的文件对它进行修改也需要git add 重新将修改更新到暂存区。

  • 版本库(commit)

    每一个commit都是一个回溯点,通过git commit把暂存区的东西加到版本库。这时候HEAD指针会指向最新的commit。此时状态为nothing to commit(没有需要提交的东西,暂存区是干净的)。

  • 分支(branch)

    分支是以commit节点为基础形成的一个概念,可以把整个版本库理解为一棵树,每个commit就是一个节点,把节点串起来的就是分支,就像树枝一样。当任务进行到一个阶段,需要朝不同方向进行时,这时候就需要以某个节点为基础切换分支,就像树一样分叉。当有一天两个方向需要共同前进,这时候就可以把两个分支合并。

二 、准备工作

安装git客户端工具,准备一个远程托管仓库Github或公司搭建的局域网Gitlab。

1、常用配置

1
2
3
git config --global user.name "your name"      #配置用户名和邮箱
git config --global user.email "your email"
git config --global core.editor vim #配置commit-msg的编辑器,默认编辑器个人觉得不太好用,可以改成自己习惯的编辑器

2、SSH

git服务器一般使用https和ssh协议,如果使用https协议,客户端在和服务器交互时,比如pull和push就需要填写用户名和密码非常麻烦,所以一般如果长期要用整个服务器的话还是使用ssh协议,把自己的SSH密钥添加到服务器,就可以不用每次输出用户名和密码。

1
ssh-keygen -t rsa -C "your.email@example.com" -b 4096

连续回车三次

1
cat ~/.ssh/id_rsa.pub #如果生成过密码就不需要重新生成

复制密钥,然后去git服务器添加到你的用户设置里面,就可以直接使用ssh认证方式。

三、开发流程

1、初始化仓库

  • 本地已经存在工程

登陆git服务器,创建远程仓库(test)。然后把本地已经存在的项目(没有建立版本库)新建一个版本仓库,初始化提交,最终推送到远程仓库。

1
2
3
4
5
6
cd basedir
git init
git remote add origin git@192.168.16.100:liurui/test.git #远程仓库的ssh链接
git add .
git commit -m "Initial commit"
git push -u origin master

git push [远程仓库] [本地分支]:[远程分支],这是push的一般用法,远程仓库就是git remote add 时添加的远程链接的别名,可以任意。这里push时省略了远程分支,表示默认推送到本地master分支关联的上游分支,如果上游仓库没有整这个分支就会自动创建一个。

  • 本地没有原始工程创建新的仓库

1
2
3
4
5
6
git clone git@192.168.16.100:liurui/test.git
cd test
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master:dev

从远程直接把原始仓库下载到本地。

  • 已经存在本地仓库

1
2
3
4
5
cd existing_repo
git remote rename origin old-origin
git remote add origin git@192.168.16.100:liurui/test.git
git push -u origin --all
git push -u origin --tags

当需要将仓库的上游服务器换成新的时,可以先增加新的远程仓库,然后把本地的仓库全部推送到新建的远程。

2、 日常维护

  • 单人开发

一个人开发的话就不需要考虑合并别人的代码,但是需要考虑同时开发多个功能的问题。这时候就不能直接在master分支开发,需要根据指定commit或者分支切换新的开发分支。等该分支的任务完成之后就可以合并到远程仓库,并删除本地的开发分支(避免本地分支越来越多,忘记当前分支的合并清空)。然后开发新功能时,先从远程更新本地master分支,在从master切开发分支继续开发。

流程图如下:

1
2
3
4
5
6
7
8
9
git fetch origin  #当前位于master分支,更新远程仓库到本地
git rebase origin/master #以rebase形式合并远程master到本地master
git checkout -b dev #默认从当前分支HEAD切出新分支
#do something
git add -u #-u是指只把modified的修改加到暂存区,不会把工作区的修改加进去
git commit -m "你的版本记录信息" # -m 用于简短描述,复杂描述不用-m
git fetch origin #push之前一定要先从远程拉取最新的提交到本地分支
git rebase origin/master
git push origin master

我这里采取git fetch和git rebase的方式更新远程代码,也可以使用git pull origin –rebase的方式,pull=fetch+merge,pull –rebase=fetch+rebase。fetch只会把远程最新的commit记录更新到.git/refs/remotes/里面,不会更新本地提交。只有merge才会合并到本地分支。

rebase的意思是变基,也就是把自己基线设置到指定分支的HEAD,然后把自己新增的commit放到新起点的后面。下面是不同操作的示意图:

合并前:

merge合并:

rebase合并:

明显看到merge会多生成一个commit节点,分支树会更加复杂一些,优点是保留了分支自己的提交历史,合并冲突比较简单,合并完了直接重新commit就行。rebase不会生成多余的merge提交,维护起来更加简单,但是合并冲突会复杂一点,一般合并完了需要git rebase –continue。

  • 多人开发

    • 非fork模式

多人开发,就需要注意每个人代码的合并问题,一般采取所有人维护master主干,开发的时候在各自的分支开发,然后合并到master主干分支上,提交前注意git pull 更新最新的代码,因为你的代码提交可能落后master。这种情况适用于共同维护一个远程仓库。

1
2
3
4
5
git fetch origin #当前位于master分支,更新远程仓库到本地
git rebase origin/master #以rebase形式合并远程master到本地master
git checkout dev
#do something and commit
git push origin liurui:liurui/dev

说明:git rebase master 可以理解为合并master分支上进度到当前自己的分支,如果没有冲突,那么直接git push,如果存在冲突,那么根据提示去相应的文件,手动合并冲突,合并完了执行git rebase –continue。git push origin liurui:liurui/dev 其中liurui是你的本地分支名,liurui/dev是你的远程分支名。然后去网站上提交合并申请,然后有master合并权限的用户审核代码,他可以合并本次提交,也可以不合并。

  • fork模式

这种模式适用于fork原始仓库,更新维护自己仓库,并将自己的仓库的更新提交merge request到主干仓库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
git clone xxxxxxxx #克隆自己的仓库到本地
git remote add upstream xxxxxxxxx #将主干仓库添加到自己git仓库中
git remote -v #查看所有远程仓库地址
#修改代码
git diff #比较工作区和暂存区之间的变化 --filename 比较指定的文件差异
git add -u #-u是为了只将已经在暂存区的内容的修改加到暂存区
git diff --cached #比较的是暂存区和分支最新版本
git commit -m "message"
git fetch upstream
git rebase upstream/master
#如果出现冲突,先手动解决冲突
git add -u
git rebase --continue
#如果没有冲突,那么直接push即可
git push origin master #默认将本地master分支push到远程的master分支
git push origin master:dev #将本地master分支push到远程dev分支
#最后去网站上提交合并请求

四、常用功能示例

1、删除具体某个commit

假设功能开发都是在master一个分支进行,这样同时开发几个功能时只能串行提交。假设有一个merge有问题并且包含多个commit,被拒绝合并到主仓库。那么本地需要删除这个commit,然后重新提交。假设有三个commit A,B,C。需要删除B。

1
git rebase -i commit-A-ID #需要删除的前一个commit的id

输出如下

1
2
pick commit-B-ID message
pick commit-C-ID message

将B对应的pick改为drop保存退出即可。

这里也可以采取拉新的分支,先将commit-C导出成patch,然后拉取一个分支到commit-A节点,然后在新的分支合并patch。代码如下:

1
2
3
4
git format-patch -1 commit_id #-1是指只导出该commit的patch
git checkout -b newbranchname commit-A
git apply --check 0001-test.patch #检测patch是否可以合并
git apply 0001-test.patch #合并patch

2、版本维护

在大型项目中,需要对某一个节点设置一个版本,用于发布,就可以使用git tag相关指令

1
2
3
4
git tag #显示所有tag
git show tagname #显示某个tag的具体commit信息
git tag tagname #给最新的commit打tag
git tag tagname commitid #给指定commit打tag,比如遗漏了版本

3、从版本库中删除文件

1
2
3
git rm file #从版本库中删除文件并且删除本地文件
git rm file --cache #只从版本库中删除文件,但保留本地文件
git rm -r floder #从版本库中删除整个文件夹

4、添加忽略文件

注意事项

添加忽略文件是为了将开发中产生的一些中间文件排除在版本管理之外,方便其他人维护更新。忽略文件只能忽略尚未添加到版本管理中的文件和文件夹。

添加方法

在项目根目录下打开gitbash,创建文件 touch .gitignore,然后编辑文件。

规则

  • 空格不匹配任意文件,可作为分隔符,可用反斜杠转义

  • #开头的模式标识注释,可以使用反斜杠进行转义

  • ! 开头的模式标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义

  • / 结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件

  • / 开始的模式匹配项目跟目录

  • 如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录

  • *匹配多级目录,可在开始,中间,结束

  • ?通用匹配单个字符

  • []通用匹配单个字符列表

常用示例

  • 忽略指定文件

    project.pro.user 这种方法会忽略根目录下的该文件,其他路径下的同名文件不会忽略

  • 忽略特定后缀的文件

    *.o

  • 忽略文件夹及文件夹下的所有内容

    build/

  • 忽略某一文件夹下的所有内容但是不忽略特定内容

    build/

    !build/temp/

5、导出补丁合并补丁

有些场景我们并不能直接提交代码到远程仓库,只能通过补丁的形式,将自己修改的代码合并到仓库当中。有两种形式diff文件和patch文件,diff没有commit信息,而patch存在commit信息,一个commit对应一个patch。

1
2
3
4
5
6
#生成补丁的两种方法
git diff commitID >test.diff
git format-patch commitID -n #n是从该commit到第n个commit
#导入补丁
git apply test.diff
git apply test.patch

6、合并其他分支的commit

1
2
git cherry-pick commitid -e //合并指定的commit,-e表示重新填写提交信息不带-e的话默认使用原始提交信息。
git cherry-pick master //合并master分支最新的commit到当前分支,这里还有很多复杂的用法比如指定master分支的倒数第几个

7、不同情况下新建分支

1
2
3
4
5
git checkout -b name   //从当前分支最新提交新建一个分支
git branch -r //查看上游分支
git checkout -b name upstream/branch //从上游指定分支创建一个本地分支并切换
git checkout -b name commitid //从指定的commit创建一个新分支
git branch name tagname //从指定tag号创建一个新分支,需要手动切换到对应分支上

8、查看代码提交信息

1
2
git blame file   #查看该文件每一行的提交信息
git blame -L 2,4 file #查看文件的2-4行提交信息

结果如下图所示:

前面是commit-id,后面是提交者,时间以及具体代码段

9、查看某个commit所在分支

1
git branch -r --contains commitid

命令会列举出本地个远程仓库里面包含这个commit的所有分支

10、修改本地分支和远程分支映射关系

1
2
git branch -vv   #查看分支映射关系
git branch --set-upstream-to=upstream/master master #设置upstream/master 和本地master映射

11、查看修改

1
2
3
git diff #查看工作区修改
git diff --cached #查看暂存区修改
git diff HEAD #查看当前最新的版本提交修改

12、重置代码

1
2
3
git reset HEAD~1 --soft   #将HEAD指向上一个commit,并且当前commit的修改仍然保留在暂存区。
git reset HEAD~1 --hard #将HEAD指针指向上一个commit,并且会清空commit对应的修改,暂存区和工作区都不会保留。
git reset HEAD~1 --mixed #将HEAD指针指向上一个commit,会将commit对应的修改从版本里面去掉,但是在工作区保留该修改。

13、gerrit使用

1
2
3
4
git fetch origin 
git rebase origin/master
#push之前先更新当前分支到最新
git push origin HEAD:refs/for/master

这里origin是指gerrit上游,如果仓库里面有多个上游需要注意。HEAD表示最前面的提交,所以如果当前存在多个未合并的提交,会把几个提交一起
推送到gerrit,然后会存在依赖关系,合并前面的才能继续合并后面的,所以如果同时开发多个功能最好单独切分支。refs/for的作用表示合并前需要review,如果是refs/heads就不用review。master表示你要推送的分支。


Git使用教程
http://yoursite.com/2019/04/08/Git使用教程/
作者
还在输入
发布于
2019年4月8日
许可协议