首页 > 技术文章 > git入门教程

JiangLiHong 2021-06-12 02:43 原文

 

前言:随着项目的越做越大,团队协作编程也慢慢变成家常便饭,“版本管理系统”(Version Control System)也渐渐走进开发工程师的视野当中,而眼下最流行的"版本管理系统",非git莫属。

 

git是什么?

git是一个开源的分布式版本控制系统(distributed version control system)

分布式版本系统没有绝对的所谓中央服务器,每个人的电脑上都有一个版本库,使用时无需联网,在本地修改就行。多人协作下需要将不同电脑下的相同文件进行交换修改,而这个交换的工作是由一台充当“中央服务器”的电脑(server computer)来完成,本质上来看,“中央服务器”电脑的作用也仅仅是方便交换,没有它,不同电脑之间也能实现交换修改。与集中式版本控制系统相比,分布式版本控制系统最大的特点是:

  1. 分布式每个人的电脑上都可以直接拷贝完整的代码版本,而集中式只能拷贝自己需要的。

  2. 分布式的服务器挂掉之后,不会影响工作。而集中式的服务器挂掉之后,根本就没法进行工作。

pic

 

 

 

 

git的组成结构

大家都知道,在 Git 系统中有 “三棵树” 的概念。

用途
Repository \ HEAD(版本库) 上一次提交的快照,下一次提交的父结点
Index(暂存区) 预期的下一次提交的快照
Working Directory(工作区) 沙盒

HEAD HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,可以把 HEAD 看做你的上一次提交的快照。可以简单理解为: HEAD 指向分支(branch),分支指向提交。

Index Index(索引,或暂存区)是你预期的下一次提交。这就是当你运行 git commit 时 Git 看起来的样子。Git 将上一次检出到工作目录中的所有文件填充到 Index,之后你会将其中一些文件替换为新版本,接着通过 git commit 将它们转换为树来用作新提交。

工作目录 另外两棵树以一种高效但并不直观的方式,将它们的内容存储在 .git 文件夹中。工作目录会将它们解包为实际的文件以便编辑。 你可以把工作目录当做 “沙盒”,在你将修改提交到暂存区并记录到历史之前,可以随意更改。

git的分支(Branch)与合并(Merge)

相比同类软件,Git有很多优点。其中很显著的一点,就是版本的分支(branch)和合并(merge)十分方便。有些传统的版本管理软件,分支操作实际上会生成一份现有代码的物理拷贝,而Git只生成一个指向当前版本(又称"快照")的指针,因此非常快捷易用。

 

分支:从主线(master)上分离出来进行另外的操作,而不影响主线的一条支线,操作完后分支又可以将结果合并到主线上,在保证主 线代码/进程不被影响的情况下完成一些临时的开发。而分支的创建不需要将主线数据全部复制,只需要创建一个分支的指针指向 对应的版本。

 

切换分支

切换分支时git实际上做了两件事

  1. 修改了HEAD指针的指向

  2. 更改了暂存区和工作区的文件内容

 

分支后合并的冲突问题

如果要合并的两条线对同一个文件有不同的修改,则在合并时会出现冲突报错,因为git不能擅自决定舍弃哪条线的修改

解决方案

image-20210608231054438

  1. 删除错误文件中git的自动标记

  2. 添加到暂存区

  3. 将暂存区的文件提交到版本库(此时不能在-m“提交日志”后面增加提交的文件名

 

checkout命令与匿名分支

如果git checkout 命令后面没有接分支名,则会创建一个匿名分支,在由匿名分支切换到其他分支后,对匿名分支的操作,提交都会被丢弃,可以用来做一些实验性的代码,不会对原有的分支产生影响

 

 

合并(Merge)

 

无参数默认情况下,Git执行"快进式合并"(fast-farward merge)

 

在使用--no-ff参数后,会执行正常合并,会在master分支上生成一个新节点 git merge --no-ff 分支名

 

git的命令行操作

 

本地化操作

  1. 本地库初始化

    mkdir 文件名 新建文件夹

    cd 文件名/ 进入该文件目录

    cd .. 退回一级目录

    cd ~ 返回根目录

    git init 在当前目录初始化git仓库

    pwd 查看当前目录

    ll 罗列出当前文件或目录的详细信息,是ls-l的别名

    ls-la 在上面的基础上显示出隐藏文件信息

    cat 文件名 查看该文件

    vim 文件名 新建该文件

    git help 具体命令 调出与该问题相关的帮助手册

     

  2. 设置签名

    签名的不同级别

    • 项目级别/仓库级别签名 : 只在当前本地库下该签名有效

      git config user.name 用户名 设置签名用户名

      git config user.email 邮箱 设置签名邮箱

    • 系统用户级别签名 : 只要系统用户不变,该用户下的所有仓库该签名都有效

      git config --global user.name 用户名 设置签名用户名

      git config --global user.email 邮箱 设置签名邮箱

     

  3. 添加,提交,查看状态,比较

    git status 查看工作区与暂存区的状态

    git add 文件名 将工作区的该文件的新建/修改添加到暂存区

    git add *.c 将该目录下的所有.c文件加入暂存区

    git add . 将目录下的所有文件添加到暂存区

    git rm --cached 文件名 将暂存区的该文件恢复到未跟踪的状态

    git reset 文件名 将暂存区的该文件恢复到未添加的状态

    git rm 文件名 将工作区的该文件删除,暂存区保持不变

    git commit -m “提交备注” 将暂存区的所有文件提交到版本库

    git commit -a -m"提交备注" 将工作目录已跟踪的文件一步添加并提交

    git commit 文件名 将该文件由暂存区提交到版本库,会进入vim编辑器要求你填写提交信息

    git commit --amend 漏提交文件用该命令,会覆盖上一次提交

    image-20210611231511372

    git rebase 新基分支名 以新基为基改变分支的原有基

    git diff 文件名 将工作区的该文件与暂存区的该文件进行比较

    git diff HEAD(or版本哈希值) 文件名 将工作区的该文件与对应版本库里的该文件进行比较

    git diff 将工作区的所有文件与暂存区进行比较

     

    git cherry- pick 补丁所在版本号 分支名 拣选某次提交的补丁添加到目标分支上

    image-20210612011936593

     

    要拣选提交 C4 到 maint 分支(maint 指向 C7),Git 会生成一个补丁(Δ = C 4 − C 3),然后把Δ应用到C7上,也就是说把 C4 对 C3 的变化在 C7 上重放一遍。

  4. 版本的前进/后退

    git log 查看所有提交的日志

    git log -p -3 显示最近3次提交日志并显示差异

    git log --oneline 以一行的形式显示提交日志

    git reflog 在显示提交日志的基础上增加了HEAD指针指向不同的版本之间需要的步数

     

    版本的穿梭——基于索引值实现

    git reset --hard 目标版本的哈希值(前7位) 将版本穿梭到目标版本

    git reset --hard HEAD 通常作用是恢复误删除文件并添加到暂存区

     

    版本回退 ^

    git reset --hard HEAD^ 回退到上一个版本

    git reset --hard HEAD^^ 回退到两个版本前

     

    大段版本回退 ~

    git reset --hard HEAD~5 回退到五个版本前

     

    git reset 的三个参数对比

    • --soft 参数 仅仅在版本库中移动HEAD指针,通常用于把多次提交压缩成一次提交

     

    • --mixed 参数 在版本库中移动HEAD指针,并且重置暂存区,通常用于撤销提交

     

    • --hard参数 将3个区都回溯到目标版本的快照

     

    git reset 不带参数默认为 --mixed

    git reset 可以接版本库 和对应文件,在mixed参数修饰下的 git reset 版本快照 文件名 只会把暂存区的文件回溯到对应版本,HEAD指针并不会改变

    image-20210611131729731

    image-20210611131749680

    详解reset命令的工作原理

    根据参数的不同,reset的工作步骤也逐一增加。

    1. 将分支及HEAD指针指向目标快照 (--soft)

    2. 利用HEAD所指的快照更新暂存区 (默认 or --mixed)

    3. 根据暂存区重置工作目录

     

    其实reset的三步骤分别对应了一个新的提交的产生的3个步骤,具有很明显的一一对应的特性,互为逆操作。

    使用 git reset --soft HEAD~ 相当于撤销了上一次 git commit当你在运行 git commit 时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。 当你将它 reset 回 HEAD~(HEAD 的父结点)时,其实就是把该分支移回原来的位置,而不会改变索引和工作目录。

    使用 git reset (--mixed) HEAD~时,它依然会撤销一上次提交,但还会根据版本库恢复暂存区。 于是,我们回滚到了所有 git add 和 git commit 的命令执行之前。

    使用 git reset --hard HEAD~时,你撤销了最后的提交(git commit )、git add 和工作目录中的所有的修改工作,–hard 标记是 reset 命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一。其他任何形式的 reset 调用都可以轻松撤消,但是 –hard 选项不能,因为它强制覆盖了工作目录中的文件。

     

  5. 分支管理

    git branch -v 查看所有本地分支

    git branch 分支名 创建一个分支

    git checkout -b 新分支名 模板分支名 以一个分支模板创建分支

    git check out(远程仓库地址/) 分支名 切换到对应分支

    git merge 有新内容的分支名 合并分支

     

     

    有关checkout命令

    事实上,checkout命令有两种功能:

    1. 由历史快照(或暂存区域)中拷贝文件到工作目录

      • 当给定一个文件名和提交(快照ID)时,git会从指定提交中拷贝文件到暂存区域和工作目录 如git checkout HEAD~3 README.md

      • 如果只给定了文件名没有给定快照ID,则将从暂存区恢复该文件到工作目录

      image-20210609193427023

       

    2. 切换分支

      • 在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。

     

    checkout和reset --hard 命令

    • checkout命令会通过检查来确保不会将已更改的文件弄丢;而 `reset --hard 则会不做检查就全面地替换所有东西。、

    • reset 会移动 HEAD 分支的指向(即 HEAD 指向的分支的指向),而checkout 只会移动 HEAD 自身来指向另一个分支。

    这里写图片描述

     

     

     

  6. 本地库与远程库交互

    git remote -v 查看地址别名

    git remote add 别名 远程仓库地址 给远程仓库地址取别名

    git push 地址or地址别名 推送分支名 将本地仓库推送到远程仓库

    git clone 远程仓库地址 将远程仓库地址克隆到本地

    git fetch 远程仓库地址or地址别名 分支名 拉取远程仓库信息

    git push-set-upstream 远程仓库地址 新分支名 将本地新创建的分支推送到远程仓库并设置为上流分支

     

  7.  

 

注意事项

  • windows下以.开头的文件代表隐藏文件

  • 签名的作用是标识开发人员的身份

  • 登陆github的用户名和密码与签名没有任何关系

  • 项目级别签名和系统用户签名至少要有一个,如果没设置项目级别签名则该项目用系统用户签名,当两种签名都存在时,采用就近原则:小范围级别签名——项目级别签名生效

  • 项目级别签名信息保存位置 项目目录/.git/config文件

    系统用户级别签名信息保存位置 家目录/.gitconfig文件

  • image-20210608171044438

  • 哈希值只要取前7位就能完成检索

  • 在使用^和~回退时再调用git log 是不能看到处于该版本后面的版本日志,可以调用 git reflog看到,但是运用哈希值的版本穿梭时,git log都能看到所有版本

  • git rm 命令后工作区与暂存区相比少了文件,需要用git add将本次修改添加到缓存区

  • 用git把文件添加并提交到版本库,那该文件就是“不可磨灭”的,也就是说即使我们误删除了也能通过版本回溯找回文件

  • 以模板分支创建分支会继承母分支的所有commit,然后母分支与子分支独立

  • 在git merge之前我们要用git chekout切换到接收修改的分支上(被合并,增加新内容),然后再 git merge

  • 本地库与远程库不一定需要名字相同

  • 远程库克隆到本地仓库后无需再git init ,自动就包含了.git文件夹,而且会自动根据远程库里的地址别名同步到本地

  • pull操作其实是fetch和merge的合操作
  • fetch操作只是把远程仓库下载到本地,但并未改变本地工作区的文件

  • fetch和pull对于远程仓库来说都是读操作,所以无需登陆github

  • windows可能会保存github的账号密码会自动登陆,如果要切换账号需要在控制面板里的凭据管理器删除github的凭据

 

 

git的基本原理

 

哈希算法

哈希算法是一个系列的加密算法,不同哈希算法之间加密强度不同,但有几个共同点

  • 不管输入的数据有多大,输入同一个哈希算法时,得到的加密结果长度固定。

  • 当哈希算法和输入数据都唯一确定时,输出结果也是唯一确定的

  • 哈希算法相同时,不同数据输入时,输出结果变化通常很大

  • 哈希算法是不可逆算法

 

git保存版本的机制

  • 集中式版本控制工具的文件管理机制 代表软件:SVN

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

    是一种增量式管理

    image-20210608235123074

     

  • 分布式版本控制工具的文件管理机制 代表软件:git

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

image-20210608235600764

 

 

 

github的相关操作

fork 将一个开源的仓库赋值到你的远程仓库

pull request 在团队合作时把你fork的代码修改后发起pull request请求原仓主合并你的代码

 

Vim编辑器常用命令

:wq 将缓冲区内的资料写入磁盘中,并离开vi。

space 向下翻页

b 向上翻页

q 退出

 

 

 

参考资料

Git 的工作区、暂存区、版本库—— Git 学习笔记 15_车子(chezi)-CSDN博客

Git分支管理策略 - 阮一峰的网络日志 (ruanyifeng.com)

推荐阅读