Git和Github

版本控制

1. 版本控制工具应该具备的功能

  1. 协同修改
    多人并行不悖的修改服务器端的同一个文件
  2. 数据备份
    不仅保存目录和文件的当前状态,还能够保存每一个提交过的历史状态
  3. 版本管理
    在保存每一个版本的文件信息的时候要做到不保存重复数据,以节约存储空间,提高运行效率
    这方面SVN采用的是增量式管理的方式,而Git采取了文件系统快照的方式
  4. 权限控制
    对团队中参与开发的人员进行权限控制
    对团队外开发者贡献的代码进行审核Git独有
  5. 历史记录
    查看修改人、修改时间、修改内容、日志信息
    将本地文件恢复到某一个历史状态
  6. 分支管理
    允许开发团队在工作过程中多条生产线同时推进任务,进一步提高效率

2. 版本控制介绍

集中式版本控制工具存在单点故障:CVS、SVN、VSS
分布式版本控制工具:Git、Mercurial、Bazaar、Darcs


Git简介

1. 简史

Linux系统开发的版本控制历史:

  1. 1991年,Linus本人手动合并代码
  2. 2002年,BitKeeper授权Linux社区免费使用版本控制系统,但要求不能进行破解
  3. 2005年,开发Samba的Andrew识图破解BitKeeper的协议,被BitMover公司发现,要收回Linux社区的免费使用权。然后,Linus自己用C语言开发一个分布式版本控制系统Git,主体程序开发完成只用了两周,一个月后Linux系统代码由Git管理,Git官网https://git-scm.com
  4. 2008年,GitHub上线

2. 优势

  1. 大部分操作在本地完成,不需要联网
  2. 完整性保证
  3. 尽可能添加数据而不是删除或者修改数据
  4. 分支操作非常快捷流畅
  5. 与Linux命令全面兼容

3. Git安装

Git下载链接https://git-scm.com/download/win

  1. 安装目录为非中文无空格目录
  2. "Use Vim as Gits default editor",建议使用Vim编辑器
  3. "Use Git from Git Bash only",完全不修改PATH环境变量,仅在Git bash中使用Git这是最安全的选择,建议使用这种方式
  4. 其他选项默认即可

4. Git结构

分3个区,用于写代码的工作区,临时存储的暂存区,用于存放历史版本的本地库
Git结构

5. Git和代码托管中心

代码托管中心的职责:维护远程库,更加方便团队或者跨团队协作

  1. 局域网环境下:GitLab服务器
  2. 外网环境下:GitHub或者码云等等

6. 本地库和远程库

  1. 团队内部协作
    团队内部协作
  2. 跨团队协作
    跨团队协作

Git操作

1. 本地库初始化

# 初始化,生成.git文件夹
git init 
# .git目录中存放的是本地库相关的子目录和文件,不需要删除且谨慎修改

2. 设置签名

用来区分不同开发人员的身份,但这里的签名和登陆远程库(代码托管中心)的账号和密码没有任何关系,需要设置用户名、邮箱等

  1. 项目级别/仓库级别:仅在当前本地库范围内有效
    修改的是项目目录下文件.git/config,执行命令前后可以使用cat .git/config查看配置文件的内容,添加了配置[user]。例如用户名为fqj、邮箱为fqj@163.com
git config user.name fqj
git config user.email fqj@163.com  # 邮件可以不设置,但是用户名必须设置
  1. 系统用户级别:登录当前操作系统的用户范围
    修改的是文件~/.gitconfig(可能不存在),执行命令前后可以使用cat ~/.gitconfig查看配置文件的内容,添加了配置[user]。例如用户名为xufeiqiong、邮箱为xufeiqiong@163.com
git config --global user.name xufeiqiong
git config --global user.email xufeiqiong@163.com

级别优先级

  1. 就近原则:项目级别优先于系统用户级别,二者都有时采用项目级别的签名
  2. 如果只有系统用户级别的签名,就以系统用户级别的签名为准
  3. 如果二者都没有,则不合法

3. 基本操作

1. 状态、添加、提交

# 1. 状态查看
git status # 查看工作区和暂存区的状态
# 2. 添加
git add 文件名 # 将工作区的新建或者修改添加到暂存区
# 3. 提交
git commit -m 本次提交的描述  文件名  # 将暂存区的内容提交到本地库

2. 日志与回滚

1. 查看日志

# 1. 提交日志信息
git log
git log --pretty=oneline
git log --oneline
git reflog
# 2. 整个提交历史记录,但跳过合并
git log --no-merges
# 3. 提交更改include/scsi或drivers/scsi子目录中的任何文件的所有提交
git log master include/scsi drivers/scsi 
# 4. 最近两周的更改文件gitk,注意: --是必要的,以避免与名为gitk的分支混淆
git log --since="2 weeks ago" -- gitk
# 5. 显示“test”分支中尚未在“release”分支中的提交,以及每个提交修改的路径列表
git log --name-status release..test
# 6. 显示更改builtin/rev-list.c的提交,包括在文件被赋予其现有名称之前发生的提交
git log --follow builtin/rev-list.c
# 7. 显示在任何本地分支中的所有提交,但不包括任何远程跟踪分支机构的起始点(origin不具有)
git log --branches --not --remotes=origin
# 8. 显示本地主服务器中的所有提交,但不显示任何远程存储库主分支
git log master --not --remotes=*/master
# 9. 显示历史,包括变化差异
git log -p -m --first-parent
# 10. 显示文件main.c中的函数main()随着时间的推移而演变
git log -L '/int main/',/^}/:main.c
# 11. 显示最近三次的提交
git log -3
# 12. 根据提交ID查询日志(commit_id可以是提交哈希值的简写模式,也可以使用HEAD代替)
git log commit_id # 查询ID之前的记录
git log commit1_id commit2_id # 查询commit1与commit2之间的记录
git log commit1_id..commit2_id # 同上,但是不包括commit1
    # HEAD代表最后一次提交,HEAD^为最后一个提交的父提交,等同于HEAD~1,HEAD~2代表倒数第二次提交
    # --pretty按指定格式显示日志信息,可选项有:oneline,short,medium,full,fuller,email,raw以及format:,默认为medium,可以通过修改配置文件来指定默认的方式
    # git log (--pretty=)oneline
    # git log --pretty=format:"%an %ae %ad %cn %ce %cd %cr %s" --graph
    # 常见的format选项:
        # %H      提交对象(commit)的完整哈希字串
        # %h      提交对象的简短哈希字串
        # %T      树对象(tree)的完整哈希字串
        # %t      树对象的简短哈希字串
        # %P      父对象(parent)的完整哈希字串
        # %p      父对象的简短哈希字串
        # %an     作者(author)的名字
        # %ae     作者的电子邮件地址
        # %ad     作者修订日期(可以用 -date= 选项定制格式)
        # %ar     作者修订日期,按多久以前的方式显示
        # %cn     提交者(committer)的名字
        # %ce     提交者的电子邮件地址
        # %cd     提交日期
        # %cr     提交日期,按多久以前的方式显示
        # %s      提交说明

2. 回滚

回滚与前进的方式有3种:

  1. 基于索引值(推荐
  2. 基于指针HEAD(只能后退)
    1. 使用"^"符号
    2. 使用"~"符号
# 1. 基于索引值操作[推荐]
git reset --hard [局部索引值,例如a6ace91] # 如果省略指针,则默认是最新的那个指针
# 2. 使用^符号:只能后退
git reset --hard HEAD^  # 一个^表示后退一步,n个表示后退n步
# 3. 使用~符号:只能后退
git reset --hard HEAD~n # 表示后退n步

# reset命令的三个参数对比
--soft # 仅仅在本地库回滚
--mixed # 在本地库和暂存区回滚
--hard # 在本地库、暂存区和工作区回滚
# 可以使用git help reset查看本地帮助文档

3. 删除与比较

找回删除文件的前提是数据已经提交到本地库

# 1. 删除文件并找回
#【删除前,文件存在时的状态已经提交到了本地库,否则无法找回】
# 删除操作已经提交到本地库:指针位置指向历史记录
# 删除操作尚未提交到本地库:指针位置使用 HEAD
git reset --hard 指针位置

# 2. 比较文件差异
git diff 文件名  # 将工作区中的文件和暂存区进行比较,不带文件名则比较所有文件
git diff [本地库中历史版本] [文件名] # 将工作区中的文件和本地库历史记录比较,不带文件名比较多个文件
# 删除行用-表示;增加行用+表示;修改行则先-原来的行,再+修改后的行;没有区别则不显示

4. 分支管理

在版本控制过程中,使用多条线同时推进多个任务
分支管理
优势:

  1. 同时并行推进多个功能开发,提高开发效率
  2. 各个分支在开发过程中,如果某一个分支开发失败(失败的分支删除重新开始即可),不会对其他分支有任何影响

对分支的操作:

# 1. 创建分支
git branch 分支名
# 2. 查看分支
git branch -v
# 3. 切换分支
git checkout 分支名
# 4. 合并分支:总共分两步
    # 4.1 切换到接受修改的分支(被合并,增加新内容)上 
    git checkout 被合并分支名
    # 4.2 执行merge命令
    git merge 有新内容分支名
# 5. 解决冲突
    # 5.1 编辑文件,删除特殊符号:“<<<<<< HEAD”到“>>>>>>>> 被合并分支名”的部分
    # 5.2 把文件修改到满意的程度,保存退出
    # 5.3 git add 文件名
    # 5.4 git commit -m "日志信息"   【注意:commit一定不能带具体文件名】

合并分支存在冲突时的表现:
合并分支存在冲突

5. 实操记录

# 首先需要进入工作目录,例如我的工作目录为/c/gitserver,即工作区
mkdir -p /c/gitserver && cd /c/gitserver
# 假设开发的程序或者项目名字为WebEE,则需要创建项目目录
mkdir WebEE && cd WebEE 

# 初始化,生成.git文件夹
git init
git config user.name fqj
git config user.email fqj@163.com  # 邮件可以不设置,但是用户名必须设置 
# .git目录中存放的是本地库相关的子目录和文件,不需要删除且谨慎修改
# -----------------------
# 命令执行结果:
$ git init
Initialized empty Git repository in C:/gitserver/WebEE/.git/
#------------------------

# 添加文件到工作区,例如添加说明文件
echo -e "这是个说明文件!\n就这么多吧" > Readme.md  # 此时文件处于Untracked状态,即只存在工作区,可通过git status查看状态
# -----------------------
# 命令执行结果:
$ echo -e "这是个说明文件!\n就这么多吧" > Readme.md

$ cat Readme.md
这是个说明文件!
就这么多吧

$ git status
On branch master

No commits yet

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

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

# 将文件提交到暂存区
git add Readme.md  # 将文件存到暂存区,此时git status会显示new file:Readme.md
# 告警"warning: LF will be replaced by CRLF in Readme.md.",不需要理会,这是Linux和windows标识换行的符号不同导致的
# git rm --cached Readme.md  # 从暂存区删除
# -----------------------
# 命令执行结果:
$ git add Readme.md
warning: LF will be replaced by CRLF in Readme.md.
The file will have its original line endings in your working directory

$ git status
On branch master

No commits yet

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



# 将文件提交到本地库
git commit -m "This is first commit" Readme.md  # -m后面添加本次提交的注释信息
# 如果在命令行中不添加-m参数,则git commit会直接调用vim编辑器,然后输入描述信息
# 如果注释信息比较少,建议-m;如果注释信息比较多,则可以不加-m,直接让其调用vim编辑器之后进行添加
# -----------------------
# 命令执行结果:
$ git commit -m "This is first commit" Readme.md
warning: LF will be replaced by CRLF in Readme.md.
The file will have its original line endings in your working directory
[master (root-commit) 66c9779] This is first commit
 1 file changed, 2 insertions(+)
 create mode 100644 Readme.md

$ git status
On branch master
nothing to commit, working tree clean

#------------------------

# 修改Readme.md之后再添加
echo -e "\n\t第二次修改的内容!" >> Readme.md
# ---------------
# 命令执行结果
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   Readme.md

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

# 修改文件之后再次提交
git add Readme.md
git commit -m "this is second commit" Readme.md
# ---------------
# 命令执行结果
$ git add Readme.md
warning: LF will be replaced by CRLF in Readme.md.
The file will have its original line endings in your working directory

$ git commit -m "this is second commit" Readme.md
warning: LF will be replaced by CRLF in Readme.md.
The file will have its original line endings in your working directory
[master 6ae89cc] this is second commit
 1 file changed, 2 insertions(+)
#--------------------

# 第3次修改后直接提交
#【适合已经提交过的文件,新建的文件无法使用此种跳过git add的方法】
echo -e "\n\t第3次修改的内容!" >> Readme.md
git commit -a -m "3" 
# --------------
# 命令执行结果
$ echo -e "\n\t第3次修改的内容!" >> Readme.md
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   Readme.md

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

$ git commit -a -m "3"
warning: LF will be replaced by CRLF in Readme.md.
The file will have its original line endings in your working directory
[master 5650822] 3
 1 file changed, 2 insertions(+)
#------------------

# 查看日志
git log
# --------------
# 命令执行结果
$ git log
commit 5650822aba9ee9c38f1c059f8502a1ab25fcfd00 (HEAD -> master)
Author: fqj <fqj@163.com>
Date:   Tue May 25 22:46:44 2021 +0800

    3

commit 6ae89ccc296144bff06f50c302ee350a950b8318
Author: fqj <fqj@163.com>
Date:   Tue May 25 22:41:26 2021 +0800

    this is second commit

commit 66c9779f4156ed49841dc78766562e9a36171467
Author: fqj <fqj@163.com>
Date:   Tue May 25 22:30:58 2021 +0800

    This is first commit
#------------------

# 如果日志过多导致需要分页查看,不方便,可以使用参数让每条日志显示在一行
git log --pretty=oneline
# --------------
# 命令执行结果
$ git log --pretty=oneline
5650822aba9ee9c38f1c059f8502a1ab25fcfd00 (HEAD -> master) 3
6ae89ccc296144bff06f50c302ee350a950b8318 this is second commit
66c9779f4156ed49841dc78766562e9a36171467 This is first commit
#------------------

git log --oneline  # 只显示哈希值的前8位
# --------------
# 命令执行结果
$ git log --oneline
5650822 (HEAD -> master) 3
6ae89cc this is second commit
66c9779 This is first commit
#------------------

git reflog  # 显示指针,即移动次数n,格式HEAD@{移动到此版本需要多少步}
# --------------
# 命令执行结果
$ git reflog
5650822 (HEAD -> master) HEAD@{0}: commit: 3
6ae89cc HEAD@{1}: commit: this is second commit
66c9779 HEAD@{2}: commit (initial): This is first commit
#------------------

# 回滚1步
git reset --hard 6ae89cc
# 或者git reset --hard HEAD^
# 或者git reset --hard HEAD~1
# --------------
# 命令执行结果
$ git reset --hard 6ae89cc
HEAD is now at 6ae89cc this is second commit

$ cat Readme.md
这是个说明文件!
就这么多吧

        第二次修改的内容!
#------------------

# 回到当前状态
git reset --hard 5650822

# 增加行对比
git diff Readme.md
# --------------
# 命令执行结果
$ git diff Readme.md
diff --git a/Readme.md b/Readme.md
index efbac46..7963a01 100644
--- a/Readme.md
+++ b/Readme.md
@@ -4,3 +4,4 @@
        第二次修改的内容!

        第3次修改的内容!
+       测试diff

Git基本原理

1. 哈希

哈希是一个系列的加密算法,各个不同的哈希算法虽然加密强度不同,但是有以下几个共同点:

  1. 不管输入数据的数据量有多大,输入同一个哈希算法,得到的加密结果长度固定
  2. 哈希算法确定,输入数据确定,输出数据能够保证不变;输入数据有变化,输出数据一定有变化
  3. 哈希算法不可逆

Git底层采用的是SHA-1算法,哈希算法可以被用来验证文件(Git就是靠这种机制来从根本上保证数据完整性的),原理如下:
哈希

2. Git保存版本的机制

Git的文件管理机制

Git把数据看作是小型文件系统的一组快照。每次提交更新时,Git都会对当前的全部文件制作一个快照并保存这个快照的索引。为了高效,如果文件没有修改,Git不再重新存储该文件,而是只保留一个链接指向之前存储的文件,所以Git的工作方式可以称之为快照流
Git的文件管理机制

Git文件管理机制细节

  1. Git的提交对象
    Git的提交对象
  2. 提交对象及其父对象形成的链条
    提交对象及其父对象形成的链条

集中式版本控制工具(例如SVN)的文件管理机制

以文件变更列表的方式存储信息。这类系统将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异
集中式版本控制工具

3. Git分支管理机制

  1. 分支的创建
    分支的创建
  2. 分支的切换
    分支的切换
    testingcommit
    mastercommit

GitHub

github创建账号时,尽量不要使用163的邮箱,因为163的邮箱有时候收不到github的邮件

1. 账号信息

岳不群:yuebuqun@qq.com
令狐冲:linghuchong@qq.com
东方不败:eastsuccess@qq.com

2. 创建远程库和本地库

  1. 创建远程库
    远程库需要在github上创建,点击右上角的加号,选择New respository,填写相应的Repository name(例如huashan),设置相应的参数,最后点击Creat repository
  2. 创建本地库
    在本地执行以下命令:
git remote -v # 查看当前所有远程地址别名
git remote add 别名 远程地址
# 例如
git remote add huashan_origin https://github.com/atguigu2021ybuq/huashan.git

3. 推送

git push 别名 分支  # 然后需要在弹窗上输入github的账户密码进行验证
# 例如
git push huashan_origin master  # 然后在远程库上刷新后就可以看见了

4. 克隆

克隆一个命令可以完成以下动作:

  1. 完整的把远程库下载到本地
  2. 创建origin远程地址别名
  3. 初始化本地库
git clone 远程地址
# 例如
git clone https://github.com/atguigu2021ybuq/huashan.git

5. 团队成员邀请

点击项目--Settings--Collaborators--然后输入邀请成员的github账号--然后复制邀请链接发送给受邀成员(此时系统会自动发送邮件)--受邀成员打开链接--点击Accept invitation--此时便可执行推送git push huashan_origin master

6. 拉取

pull=fetch+merge

git fetch 远程库地址别名 远程分之名  # 仅仅把远程库的文件下载到本地,并不修改本地库内容
git merge 远程库地址别名 远程分之名  # 把fetch的文件与本地文件合并
git pull 远程库地址别名 远程分之名
# 例如
git fetch huashan_origin master
git merge huashan_origin master

7. 解决冲突

如果不是基于 GitHub 远程库的最新版所做的修改,不能推送,必须先拉取。拉取下来后如果进入冲突状态,则按照“分支冲突解决”操作解决即可

8. SSH登陆

因为https每次push都需要输入用户名和密码,但是ssh可以免密登陆,方便push

# 进入当前用户的家目录
cd ~
# 删除.ssh目录
rm -rvf .ssh
# 运行命令生成.ssh密钥目录
ssh-keygen -t rsa -C atguigu2021ybuq@aliyun.com # 这里-C这个参数是大写的
# 进入.ssh目录查看文件列表
cd .ssh
ls -lF
# 查看id_rsa.pub文件内
cat id_rsa.pub
复制id_rsa.pub文件内容,登录GitHub,点击用户头像--Settings--粘贴到SSH and GPG
keys的Key中--Title自己定义--点击Add SSH Key
# 回到 Git bash 创建远程地址别名
git remote add huashan_ssh git@github.com:atguigu2021ybuq/huashan.git
# 推送文件进行测
git push huashan_ssh master 

Git工作流

在项目开发过程中使用Git的方式管理

1. 分类

  1. 集中式工作流
    像SVN一样,集中式工作流以中央仓库作为项目所有修改的单点实体。所有修改都提交到Master这个分支上。这种方式与SVN的主要区别就是开发人员有本地库。Git很多特性并没有用到。
  2. GitFlow工作流
    Gitflow工作流通过为功能开发、发布准备和维护设立了独立的分支,让发布迭代过程更流畅。严格的分支模型也为大型项目提供了一些非常必要的结构。
  3. Forking工作流
    Forking工作流是在GitFlow基础上,充分利用了Git的Fork和pull request的功能以达到代码审核的目的。更适合安全可靠地管理大团队的开发者,而且能接受不信任贡献者的提交

2. GitFlow工作流详解(不同分支介绍)

  1. 主干分支master
    主要负责管理正在运行的生产环境代码,永远保持与正在运行的生产环境完全一致
  2. 开发分支develop
    主要负责管理正在开发过程中的代码,一般情况下应该是最新的代码
  3. bug修理分支hotfix
    主要负责管理生产环境下出现的紧急修复的代码。 从主干分支分出,修理完毕并测试上线后,并回主干分支。并回后,视情况可以删除该分支
  4. 准生产分支(预发布分支)release
    较大的版本上线前,会从开发分支中分出准生产分支,进行最后阶段的集成测试。该版本上线后,会合并到主干分支。生产环境运行一段阶段较稳定后可以视情况删除
  5. 功能分支feature
    为了不影响较短周期的开发工作,一般把中长期开发模块,会从开发分支中独立出来。 开发完成后会合并到开发分支

GitLab服务器搭建

参考博客<Gitlab部署及汉化>