Git 的正确使用姿势 | 青训营

34 阅读12分钟

概念介绍

方向具体能力
代码托管负责管理公司内数十万的代码仓库,并在这之上对代码管理的相关功能进行迭代,提升研发活动的效率及质量
代码智能提供更准确高效的代码搜索能力和代码导航能力,支持多种场景下的代码跳转,帮助用户更高效的去阅读代码
代码分析提供一种代码检查能力,目的是在整个研发流程中自动的发现并反馈代码中存在的代码结构、代码漏洞、代码风格等问题
持续集成一种软件开发实践,团队成员频繁将他们的工作成果集成在一起。每次提交后,自动触发运行一次包含自动化验证集的构建任务,以便能尽早发现集成问题
Cloud IDE一个开箱即用的云端开发环境,支持node/python/go/java/c++等多种编程语言。你可以在云端开发环境中编写、编译、运行和调试你的项目

为什么要学习Git

  • 协同工作 业界绝大多数公司都是基于Git进行代码管理,因此Git是一个程序员的必备技能

  • 开源社区 目前绝大多数的开源项目都是基于Git维护的,参与这些项目的开发都需要使用 Git

  • 常见问题

    1. 入职后按照文档进行Git配置,但是配置后依然拉取代码有问题,缺少自己排查配置问题的能力
    2. 研发流程中进行一些异常操作,不符合研发规范,不清楚保护分支,MR/ PR等概念
  • 基本原理

    1. 每个库都存有完整的提交历史,可以直接在本地进行代码提交
    2. 每次提交记录的都是完整的文件快照,而不是记录增量
    3. 通过 Push等操作来完成和远端代码的同步
  • 优点

    1. 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
    2. 分支管理功能强大,方便团队合作,多人协同开发
    3. 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失
  • 缺点

    1. 相对SVN更复杂,学习成本更高
    2. 对于大文件的支持不是特别好(git-lfs工具可以弥补这个缺陷)

Git基本使用方式

image-20230828011906010.png

Git目录介绍

初始化

  • 项目初始化

  • mkdir study

  • cd study

  • git init

  • 其他参数 --initial-branch 初始化的分支 --bare 创建一个裸仓库(纯Git目录,没有工作目录) -template 可以通过模版来创建预先构建好的自定义git目录

Git 常见配置

  • 用户名配置

    git config --global user.name“liaoxingju” git config --global user.email liaoxingju@bytedance.com

  • lnstead of 配置 git config --global url.git@github.com:.insteadOf https://github.com/

  • Git命令别名配置 git config --global alias.cin "commit --amend --no-edit"

Git Remove(删除文件)

  • 查看 Remote

    git remote -v

  • 添加 Remote git remote add origin_ssh git@github.com:git/git.git

    git remote add origin_http https://github.com/git/git.git

Git Add(添加文件)

git add <文件/文件夹>

  • 使用通配符: 可以使用通配符来指定要添加的文件。例如,git add *.txt 将添加所有扩展名为 .txt 的文件。
  • 添加所有修改: 如果要添加所有已修改、已删除或新增的文件,可以使用 git add --allgit add -A
  • 部分添加文件: 除了一次性添加整个文件,还可以使用交互式模式(git add -p)选择要添加的文件的具体部分。这对于仅想添加文件的一部分更改非常有用。
  • 添加指定行或范围: 使用 git add -e 命令可以打开编辑器并添加指定行或行范围的更改。这对于仅添加特定更改非常有用。
  • 移除文件: 使用 git rm 命令可以从 Git 仓库中删除文件,并自动将其从暂存区中移除。如果只是想从版本控制中移除文件但保留在文件系统中,可以使用 git rm --cached
  • 忽略文件: 如果有一些不想添加到版本控制的文件或目录,可以在项目根目录中创建一个名为 .gitignore 的文件,并将要忽略的文件或目录模式添加到其中。这样 Git 在执行 git add 时会自动跳过这些文件。

Objects

commit / tree / blob 在git里面都统一称为Object,除此之外还有个tag的object.

1. Commit(提交对象): commit 对象包含了一次代码变更的元数据信息,如作者、提交时间、提交消息等。每个 commit 对象都指向一个 tree 对象,表示该提交时文件系统的状态。

2. Tree(树对象): tree 对象表示文件和目录的结构以及其在文件系统中的位置。它存储了一个或多个 blob 或其他 tree 对象的指针,并记录了文件名、文件模式和指向相应 blobtree 的 SHA-1 值。

3. Blob(文件对象): blob 对象对应于文件内容。它保存了文件的实际数据,是 Git 中最基本的对象类型。blob 对象与文件的内容相关联,但不包含文件名或其他元数据。

如何把这三个信息串联在一起呢?

  1. 通过 Commit寻找到Tree 信息,每个Commit都会存储对应的Tree ID.
  2. 通过Tree存储的信息,获取到对应的目录树信息。
  3. 从 tree中获得blob的 ID,通过 Blob ID获取对应的文件内容。

悬空的Object

顾名思义就是没有ref 指向的object

分支

分支(Branch)是一种指向提交(Commit)的可变指针。

  1. 默认分支(Main Branch): 创建一个 Git 仓库时,默认会有一个名为 mastermain 的主分支。这个分支通常用于跟踪生产代码。
  2. 新建分支: 在 Git 中,通过使用 git branch 命令可以创建新的分支。例如,使用 git branch new-branch 可以创建一个名为 new-branch 的新分支。新分支的起点默认是当前所在分支的最新提交。
  3. 切换分支: 使用 git checkout 命令可以在不同的分支之间进行切换。例如,使用 git checkout new-branch 可以切换到 new-branch 分支。
  4. 创建并切换分支: 在 Git 2.23 版本之后,引入了 git switch 命令用于创建并切换分支,用法类似 git checkout -b。例如,使用 git switch -c new-branch 可以创建并切换到 new-branch 分支。
  5. 查看分支: 使用 git branch 命令可以查看所有分支的列表。运行 git branch --all 可以查看包括远程分支在内的所有分支。
  6. 合并分支: 使用 git merge 命令可以将一个分支的更改合并到另一个分支。例如,切换到目标分支后,运行 git merge source-branchsource-branch 的更改合并到当前分支。
  7. 删除分支: 使用 git branch -d 命令可以删除已合并的分支。例如,使用 git branch -d new-branch 可以删除名为 new-branch 的分支。
  8. 推送分支: 如果想将新分支推送到远程仓库,需要使用 git push 命令,例如 git push origin new-branch。这样其他人就可以看到和访问该分支了。

Refs

ref 是指向提交对象(commit object)的引用。它是一种可变指针,用于标记拥有特定名称的提交或分支。

通过管理和操作 refs,可以查找、创建、删除和移动分支、标签等 Git 的引用。它们可以更好地组织和追踪项目的历史以及不同版本的状态。

  1. 分支引用(Branch Reference): 在 Git 中,分支是通过分支引用来管理的。例如,mastermainfeature/branch 等都是分支引用的名称。这些分支引用指向了最新的提交对象(commit object),以便追踪代码的不同版本。
  2. HEAD 引用: 在 Git 中,HEAD 是一个特殊的引用,指向当前所在分支或提交。它通常指向当前分支的最新提交,表示当前工作目录中的代码状态。通过移动 HEAD 引用,可以切换分支或浏览项目的历史。
  3. 标签引用(Tag Reference): 标签是一个具有特定名称的静态引用,指向特定的提交对象。它通常用于标记项目的重要里程碑、发布版本等。标签引用在创建后是固定不变的,不会随着新的提交而移动。
  4. 远程引用(Remote Reference): 远程引用是指向远程仓库的引用,用于跟踪远程分支及其提交。当从远程仓库拉取或推送代码时,本地仓库会更新远程引用。
  5. 引用命名空间: 在 Git 中,引用名称存储在 .git/refs 目录下。不同类型的引用(分支、标签、远程等)存储在不同的子目录中。例如,分支引用存储在 .git/refs/heads 目录下,标签引用存储在 .git/refs/tags 目录下。

标注标签(Annotation Tag)

标注标签(Annotation Tag)是一种更详细的标签类型,用于为特定的提交对象提供额外的注释和元数据。它包含了标签名称、标签作者、创建日期、注释信息等。

标注标签提供了一种更详细的标记方式,使得开发人员可以为特定的提交对象添加额外的注释和元数据,方便项目管理和版本控制。它们常用于标记重要的里程碑、发布版本或其他重要事件。

  1. 创建标注标签: 使用 git tag -a 命令可以创建一个标注标签。例如,git tag -a v1.0 -m "Release version 1.0" 将创建一个名为 v1.0 的标注标签,并附带注释信息。
  2. 标注标签的元数据: 创建标注标签时,除了标签名称和注释信息外,还可以包含其他元数据,如标签作者和创建日期。这些元数据将与标签一起被提交和存储。
  3. 查看标注标签: 使用 git show 命令可以查看标签的详细信息,包括标签作者、创建日期和注释。例如,git show v1.0 将显示名为 v1.0 的标注标签的相关信息。
  4. 推送标注标签: 标注标签默认不会被自动推送到远程仓库,因此需要显式地将标签推送到远程仓库。使用 git push 命令并指定 --tags 参数可以将本地所有的标签都推送到远程仓库。
  5. 删除标注标签: 使用 git tag -d 命令可以删除本地的标注标签。例如,git tag -d v1.0 将删除名为 v1.0 的本地标注标签。如果需要删除远程仓库中的标签,可以使用 git push 命令并指定 --delete 参数。
  6. 校验标签: 使用 git verify-tag 命令可以验证标注标签的完整性和真实性。这有助于确保标签没有被篡改或损坏。

追溯历史版本

  • 获取当前版本代码 通过 Ref 指向的Commit可以获取唯一的代码版本。
  • 获取历史版本代码 Commit里面会存有parent commit字段,通过commit的串联获取历史版本代码。
    1. 修改文件,并提交,创建新的commit.
    2. 查看最新的commit,新增了parent 信息。

修改历史版本

  1. commit --amend 通过这个命令可以修改最近的一次commit信息,修改之后commit id会变

  2. rebase

    通过git rebase -i HEAD~3可以实现对最近三个commit的修改:

    1. 合并commit
    2. 修改具体的 commit message
    3. 删除某个commit
  3. ilter --branch 该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作

Git GC

GC

通过git gc命令,可以删除一些不需要的object,以及会对object进行一些打包压缩来减少仓库的体积。

Reflog

reflog是用于记录操作日志,防止误操作后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期。

指定时间

git gc prune=now指定的是修剪多久之前的对象,默认是两周前

Git Clone & Pull & Fetch

  • Clone 拉取完整的仓库到本地目录,可以指定分支,深度。

  • Fetch 将远端某些分支最新代码拉取到本地,不会执行merge操作, 会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动操作。

  • Pull 拉取远端某分支,并和本地代码进行合并,操作等同于 git fetch + git merge,也可以通过git pull --rebase完成 git fetch + git rebase 操作。

    可能存在冲突,需要解决冲突。

Git Push

Push是将本地代码同步至远端的方式。

  • 常用命令 一般使用git push origin master命令即可完成冲突问题
    1. 如果本地的commit 记录和远端的commit历史不一致,则会产生冲突,比如 git commit --amend or git rebase都有可能导致这个问题。
    2. 如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过git push origin master -f来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送。 推送规则限制 可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失。

常见问题

  1. 为什么我明明配置了Git配置,但是依然没有办法拉取代码?
    • 免密认证没有配。
    • lnstead Of 配置没有配,配的SSH免密配置,但是使用的还是HTTP协议访问。
  2. 为什么我 Fetch了远端分支,但是我看本地当前的分支历史还是没有变化?
    • Fetch 会把代码拉取到本地的远端分支,但是并不会合并到当前分支,所以当前分支历史没有变化。