Git简介

Git是一种分布式版本控制系统,主要用于跟踪代码的变更和管理项目的版本。它允许开发者在本地仓库进行操作,并通过远程仓库协作。Git的分支管理功能强大,支持多人并行开发和高效的合并。它通过SHA-1哈希确保数据的完整性,能够灵活处理各种代码版本和历史,使得协作和代码管理更加便捷。

当前Git可视化工具非常多,在代码协作的过程中,还是比较推荐使用Git可视化系统进行代码管理,这样大大提高了工作效率。

安装与配置

Git的安装与配置

Git的安装非常简单,只需要到官网下载git客户端,然后按默认配置进行安装。

在使用 Git 时,为了标识提交记录的所有者,需要配置用户名和邮箱。你可以通过以下命令来设置:

  • 设置全局用户名和邮箱
git config --global user.name "你的用户名"
git config --global user.email "你的邮箱地址"
  • 设置仓库级别的用户名和邮箱

如果你希望只针对某个特定仓库设置用户名和邮箱,可以进入该仓库的目录,然后执行以下命令:

git config user.name "你的用户名"
git config user.email "你的邮箱地址"
  • 验证配置

你可以通过以下命令查看当前的用户名和邮箱配置:

git config --global --list

或者查看某个仓库的配置:

git config --list

Git 使用 SSH 密钥来进行身份验证,确保安全连接到远程仓库。生成 SSH 密钥的步骤如下:

1.生成 SSH 密钥

在你的终端或命令行中,输入以下命令:

ssh-keygen -t rsa -b 4096 -C "你的邮箱地址"
  • -t rsa 表示生成 RSA 类型的密钥。
  • -b 4096 指定密钥的长度为 4096 位(可以选择其他长度)。
  • -C 用来添加注释,这里通常使用邮箱地址。

这样就生成了ssh密钥,它通常会在**c盘当前用户主目录/.ssh/**文件夹下

2.添加公钥到远程 Git 平台
GitHub:前往 GitHub,点击头像,选择 Settings -> SSH and GPG keys -> New SSH Key,然后将生成的公钥粘贴到文本框中。

Git相关基本概念

  1. 仓库(Repository)
    仓库是 Git 存储和管理项目的核心概念,它包含了所有文件的历史记录。仓库可以分为两种:

    本地仓库:指你在本地计算机上创建和管理的仓库。通过命令 git init 可以创建一个本地仓库。所有的提交和分支都可以在本地操作。
    远程仓库:指托管在远程服务器上的仓库,例如 GitHub、GitLab、Gitee 等。远程仓库便于团队协作,开发者可以通过 git clone 下载远程仓库,也可以通过 git push 上传本地更改。

  2. 工作区(Working Directory)
    工作区就是你当前项目的文件目录,包含你正在编辑的所有文件。当你修改一个文件并保存时,这些变更发生在工作区。工作区是你操作的主要场所,在这里你可以对文件进行增删改等操作。

  3. 暂存区(Staging Area)
    暂存区(也称为索引 Index)是 Git 中一个临时区域,用于保存你即将提交的更改。每次使用 git add 命令时,文件的变更就会被添加到暂存区。暂存区的存在使得你可以选择性地提交某些修改,而不是将工作区中的所有修改一并提交。

  4. 本地仓库(Local Repository)
    本地仓库是 Git 中存储所有历史版本和元数据的地方。通过 git commit 命令,将暂存区的更改提交到本地仓库。每次提交会生成一个唯一的 SHA-1 哈希值,用于标识该次提交。

  5. 远程仓库(Remote Repository)
    远程仓库是 Git 用来存储项目的远程副本,通常托管在服务器上。你可以通过命令 git remote add 添加远程仓库的 URL 地址,以便进行推送或拉取操作。远程仓库是团队协作开发的基础,允许多名开发者共享项目代码。

  6. 提交(Commit)
    提交是 Git 中记录更改的基本单元。每次执行 git commit,都会在本地仓库中创建一个新的快照,并保存此次提交的详细信息(包括作者、时间和更改的内容)。提交记录会生成一个唯一的 SHA-1 哈希值,标识每次提交。提交可以视为对代码历史的一个“快照”。

  7. 分支(Branch)
    分支是 Git 中的一个核心概念,用于隔离不同的开发线。每个分支都是从主线中独立出来的开发轨迹。Git 默认会创建一个 main 或 master 分支,用户可以通过命令 git branch创建新分支,然后通过 git checkout切换到该分支。分支的存在使得并行开发、修复 Bug 和特性开发变得简单。

  8. 标签(Tag)
    标签用于给某个特定的提交创建一个固定的标记,通常用于版本发布。

  9. HEAD
    HEAD 是 Git 中的一个特殊指针,它指向你当前所在的分支的最新提交。它是 Git 跟踪当前操作位置的方式。当你切换分支时,HEAD 也会指向新分支的最新提交。

  10. 合并(Merge)
    合并是将不同分支上的修改合并到一个分支中的操作。通过 git merge命令,你可以将分支的修改合并到当前分支。

  11. 冲突(Conflict)
    当两个分支对同一文件的同一部分做出不同的修改时,Git 会产生冲突。冲突会阻止合并操作完成,开发者需要手动解决冲突,然后再提交。Git 会标记冲突部分,帮助你进行手动合并。

  12. Rebase
    Rebase 是 Git 中的一种分支整合方法,它的作用是将一个分支的提交记录重新应用到另一条分支的顶部。与 merge 不同,rebase 会重写提交历史,将目标分支的提交线性化。通过 git rebase命令,你可以将当前分支的提交应用到分支的顶部。rebase 主要用于保持提交历史的整洁。

  13. 远程追踪分支(Remote Tracking Branch)
    远程追踪分支是指向远程仓库中对应分支的本地引用,它们的名字通常以 origin/branch-name 形式存在。例如,origin/main 表示远程仓库 origin 中的 main 分支。远程追踪分支使得你可以同步和追踪远程仓库的变更。

  14. Fork
    Fork 是托管平台(如 GitHub 和 GitLab)提供的功能,它允许你从一个公开的项目创建一个副本到你自己的账户中。Fork 的主要作用是方便在不影响原始项目的情况下进行独立开发和改动。Fork 项目之后,你可以修改它、提交更改,并通过创建 Pull Request 将改动提交回原始项目。

  15. Pull Request(PR)
    Pull Request 是托管平台中一种用于代码协作的机制。开发者可以在自己的分支或 Fork 中做出修改,然后创建一个 Pull Request,通知仓库的管理员他们希望将这些修改合并到主分支中。Pull Request 通常伴随代码审查和讨论,用于确保代码质量。

常用命令

  • git init
    初始化一个新的 Git 仓库。通常在你从零开始创建项目时使用:

    git init
  • git remote
    管理远程仓库。可以查看当前仓库的所有远程连接、添加远程仓库、删除远程仓库等:

    git remote -v                        # 查看当前所有的远程仓库
    git remote add origin <url>          # 添加一个名为 origin 的远程仓库
    git remote remove origin              # 删除远程仓库
  • git status
    查看当前仓库的状态,包括哪些文件被修改、哪些文件处于暂存区,以及未被跟踪的文件等:

    git status
  • git log
    查看仓库的提交历史。你可以使用不同的参数来获取更详细或简洁的日志:

    git log              # 查看提交历史
    git log --oneline    # 简洁的单行提交历史
  • git diff
    查看工作区中哪些文件或内容被修改。可以用来对比不同提交或分支之间的差异:

    git diff                     # 查看当前工作区的更改
    git diff <commit1> <commit2> # 对比两个提交之间的差异
  • git add
    将文件的更改添加到暂存区,以便进行下一步的提交。可以添加单个文件,也可以一次性添加所有更改的文件:

    git add <file>       # 添加单个文件
    git add .            # 添加所有修改过的文件
  • git commit
    将暂存区的更改提交到本地仓库,并且需要附带提交消息来描述这次提交的内容:

    git commit -m "Add new feature"
  • git stash
    暂存当前的未提交更改,并在以后取出。适合在你正在开发时突然需要切换分支但不想提交当前工作时使用:

    git stash             # 暂存当前更改
    git stash apply       # 恢复暂存的更改
  • git branch
    列出所有分支,创建新分支,或者删除分支:

    git branch            # 列出所有分支
    git branch <branch>   # 创建新分支
    git branch -d <branch> # 删除分支
  • git checkout
    切换到其他分支,或检出某个提交。也可以用来创建和切换新分支:

    git checkout <branch>               # 切换到已有的分支
    git checkout -b <new-branch>        # 创建并切换到新分支
  • git merge
    合并两个分支的更改。通常用于将其他分支的更改合并到当前分支:

    git merge <branch>
  • git rebase
    将一个分支的提交“重新基准化”到另一个分支的顶部。可以用来整理提交历史:

    git rebase <branch>
  • git fetch
    从远程仓库中获取更新,但不自动合并。git fetch 只会下载远程分支的更新到本地,而不会直接影响当前工作区的文件:
    git fetch origin
  • git reset
    回退到某个特定的提交。你可以使用不同的参数来控制回退的程度:
    git reset --hard <commit>  # 将工作区、暂存区和提交记录全部回退到某个提交
    git reset --soft <commit>  # 只回退提交记录,保留暂存区的内容
  • git tag
    给特定的提交打标签,通常用于标记发布版本:
    git tag <tagname>                    # 创建一个新的标签
    git tag -a <tagname> -m "message"    # 创建带注释的标签
    git push origin <tagname>            # 推送标签到远程仓库

Git提交规范

目前流传比较广泛的就是Git提交规范

提交信息结构

Git提交消息的基本结构如下

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
  • <type>: 提交的类型。一般为fixfeatbuildchorecidocsstylerefactorperftest
  • [optional scope]: 可选的范围,用于描述该提交影响的代码区域或模块。
  • <description>: 提交的简短描述,概述了更改的内容。
  • [optional body]: 可选的详细信息,描述更改的原因或背景。
  • [optional footer(s)]: 可选的页脚,通常用于提供额外信息,如 BREAKING CHANGE

提交类型

  • fix: 修复代码中的错误。
  • feat: 引入新功能。
  • build: 更改构建系统或外部依赖项的提交。
  • chore: 维护性提交,通常不影响源代码或测试。
  • ci: 与持续集成相关的更改。
  • docs: 文档相关的更改。
  • style: 代码格式方面的更改(不影响功能)。
  • refactor: 代码重构的提交,不会修复错误或添加功能。
  • perf: 性能优化的提交。
  • test: 测试相关的更改。

注:BREAKING CHANGE:表示提交中包含该标识的内容表示此次更改会影响 API 的向后兼容性,通常会引发使用者的代码中断。例如:

  • 在页脚中声明: 通过 BREAKING CHANGE: <description> 的方式描述具体的破坏性更改。
  • 在类型后附加 !: 例如,feat!fix! 代表这个提交引入了破坏性更改。

相关工具

Jetbrains全家桶系列中的插件Git Commit Message Helper

git merge 和 git rebase

git mergegit rebase 是版本控制中非常重要的操作,而它们在处理冲突时也发挥着关键作用。

注:git pull = git fetch+git merge

1. git merge

  • 定义: git merge 是将一个分支的更改合并到当前分支中。这通常用于将特性分支合并回主分支(如 maindevelop),以整合多个开发者的工作。

  • 工作原理:

    1. Git 会自动尝试合并两个分支的历史。
    2. 如果两个分支的更改在同一文件的同一部分冲突,Git 会标记这些文件,并提示开发者手动解决冲突。
  • 优点:

    • 保留了分支的历史,可以清楚地看到何时进行了合并操作。
    • 适合团队协作,能方便地管理和整合多条开发线。
  • 缺点:

    • 可能会导致多条合并历史,增加 Git 历史的复杂性,尤其是在频繁合并的情况下。

2. git rebase

  • 定义: git rebase 是将当前分支的基点更改为另一个分支的最新提交。它会将当前分支的提交“移到”目标分支的末尾。

  • 工作原理:

    1. 通过将当前分支的更改逐个应用到目标分支,Git 会尽量将历史线性化。
    2. 如果在重新应用某个提交时发生冲突,Git 会停止并要求开发者解决冲突。
  • 优点:

    • 生成更清晰的线性提交历史,使得回溯和理解更容易。
    • 避免了合并引入的额外合并提交。
  • 缺点:

    • 可能会重写历史,导致在多人协作时出现问题(尤其是在公共分支上)。
    • 处理冲突时,可能需要逐个提交进行解决,较为繁琐。

3.实际场景

下面使用节点 a, b, c, d 等等来表示提交。

假设的提交历史

我们从一个初始的提交开始,假设我们有以下的提交历史:

A -- B -- C  (main)
1. 使用 git merge

现在假设我们创建了一个特性分支 feature,并在这个分支上进行了两次提交 DE

A -- B -- C  (main)
         \
          D -- E (feature)

现在我们希望将 feature 分支的更改合并回 main 分支。我们可以执行 git merge feature,结果如下:

# 切换到 main 分支
git checkout main
# 执行合并
git merge feature

合并后,提交历史会变成:

A -- B -- C ------ M (main)
         \        /
          D -- E (feature)
  • M 是合并提交,表示 main 分支和 feature 分支的历史合并。
  • 历史保留了两个分支的提交(C, D, E),并且形成了一个分支图。
2. 使用 git rebase

如果我们选择在特性分支上使用 git rebase,我们首先会将 main 分支的更新应用到 feature 分支上,然后再将 feature 分支合并回 main。假设在 feature 分支上我们做了提交 DE 之后,main 分支又有了新的提交 F

A -- B -- C -- F (main)
         \
          D -- E (feature)

在这种情况下,我们执行 git rebase main

# 切换到 feature 分支
git checkout feature
# 执行 rebase
git rebase main

变基后,提交历史变成:

A -- B -- C -- F -- D' -- E' (feature)
  • 提交 DE 被重新应用为 D'E',它们有新的哈希值,因为它们的父提交发生了变化。
  • feature 分支的历史变得线性了。

接下来,如果我们将 feature 分支合并到 main

# 切换到 main 分支
git checkout main
# 合并 feature
git merge feature

最终的提交历史如下:

A -- B -- C -- F -- D' -- E' (main)

4.通俗理解

  • git rebase:将自己的开发分支的提交“移到”主分支后面,历史记录被重写为线性(原有分支看似被丢弃,但实际上是重写的)。
  • git merge:在合并过程中保留自己提交的分支历史,生成一个新的合并提交,清晰地显示出合并后的代码状态。