跳到主要内容

Git 从入门到熟练:一篇全面的指南


一、Git 基础篇

1.1 版本控制:为什么要使用 Git?

1.1.1 什么是版本控制

版本控制是一种管理文件变更历史的方法和工具,能够记录文件在不同时间点的状态,并允许用户随时还原到任何一个记录。

它对于团队协作和个人开发都非常重要,主要体现在以下几个方面:

  • 历史追溯:每次修改都会被记录下来,可以随时查看修改历史。
  • 还原能力:如果某次修改出现问题,可以迅速回退到之前的版本。
  • 协作支持:多名开发者可以在同一项目上协作开发,版本控制系统能够合并不同开发者的代码,并解决冲突。

应用场景,例如:

  • 代码管理:追踪代码的修改历史,方便回滚和协作。
  • 文档撰写:多人协同编辑文档,记录每次修改,避免版本混乱。
  • 设计稿管理:保存设计稿的不同版本,方便对比和迭代。

1.1.2 版本控制系统的类型

  1. 本地版本控制系统

    • 示例:RCS(Revision Control System)。
    • 特点:只能在本地计算机上使用,适合单人开发。
  2. 集中式版本控制系统

    • 示例:CVS、Subversion (SVN)。
    • 特点:所有版本数据存储在中央服务器上,客户端需要连接到服务器才能获取和提交更新。
  3. 分布式版本控制系统

    • 示例:Git、Mercurial。
    • 特点:每个开发者的本地仓库都包含完整的版本历史,无需联网即可操作。

1.1.3 Git 的优势

相比于其他版本控制系统,Git 具有以下明显优势:

  • 分布式架构:每个开发者的本地仓库都完整备份了整个项目的版本历史,无需联网也能查看历史记录或创建新提交。
  • 高效性:Git 的操作(如分支创建、合并等)速度非常快,因为大部分操作都在本地完成。
  • 强大的分支管理:Git 的分支非常轻量,可以轻松地创建、切换和合并分支。
  • 丰富的社区支持:作为目前最流行的版本控制系统,Git 拥有广泛的用户群体和丰富的第三方工具支持。
  • 轻量级分支:Git 的分支操作非常快速轻量,鼓励开发者频繁使用分支进行功能开发和 bug 修复。
  • 数据完整性:Git 使用 SHA-1 算法保证数据的完整性,防止数据损坏或篡改。
  • 开源免费:Git 是一个开源免费的版本控制系统,任何人都可以使用和贡献。

1.2 Git 的安装和配置

1.2.1 在不同操作系统上安装 Git

  1. Windows

    • 下载 Git for Windows,按照提示安装。
    • 安装过程中可以选择安装 Git Bash 和 Git GUI,这些工具会提升使用体验。
  2. macOS

    • 打开终端,使用 Homebrew 安装 Git:
      brew install git
    • 确认安装:
      git --version
  3. Linux

    • 使用系统自带的包管理器安装:
      # 对于 Debian/Ubuntu 系统
      sudo apt update
      sudo apt install git

      # 对于 CentOS/Fedora 系统
      sudo yum install git
    • 验证安装是否成功:
      git --version

1.2.2 配置 Git 用户信息

安装 Git 后,需要配置用户名和邮箱。这些信息会记录在每次提交中,标识提交者身份。

--global 参数的作用:配置全局用户信息,适用于所有 Git 仓库。

# 设置全局用户名和邮箱
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"

# 查看配置是否成功
git config --list

1.2.3 配置 SSH 密钥

配置 SSH 密钥可以避免每次推送代码到远程仓库时输入用户名和密码。

  1. 生成 SSH 密钥:

    ssh-keygen -t rsa -b 4096 -C "你的邮箱"

    按提示输入文件保存路径(通常默认保存在 ~/.ssh/id_rsa)。

  2. 查看生成的公钥:

    cat ~/.ssh/id_rsa.pub

    将输出的公钥复制到远程仓库的 SSH 设置中。

  3. 测试连接:

    ssh -T git@github.com

    如果成功,会显示欢迎信息。

1.3 Git 基本概念

1.3.1 工作区、暂存区和版本库

  • 工作区(Working Directory):当前文件所在的本地目录,开发者直接编辑文件的地方。
  • 暂存区(Staging Area):通过 git add 命令将修改的文件加入暂存区,准备提交。
  • 版本库(Repository):通过 git commit 将暂存区的内容提交到版本库,进行持久保存。

git 分区关系简图

1.3.2 Git 对象模型

Git 的数据存储基于以下四种对象:

  1. Blob(文件数据)
    • 保存文件的内容,不包含文件名和其他元信息。
  2. Tree(目录)
    • 保存文件名和指向对应 Blob 的指针。
  3. Commit(提交)
    • 记录一次提交的信息,包括作者、提交时间和指向上一次提交的指针。
  4. Tag(标签)
    • 用于给特定的提交打标签,通常用于标记版本。

img

1.3.3 分支的概念

分支是代码开发的独立拷贝,允许在不影响主分支的情况下添加功能或修复问题。

  • 创建分支:git branch 分支名
  • 切换分支:git checkout 分支名
  • 合并分支:git merge 分支名

1.3.4 HEAD 的作用和含义

HEAD 是一个指针,指向当前分支的最新提交。

  • 切换分支时,HEAD 会指向新的分支。
  • 在分离 HEAD 状态下,HEAD 指向某个具体的提交,而不是分支。
  • 常用命令:
    git log  # 查看 HEAD 所指向的提交历史
    git checkout commit-hash # 分离 HEAD 状态

二、Git 常用操作

2.1 创建 Git 仓库

2.1.1 初始化本地仓库

Git 允许用户在本地创建自己的版本库,用于管理文件的变更历史。

  1. 进入项目目录:

    cd /path/to/your/project
  2. 初始化 Git 仓库:

    git init

    这将在当前目录下创建一个名为 .git 的隐藏文件夹,用于存储版本库的所有信息。

  3. 验证初始化成功:

    git status

    如果显示当前分支为 mastermain,且没有提交,说明初始化成功。

2.1.2 克隆远程仓库

克隆操作用于从远程服务器复制一个完整的 Git 仓库,包括所有历史记录和分支。

  1. 克隆远程仓库到本地:

    git clone <远程仓库地址>

    例如:

    git clone git@github.com:username/repo.git

    这将在当前目录下创建一个名为 repo 的文件夹,包含远程仓库的所有内容。

  2. 克隆到指定目录:

    git clone <远程仓库地址> <目标目录>

    例如:

    git clone git@github.com:username/repo.git my_project
  3. 克隆时只获取最新的提交记录(浅克隆):

    git clone --depth=1 <远程仓库地址>

    此命令会显著加快克隆速度,适合仅需要最新代码的场景。 克隆操作用于从远程服务器复制一个完整的 Git 仓库,包括所有历史记录和分支。

2.2 文件操作

2.2.1 添加文件到暂存区

将修改的文件添加到暂存区,准备提交到版本库。

  1. 添加单个文件:

    git add <文件名>

    例如:

    git add README.md
  2. 添加所有文件:

    git add .

    注意:git add . 会添加当前目录及其子目录下的所有修改文件。

  3. 交互式添加文件:

    git add -p

    该命令允许用户逐个查看修改并选择是否将其添加到暂存区,适用于需要精确选择部分修改进行提交的场景。 将修改的文件添加到暂存区,准备提交到版本库。

2.2.2 提交文件到版本库

将暂存区的内容提交到版本库。

  1. 提交文件:

    git commit -m "提交信息"

    提交信息应尽量简洁明了,例如:

    git commit -m "添加项目初始化文档"
  2. 提交并跳过暂存区(直接提交所有修改):

    git commit -a -m "提交信息"
  3. 修改最近一次提交: 如果需要修改最近一次提交的信息或添加新的修改,可以使用以下命令:

    git commit --amend

    例如:

    git commit --amend -m "更新提交信息"

    注意:此命令会覆盖上一次提交记录,适用于尚未推送到远程仓库的提交。 将暂存区的内容提交到版本库。

2.2.3 修改文件

修改文件后,使用以下步骤提交更改:

  1. 查看修改状态:

    git status
  2. 查看具体修改内容:

    git diff

    如果修改内容已被添加到暂存区,可以使用:

    git diff --cached
  3. 提交修改: 按照 2.2.1 和 2.2.2 的步骤添加并提交文件。

2.2.4 删除文件

在 Git 中删除文件有两种方式:

  1. 删除文件并移除版本库中的记录:

    git rm <文件名>
  2. 仅从版本库中移除文件,但保留本地文件:

    git rm --cached <文件名>
  3. 恢复误删的文件: 如果文件被删除但仍然存在于版本库中,可以使用以下命令恢复到工作区:

    git checkout -- <文件名>

提交更改:

git commit -m "删除文件"

2.2.5 移动和重命名文件

  1. 重命名文件:

    git mv <旧文件名> <新文件名>
  2. 提交更改:

    git commit -m "重命名文件"

2.3 分支管理

2.3.1 创建分支

  1. 创建新分支:

    git branch <分支名>
  2. 创建并切换到新分支:

    git checkout -b <分支名>

    或使用新命令:

    git switch -c <分支名>

2.3.2 切换分支

  1. 切换到已有分支:

    git checkout <分支名>

    或使用新命令:

    git switch <分支名>
  2. 查看所有分支:

    git branch -a

2.3.3 合并分支

将其他分支的更改合并到当前分支。

  1. 切换到目标分支(通常是主分支):

    git checkout main
  2. 合并分支:

    git merge <分支名>
  3. 禁用 Fast-forward 合并,保留分支的合并历史:

    git merge --no-ff <分支名>

    此选项会生成一个合并提交,便于在历史记录中清晰看到分支的合并点。

  4. 查看合并结果并提交:

    git status
    git commit -m "合并分支 <分支名>"
  5. 使用变基(rebase)将一个分支的修改应用到另一个分支上,使提交历史更加线性:

    git rebase <目标分支>

    例如,将当前分支变基到 main

    git rebase main

    注意:在协作开发中,变基应谨慎使用,避免对已推送的分支执行变基操作,以免影响他人工作。将其他分支的更改合并到当前分支。

2.3.4 删除分支

  1. 删除本地分支:

    git branch -d <分支名>

    如果分支尚未被合并,可以强制删除:

    git branch -D <分支名>
  2. 删除远程分支:

    git push origin --delete <分支名>

2.3.5 解决冲突

当多个分支修改了同一文件的同一部分时,合并时会产生冲突。解决冲突步骤如下:

  1. 查看冲突文件:

    git status
  2. 手动编辑冲突文件,保留需要的修改。

  3. 使用冲突解决工具:

    • 编辑器自带的合并工具:如 VSCode 提供了图形化的冲突解决界面,可以直观地选择保留的修改。
    • 外部工具
      • Meld:一个免费开源的可视化比较和合并工具。
      • Beyond Compare:功能强大的文件和文件夹比较工具。
      • 配置方式:
        git config --global merge.tool <工具名>
        git config --global mergetool.<工具名>.path <工具路径>
  4. 将解决后的文件添加到暂存区:

    git add <文件名>
  5. 提交冲突解决记录:

    git commit -m "解决冲突"

2.3.6 远程分支操作

  1. 查看远程分支:

    git branch -r
  2. 跟踪远程分支:

    git checkout -b <本地分支名> origin/<远程分支名>

2.4 远程仓库操作

2.4.1 添加远程仓库

  1. 添加远程仓库:

    git remote add origin <远程仓库地址>
  2. 查看远程仓库:

    git remote -v

2.4.2 推送代码到远程仓库

  1. 推送代码到默认分支:

    git push origin main
  2. 推送代码到指定分支:

    git push origin <分支名>
  3. 设置本地分支与远程分支的关联关系:

    git push -u origin <分支名>

    使用 -u 参数后,后续可以直接使用 git pushgit pull,无需每次指定远程分支。

2.4.3 拉取代码到本地仓库

  1. 获取远程仓库的最新信息但不合并到本地分支:

    git fetch origin

    此命令会下载远程仓库的最新提交记录和分支信息,但不会对当前分支造成任何影响,便于细粒度控制更新流程。

  2. 拉取远程分支的最新代码并合并到本地分支:

    git pull origin <分支名>
  3. 拉取并合并到当前分支:

    git pull

三、Git 进阶技巧

3.1 撤销操作

3.1.1 撤销暂存区的修改

当你已经将文件添加到暂存区,但想撤销这些修改时,可以使用以下命令:

  1. 从暂存区移除文件,但保留工作区中的修改:

    git reset HEAD <文件名>

    例如:

    git reset HEAD example.txt

    此命令会将文件从暂存区移除,但不会删除工作区的修改。

  2. 撤销所有文件的暂存:

    git reset

3.1.2 回退版本

Git 允许你回退到以前的提交版本。

  1. 回退到上一个版本:

    git reset --hard HEAD~1

    此命令会将工作区和版本库都回退到上一个提交。

  2. 回退到指定版本:

    git reset --hard <提交哈希值>

    你可以通过 git log 查看提交哈希值。

  3. 仅回退版本库,不影响工作区文件:

    git reset --soft <提交哈希值>
  4. 参数区别:

    • --hard:重置暂存区和工作区,使其与指定的提交保持一致。
    • --soft:只重置版本库,暂存区和工作区不变。
    • --mixed(默认):重置版本库和暂存区,工作区不变。
  5. 撤销某次提交但保留修改:

    git revert <提交哈希值>

    此命令会生成一个新的提交,用于撤销指定提交的更改,而不会直接修改历史。

3.1.3 恢复删除的文件

如果你误删了文件,可以通过以下命令恢复:

  1. 恢复删除的文件:

    git checkout -- <文件名>

    此命令会将文件恢复到最近一次提交时的状态。

  2. 恢复整个目录:

    git checkout -- .

3.1.4 如何使用 reflog 恢复丢失的提交

git reflog 记录了所有 HEAD 的变动历史,即使提交已经被 git reset 删除,也可以通过以下方法恢复:

  1. 查看 HEAD 历史:

    git reflog
  2. 找到目标提交的哈希值,恢复到该提交:

    git reset --hard <提交哈希值>

3.2 标签管理

3.2.1 创建标签

Git 支持两种标签类型:轻量标签和附注标签。

  1. 创建轻量标签:

    git tag <标签名>
  2. 创建附注标签:

    git tag -a <标签名> -m "标签描述"
  3. 给过去的提交打标签:

    git tag -a <标签名> <提交哈希值> -m "标签描述"

    这种方式可以为历史提交添加标记。 Git 支持两种标签类型:轻量标签和附注标签。

3.2.2 查看标签

  1. 查看所有标签:

    git tag
  2. 查看特定标签的信息:

    git show <标签名>

3.2.3 删除标签

  1. 删除本地标签:

    git tag -d <标签名>
  2. 强制删除本地标签:

    git tag -d -f <标签名>
  3. 删除远程标签:

    git push origin --delete <标签名>

3.2.4 轻量标签和附注标签的区别

  • 轻量标签: 仅仅是对某个提交的引用,不包含额外信息。
  • 附注标签: 存储了标签的作者、日期和说明信息,适合用于版本发布。

3.2.5 标签的签名与验证

  1. 创建带签名的标签:

    git tag -s <标签名> -m "标签描述"
  2. 验证标签:

    git tag -v <标签名>

3.3 .gitignore 文件

3.3.1 忽略文件的规则

.gitignore 文件用于指定 Git 应忽略的文件或目录。

  1. 通配符规则:

    • 忽略特定文件:
      *.log
    • 忽略特定目录:
      /build/
    • 忽略某目录下的特定文件:
      /logs/*.log
    • 匹配单个字符:
      ?.log
      例如:a.log 会被忽略,但 ab.log 不会。
    • 匹配括号内的任意字符:
      [abc].log
      例如:a.logb.logc.log 会被忽略,但 d.log 不会。
    • 匹配任意层级的目录:
      **/temp/
      忽略项目中任意位置的 temp 目录。
  2. 排除规则: 使用 ! 取消忽略:

    !important.log
  3. 全局忽略文件: 在 ~/.gitignore_global 文件中定义全局忽略规则:

    echo "*.log" >> ~/.gitignore_global
    git config --global core.excludesfile ~/.gitignore_global

    .gitignore 文件用于指定 Git 应忽略的文件或目录。

  4. 通配符规则:

    • 忽略特定文件:
      *.log
    • 忽略特定目录:
      /build/
    • 忽略某目录下的特定文件:
      /logs/*.log
  5. 排除规则: 使用 ! 取消忽略:

    !important.log

3.3.2 .gitignore 文件的语法

.gitignore 的规则遵循以下语法:

  1. 空行或 # 开头的行会被忽略。
  2. * 匹配任意字符。
  3. / 表示目录。
  4. ! 表示排除。

3.3.3 如何排除已经提交的文件

如果文件已经被提交到版本库中,.gitignore 不会自动忽略它。需要使用以下命令:

  1. 从版本库中移除文件,但保留在本地:

    git rm --cached <文件名>
  2. 提交更改:

    git commit -m "移除已提交的文件"

3.3.4 .git/info/exclude

这是 本地仓库私有的忽略规则

  • 位置:

    .git/info/exclude
  • 语法:

    • 完全与 .gitignore 一致
  • 特点:

    • 不在版本控制中
    • 只对本地仓库有效
    • 切换分支后仍然保留

适用场景

  • 个人在当前仓库中需要忽略的文件,但不希望污染团队的 .gitignore

3.3.5 全局忽略文件

如果你希望在 所有项目 中都自动忽略一些个人文件(如编辑器、系统缓存),可以设置全局 ignore。

配置方法

1️⃣ 创建一个全局 ignore 文件:

touch ~/.gitignore_global

2️⃣ 加入你自己的规则:

.DS_Store
*.swp
*.sublime-workspace
.vscode/

3️⃣ 告诉 Git 使用它:

git config --global core.excludesfile ~/.gitignore_global

4️⃣ 查看设置:

git config --global --get core.excludesfile

特点总结

  • 对所有仓库生效
  • 不会被任何项目提交
  • 非常适合个人习惯、IDE 文件

3.3.6 三者对比总结表

文件位置作用范围是否跟版本库走是否随分支切换
.gitignore项目全体✔️ 是✔️ 会
.git/info/exclude当前仓库私有❌ 否❌ 不会
~/.gitignore_global所有仓库(全局)❌ 否❌ 不会

3.4 Git 分支重命名与管理

团队协作里也常会遇到分支命名管理,比如重命名分支删除远端分支误删后恢复

下面给出详细命令与解释:

3.4.1 修改本地分支名称

语法

git branch -m 旧名 新名

如果你在 yangyz/net_resolver 上想改名为 yangyz/to_delete/net_resolver

git branch -m yangyz/net_resolver yangyz/to_delete/net_resolver

如果你正好就在这个分支上,也可以省略旧名:

git branch -m yangyz/to_delete/net_resolver

3.4.2 推送新的远端分支

重命名后需要把新分支推到远端:

git push origin yangyz/to_delete/net_resolver

3.4.3 删除远端的旧分支

推送完新分支后,可以安全地删除远端的旧分支:

git push origin --delete yangyz/net_resolver

3.4.4 一键顺序命令示例

最常用的改名+推送+删除组合:

git branch -m yangyz/net_resolver yangyz/to_delete/net_resolver
git push origin yangyz/to_delete/net_resolver
git push origin --delete yangyz/net_resolver

3.4.5 删除远端分支后会永久丢失吗?

❗️不会立刻丢失。Git 的分支只是一个指针,删除远端分支只是把远端的这个指针移除。提交对象还在,只要有人保留引用就不会丢。

✔️ 只要有人本地还保留着那个分支或者有那个 commit 的哈希,就可以恢复。

3.4.6 恢复已删除分支的方法

方法一:用最后的 commit id

如果你知道最后的提交哈希:

git checkout -b 恢复的分支名 <commit-id>
git push origin 恢复的分支名

方法二:通过 reflog 查找

即使分支删除,本地还会留有记录:

git reflog

找到相关的 commit id,然后同上建分支。

3.4.7 建议:安全删除前打个 tag

如果想完全可恢复,可以先打一个 tag:

git tag backup/yangyz_net_resolver yangyz/net_resolver
git push origin backup/yangyz_net_resolver

以后随时可以从这个 tag 恢复:

git checkout -b 恢复分支名 backup/yangyz_net_resolver

四、Git 实战演练

4.1 本地领先远端,如何强制刷新本地,与远端一致

当本地分支的内容与远端不一致时,可以使用以下命令强制刷新:

# 确认当前分支
git branch
# 拉取最新的远端分支信息
git fetch
# 重置本地分支
git reset --hard origin/master

4.2 添加文件

在提交文件之前,需要将其添加到暂存区。

git add test.sv

4.3 添加错误撤回文件

如果文件已被 git add 添加到暂存区,但未提交,可以撤回到工作区:

# 从暂存区移除文件,保留工作区的修改
git reset HEAD file.txt

4.4 已经添加的文件如何看修改内容

查看暂存区中已添加文件的修改内容:

git diff --cached file.txt

4.5 查询本地状态

查看当前分支的文件状态:

git status -uno

4.6 如何提交代码到远端

提交代码时,先拉取远端更新并解决冲突,然后推送:

git pull --rebase
git push ssh://username@host:port/repository HEAD:refs/for/master

4.7 如何放弃某个文件的修改,恢复到本地库中版本

恢复文件到上一次提交的状态:

git checkout -- ./test.sv

4.8 如何切换本地分支

  1. 查看本地分支:

    git branch
  2. 查看所有分支,包括远端分支:

    git branch -a
  3. 切换到已有分支:

    git checkout bname
    git switch bname # Git 2.23+
  4. 切换并创建新分支:

    git checkout -b new-bname
    git switch -c new-bname # Git 2.23+

4.9 git pull 报错后,强制用远端覆盖本地

当拉取远端代码报错时,可以用以下方法覆盖本地分支:

# 获取所有分支和标签的最新内容
git fetch --all
# 重置本地分支到远端状态,丢弃所有本地未提交的修改
git reset --hard origin/master

4.10 如何查看本地尚未合并的提交

  1. 查看文件名:

    git log origin/master..HEAD --name-only
  2. 查看具体内容:

    git diff <commit-hash>^ <commit-hash> -- <file-path>

4.11 通过 git log 查询日志时只看某个人的提交

可以通过作者过滤提交记录:

git log --author="作者名字或电子邮件"

4.12 更新本地代码到某个特定版本

将本地代码回滚到某个版本:

git reset --hard <commit-hash>

4.13 本地 commit 了 2 次,如何合并

  1. 如果本地有多次提交,且中间拉取了别人的代码:

    git reset --soft <最新的公共提交哈希值>
  2. 如果只需要修改最近一次提交:

    git add .
    git commit --amend

4.14 本地修改了多个文件,只想提交其中一部分

  1. 提交前,将不想提交的部分暂存:

    git stash
  2. 提交后,恢复之前的暂存:

    git stash pop

4.15 push 到服务器后有问题被驳回,如何修改再次提交

如果提交后发现问题,且未拉取新的远程更新:

  1. 找到需要回滚的版本:

    git log -3
  2. 回滚到指定版本:

    git reset --soft <commit-hash>
  3. 修改代码并重新提交:

    git add .
    git commit -m "更新提交"
    git push

4.16 如何查看当前 stash 文件

  1. 查看所有 stash 条目:

    git stash list
  2. 查看特定 stash 的内容:

    git stash show stash@{n}
  3. 查看文件具体修改:

    git stash show -p stash@{n}

4.17 如何还原 stash

  1. 应用 stash:

    git stash apply stash@{n}
  2. 应用并删除 stash:

    git stash pop
  3. 删除指定 stash:

    git stash drop stash@{n}
  4. 创建新分支并应用 stash:

    git stash branch new-branch-name stash@{n}

4.18 基于 Feature 分支的开发流程

在团队协作开发中,使用 Feature 分支(功能分支)是管理代码变更的常见工作流。这种方法能够将新功能的开发与主分支隔离,降低直接在主分支上开发带来的风险,同时确保代码在合并时经过精心整理。以下是具体的操作步骤:

操作流程

  1. 创建功能分支

    • 从主分支(通常是 mastermain)创建一个新的功能分支:
      git checkout -b feature
    • 说明:此步骤会基于当前分支创建并切换到名为 feature 的新分支。
  2. 开发与提交代码

    • 在功能分支中进行开发工作,并根据需要多次提交代码:
      git commit -m "实现 XXX 功能"
      git commit -m "修复 XXX 问题"
    • 说明:提交信息应简洁明确,便于团队成员理解变更内容。
  3. 推送功能分支到远程仓库

    • 将本地的功能分支推送到远程仓库,以便共享或备份:
      git push origin feature
    • 说明:此操作将创建远程分支 feature,并同步本地的提交。
  4. 切换回主分支

    • 功能开发完成后,切换回主分支以准备合并:
      git checkout master
    • 说明:确保当前分支为主分支。
  5. 使用 Squash 合并功能分支

    • 将功能分支的所有提交合并为一个整洁的提交记录:
      git merge --squash feature
    • 说明:--squash 参数会将功能分支中的所有变更整合为一次提交,而不会保留中间的提交记录,适合清理开发中的碎片化历史。
  6. 解决冲突(如有)并提交合并结果

    • 如果在合并过程中产生冲突,按照以下步骤解决:
      • 查看冲突文件:
        git status
      • 手动编辑冲突文件并保留需要的内容。
      • 将解决后的文件标记为已处理:
        git add <冲突文件名>
    • 提交合并结果:
      git commit -m "整合 feature 分支的开发内容"
    • 说明:解决冲突后提交的变更记录通常包含对功能分支的说明,便于历史追溯。

上述工作流通过将新功能的开发隔离在独立分支中,有效减少了对主分支的干扰。同时,使用 --squash 参数合并代码能够保持提交历史的清晰,提升代码库的可读性。这种流程非常适合多人协作和迭代开发的场景。

4.19 如何导出某一次commit的文件

这里导出的是这次commit修改的文件,而不是快照

手动导出修改文件

  1. 查看你要导出的 commit 中修改的文件列表:
git diff-tree --no-commit-id --name-only -r <commit_hash>

这条命令会列出 <commit_hash> 中所有修改过的文件。

  1. 使用 git show 导出每个文件到你想要的位置。例如:
git show <commit_hash>:<file_path> > /your/output/directory/<file_name>

​ 这样可以逐个文件使用这个命令,将它们导出到你需要的位置。

使用脚本导出所有修改的文件

#!/bin/bash

# 替换为你要导出的 commit 的哈希值
commit_hash=<commit_hash>
output_dir=/your/output/directory

# 创建输出目录
mkdir -p $output_dir

# 获取所有修改的文件并导出
for file in $(git diff-tree --no-commit-id --name-only -r $commit_hash)
do
# 提取文件的目录结构并创建对应的子目录
sub_dir=$(dirname $file)
mkdir -p $output_dir/$sub_dir

# 使用 git show 导出文件到对应的子目录中
git show $commit_hash:$file > $output_dir/$file
done

echo "所有文件已导出至 $output_dir"

4.20 查询本地修改文件的最后修改时间

有时需要通过 git status -uno 获取修改文件的列表,并查询这些文件在本地磁盘上的最后修改时间,而不是查看 Git 提交日志的修改时间。这可以通过以下 Python 脚本实现。

脚本功能说明

  1. 使用 git status -uno 获取修改的文件列表。
  2. 查询这些文件在本地磁盘上的最后修改时间。
  3. 输出文件路径及其修改时间,格式为 YYYY-MM-DD HH:MM:SS

脚本代码

import subprocess
import os
from datetime import datetime

def get_modified_files():
"""
获取通过 `git status -uno` 标记为修改的文件列表。
"""
result = subprocess.run(['git', 'status', '-uno', '--porcelain'],
stdout=subprocess.PIPE, text=True)
files = []
for line in result.stdout.strip().split('\n'):
if line: # 只处理非空行
parts = line.strip().split(maxsplit=1)
if len(parts) == 2:
files.append(parts[1])
return files

def get_local_modification_time(file_path):
"""
获取文件在本地磁盘上的最后修改时间。
"""
try:
mtime = os.path.getmtime(file_path)
# 格式化时间为 'YYYY-MM-DD HH:MM:SS'
return datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M:%S')
except FileNotFoundError:
return "文件不存在"

def main():
# 获取修改的文件列表
modified_files = get_modified_files()
if not modified_files:
print("没有修改的文件。")
return

print("本地文件的最后修改时间:")
for file in modified_files:
modification_time = get_local_modification_time(file)
print(f"{file}:\n 修改时间: {modification_time}")

if __name__ == "__main__":
main()

使用方式

  1. 将脚本保存为 check_local_mod_time.py

  2. git 项目根目录运行脚本:

    python3 check_local_mod_time.py

输出示例

假设 git status -uno 输出如下:

modified: ./source/dsim/args/AnalArg.cpp
modified: ./source/dsim/cg/CGLockingBlockUI.cpp

运行脚本后会输出:

本地文件的最后修改时间:
./source/dsim/args/AnalArg.cpp:
修改时间: 2024-12-09 10:28:42
./source/dsim/cg/CGLockingBlockUI.cpp:
修改时间: 2024-12-09 09:15:30

核心逻辑解释

  1. 获取文件列表:
    • 使用 git status -uno --porcelain 获取文件状态和路径。
    • 提取输出中的文件路径。
  2. 查询本地修改时间:
    • 通过 os.path.getmtime(file_path) 获取文件的最后修改时间戳。
    • 使用 datetime.fromtimestamp() 将时间戳格式化为人类可读的字符串。
  3. 异常处理:
    • 如果文件不存在(例如被删除),会捕获 FileNotFoundError 并打印提示。

注意事项:

  • 确保运行脚本时当前目录在 Git 项目的根目录。
  • 如果文件路径是相对路径,脚本会直接按相对路径查找并获取修改时间。
  • 如果需要转换为绝对路径,可修改 os.path.getmtime 中的路径为 os.path.abspath(file_path)

4.21 如何暂时屏蔽某个文件的修改

git update-index --assume-unchanged file.txt
git update-index --no-assume-unchanged file.txt
  • 主要用于让 Git 假设某些文件在工作区中未被修改,从而跳过对这些文件的检查,以提高性能和减少不必要的操作

  • 在大型项目中,工作区可能包含大量文件。如果某些文件在开发过程中很少被修改,或者这些文件的修改不需要被 Git 跟踪(例如配置文件、日志文件等)

    • 我主要用于屏蔽.gitignore文件

4.22 git ls_files用法简介

git ls-files 用于列出 Git 索引(Index,也叫暂存区)中被 Git 跟踪的所有文件。 这些文件通常已经被 git add,并处于 Git 管理之中(即使还未 commit)。

4.22.1 列出所有 Git 管理的文件

git ls-files

4.22.2 查看索引信息(包括 SHA1 和权限)

git ls-files --stage

# 输出格式:
100644 5c0f22a1... 0 README.md

说明:

  • 100644: 权限模式
  • 5c0f22a1...: blob 对象的哈希
  • 0: stage 号
  • README.md: 文件路径

4.22.3 查看未被 Git 管理的文件(untracked)

git ls-files --others --exclude-standard

该命令常用于查找工作区中尚未 add 的纯本地文件

4.22.4 查看被删除但尚未 git rm 的文件

git ls-files --deleted

显示:文件已被从磁盘删除,但仍在 index 中(已追踪但消失)。

4.22.5 查看已被修改但未 add 的文件

git ls-files --modified

4.22.6 查找特定路径、正则匹配

git ls-files '*.cpp'

4.22.7 列出某个子目录下所有被 Git 管理的文件:

git ls-files src/

4.22.8 列出 src 目录下所有 .h 文件

git ls-files 'src/**/*.h'

4.22.9 列出所有被追踪文件,但排除 test 目录

git ls-files ':!test/*'

: 是路径spec前缀,:! 表示排除路径。

4.23 撤销最近一次提交

git reset --soft HEAD~1
  • 适用于只想撤销最近一次提交,但保留更改内容的情况

4.24 导出补丁文件

4.24.1 导出

# 导出单个 commit
# 假设要导出 abc1234 这个提交,-1 表示导出 1 个提交
# 当前目录下会生成一个形如 0001-commit-message.patch 的文件
git format-patch -1 abc1234

# 导出一段提交
# 导出从 commitA 到 commitB(不包含 commitA,包含 commitB)
git format-patch commitA..commitB

# 导出到指定目录
# 通过 -o 参数,把补丁文件输出到指定目录
git format-patch -1 abc1234 -o ./patches

# 使用 git show 导出简单 diff
# 如果只是想生成一个简单的 diff(不含邮件头),也可以使用 git show
# 这会把 abc1234 的变更内容保存到 abc1234.patch 文件
git show abc1234 > abc1234.patch

4.24.2 导入/应用

# git apply
# 只应用文件内容(不保留提交元信息)
# 适合只想把改动合并到当前工作区,自己提交。
git apply 0001-xxx.patch

# git am
# 保留原作者、提交信息、提交时间
# 适合想要原封不动地把提交引入仓库,推荐做法。
git am 0001-xxx.patch

五、Git 学习资源

5.1 官方文档

Git 的官方文档是学习和参考的首选资源,详细记录了所有 Git 命令及其用法。

  1. Git 官方文档:提供多语言版本,涵盖基础到高级的所有功能。
  2. Pro Git:一本由官方维护的免费电子书,适合系统学习 Git。
  3. Git man 手册:命令行中的 man git 对应的详细文档,适合查询特定命令的参数和用法。
  4. Git Cheat Sheet:一份官方速查表,列出了常用 Git 命令及其功能,适合快速参考。
  5. Git Command Explorer:一个交互式工具,帮助用户根据具体任务快速找到所需的 Git 命令。

5.2 在线教程

  1. 廖雪峰的 Git 教程:深入浅出的中文教程,涵盖 Git 的主要功能和典型使用场景。
  2. Atlassian Git 教程:结构化的 Git 入门和进阶课程,附带实践示例。
  3. Learn Git Branching:一个交互式网站,帮助用户直观理解 Git 分支管理和操作。
  4. Codecademy Learn Git:互动性强的英文教程,适合从零基础开始学习。
  5. Udacity Version Control with Git:一个系统的版本控制课程,涵盖 Git 的核心概念和高级功能。

5.3 书籍推荐

  1. 《Pro Git》(作者:Scott Chacon 和 Ben Straub):

    • Git 官方推荐的一本权威书籍,从基础到高级功能都有覆盖,适合系统学习。
  2. 《Git 权威指南》(作者:蒋鑫):

    • 专注于实践和应用,结合开发场景介绍 Git 的使用技巧。
  3. 《Version Control with Git》(作者:Jon Loeliger 和 Matthew McCullough):

    • 更偏重于 Git 的原理和内部实现,适合高级用户深入研究。
  4. 《Git Pocket Guide》(作者:Richard E. Silverman):

    • 一本小巧便携的指南,适合快速查询命令和解决问题。
  5. 《Head First Git》(作者:Rachael Tatman):

    • 面向入门级用户,以生动有趣的方式讲解 Git 的核心概念。
  6. 《Git in Practice》(作者:Mike McQuaid):

    • 适合进阶用户,介绍了在实际开发中应用 Git 的各种技巧和高级用法。

5.4 常用工具

  1. 图形化 Git 客户端:

    • GitHub Desktop:适合 GitHub 用户,支持简单的分支管理和同步操作。
    • Sourcetree:由 Atlassian 提供,功能强大,适合初学者和进阶用户。
    • GitKraken:跨平台客户端,界面友好,支持复杂的分支和合并管理。
  2. 命令行增强工具:

    • oh-my-zsh 的 Git 插件:提供更高效的命令补全和快捷方式。
    • tig:命令行 Git 可视化工具,方便查看提交历史和分支结构。
  3. 集成开发环境(IDE)支持:

    • VSCode:通过 Git 插件支持常用 Git 功能,操作便捷。
    • JetBrains 系列 IDE:内置 Git 支持,适合大型项目的版本管理。
  4. Git 工作流工具:

    • Gitflow:一种基于分支的开发模型,适合复杂项目的版本管理。
    • GitHub Flow:一种轻量级工作流,适合快速开发和持续部署场景。

5.5 社区资源

  1. GitHub CommunityGitHub Discussions

    • 全球最大的代码托管平台社区,交流项目管理和 Git 使用经验。
  2. Stack OverflowGit 标签

    • 涵盖 Git 常见问题解答,适合查找解决方案。
  3. Reddit Git Communityr/git

    • 讨论 Git 的新功能、最佳实践和使用心得。
  4. Git Meetup

    • 参加线下或在线的技术分享活动,与其他开发者交流。
  5. Gitee 社区Gitee

    • 国内知名的代码托管平台,适合了解本土化的 Git 使用场景。
  6. Git 中文社区Git 中文社区

    • 专为中文开发者打造的学习交流平台,提供大量本地化的资源。
  7. SegmentFault Git 专题SegmentFault Git 专题

    • 聚合了 Git 的相关问题和技术讨论,是中文开发者的宝贵资源。

六、Git 的高级功能

6.1 子模块管理

子模块(Submodule)是 Git 中用于管理嵌套项目的功能,适合在一个仓库中引用另一个仓库的场景,例如依赖库管理。

  1. 添加子模块:

    git submodule add <子模块仓库地址> <子模块目录>

    例如:

    git submodule add https://github.com/example/repo.git libs/example
  2. 初始化子模块: 克隆带有子模块的仓库后,需要初始化和更新子模块:

    git submodule init
    git submodule update
  3. 更新子模块: 当子模块的仓库有更新时,可以通过以下命令获取最新代码:

    git submodule update --remote
  4. 删除子模块:

    • 从配置文件中删除子模块:
      git submodule deinit -f <子模块目录>
    • 删除子模块目录:
      rm -rf <子模块目录>
    • 删除相关记录:
      git rm --cached <子模块目录>

6.2 Git Hooks

Git Hooks 是一组脚本,可在特定 Git 操作触发时自动执行。例如,在提交代码前检查代码风格。

  1. 常用 Hook 类型:

    • pre-commit:在执行 git commit 前触发。
    • post-commit:在提交完成后触发。
    • pre-push:在执行 git push 前触发。
  2. 使用示例:

    • 创建 Hook 文件(以 pre-commit 为例):
      touch .git/hooks/pre-commit
      chmod +x .git/hooks/pre-commit
    • 编辑 Hook 文件,添加如下内容:
      #!/bin/sh
      echo "Running pre-commit checks..."
      exit 0
      注意:返回非零值会阻止提交。
  3. Hook 的共享: 默认情况下,Git Hooks 只在本地生效。若需共享,可以使用 husky 等工具管理 Hooks。

6.3 配置别名

Git 提供了配置别名的功能,用于简化常用命令。

  1. 配置别名:

    git config --global alias.<别名> '<实际命令>'

    例如:

    git config --global alias.st status
    git config --global alias.co checkout
    git config --global alias.br branch
    git config --global alias.ci commit
  2. 使用别名: 配置完成后,可以直接使用别名:

    git st
    git co main
  3. 查看别名配置:

    git config --global --get-regexp alias

6.4 差异分析和代码审查

Git 提供多种工具用于分析文件差异和代码变更,便于团队协作和代码质量管理。

  1. 查看文件差异:

    git diff
    • 查看暂存区和工作区的差异:
      git diff --cached
    • 查看两个分支间的差异:
      git diff <分支1>..<分支2>
  2. 图形化工具:

    • Git 自带的差异工具:
      git difftool
    • 配置外部差异工具(如 Meld):
      git config --global diff.tool meld
      git difftool
  3. 使用代码审查工具:

    • GitHub Pull Requests:提交代码变更后进行审查。
    • Gerrit:适用于大型团队的代码审查工具。
    • Phabricator:全面的协作和代码审查平台。
  4. 差异统计: 查看某次提交的变更统计:

    git show --stat <提交哈希值>

七、Git 性能优化与大规模使用

7.1 优化大仓库性能

随着仓库规模的增长,Git 操作可能变得缓慢。以下是一些优化大仓库性能的建议:

  1. 减少历史记录查询范围

    • 克隆时仅下载最近的提交记录:
      git clone --depth=1 <仓库地址>
    • 更新时仅获取最新的更改:
      git fetch --depth=1
  2. 优化大文件和二进制文件的管理

    • 使用 Git LFS(Large File Storage):
      git lfs track "*.bin"
      git add .gitattributes
      git commit -m "Add Git LFS tracking"
      Git LFS 可将大文件存储在外部存储中,减少仓库的实际大小。
  3. 按需清理仓库

    • 移除不再需要的对象:
      git gc --prune=now
    • 检查和修复仓库中的潜在问题:
      git fsck
  4. 按需分离历史

    • 对于历史很久的提交,可以使用 git filter-repo(或旧的 git filter-branch)分离老旧历史:
      git filter-repo --path <路径> --force
    • 仅保留最近的历史用于日常开发。
  5. 改进网络性能

    • 配置更高效的压缩算法:
      git config --global core.compression 9
    • 使用更快的协议:
      git config --global url."git@github.com:".insteadOf "https://github.com/"

7.2 数据完整性检查

Git 使用哈希值(SHA-1 或 SHA-256)确保数据完整性。以下是检查和修复数据的方法:

  1. 验证对象完整性

    • 检查仓库中所有对象是否完整:
      git fsck --full
      如果发现问题,Git 会报告丢失或损坏的对象。
  2. 修复丢失的引用

    • 通过 reflog 恢复丢失的提交:
      git reflog
      git reset --hard <提交哈希值>
  3. 检测冲突或重复提交

    • 查看重复提交的对象:
      git log --oneline --graph --all
  4. 防止损坏数据的上传

    • 使用 pre-receive hook 防止错误的数据被推送到远程: 在 .git/hooks/pre-receive 中添加校验逻辑:
      #!/bin/sh
      git fsck --strict
      exit $?
  5. 启用 SHA-256 支持

    • 对于高安全性需求的项目,可以使用 Git 的 SHA-256 哈希模式(需要 Git 2.29+):
      git init --object-format=sha256
      注意:旧版本 Git 无法读取 SHA-256 仓库。

通过以上方法,开发者可以在大规模使用 Git 时确保高效和可靠的版本管理。