拾忆🍂拾忆🍂
  • Ollama
  • OpenClaw-飞书
  • OpenClaw
  • skills
  • MySQL
  • Oracle
  • PostgreSQL
  • MyBatis
  • API升级
  • HMOS
  • HMOS 官方示例项目集
  • HDC
  • 百变小组件
  • 元服务
  • Java
  • MinIO
  • Stream
  • JSP & Struts2
  • Spring
  • FFmpeg
  • Linux
  • docker
  • Git
  • Nginx
  • 终端快捷键
  • Adobe Audition
  • Aseprite
  • cpp
  • Excel
  • K
  • Markdown基本用法
  • MuseScore 4.x
  • UVR
  • Windows
  • emoji-cheat-sheet
  • IDE快捷键
  • obs-studio
  • YOLO
  • Python
  • VuePress 2.x
  • 内置组件
  • markdown-container
  • markdown-ext
  • markdown-hint
  • markdown-preview
  • markdown-tab
  • Markdown扩展语法
  • 插件配置
  • prismjs
  • 样式
  • CSS
  • JS
  • TS
  • Vue3
主页
梦的开始🌅
  • Ollama
  • OpenClaw-飞书
  • OpenClaw
  • skills
  • MySQL
  • Oracle
  • PostgreSQL
  • MyBatis
  • API升级
  • HMOS
  • HMOS 官方示例项目集
  • HDC
  • 百变小组件
  • 元服务
  • Java
  • MinIO
  • Stream
  • JSP & Struts2
  • Spring
  • FFmpeg
  • Linux
  • docker
  • Git
  • Nginx
  • 终端快捷键
  • Adobe Audition
  • Aseprite
  • cpp
  • Excel
  • K
  • Markdown基本用法
  • MuseScore 4.x
  • UVR
  • Windows
  • emoji-cheat-sheet
  • IDE快捷键
  • obs-studio
  • YOLO
  • Python
  • VuePress 2.x
  • 内置组件
  • markdown-container
  • markdown-ext
  • markdown-hint
  • markdown-preview
  • markdown-tab
  • Markdown扩展语法
  • 插件配置
  • prismjs
  • 样式
  • CSS
  • JS
  • TS
  • Vue3
主页
梦的开始🌅
  • 「从开始,到永久」
  • AI

    • Ollama
    • OpenClaw-飞书
    • OpenClaw
    • skills
  • Database

    • MySQL
    • Oracle
    • PostgreSQL
    • MyBatis
  • HarmonyOS

    • API升级
    • HMOS
    • HMOS 官方示例项目集
    • HDC
    • 百变小组件
    • 元服务
  • Java

    • Java
    • MinIO
    • Stream
    • JSP & Struts2
    • Spring
  • Linux

    • FFmpeg
    • Linux
    • docker
    • Git
    • Nginx
    • 终端快捷键
  • Others

    • Adobe Audition
    • Aseprite
    • cpp
    • Excel
    • K
    • Markdown基本用法
    • MuseScore 4.x
    • UVR
    • Windows
    • emoji-cheat-sheet
    • IDE快捷键
    • obs-studio
    • YOLO
  • Python

    • Python
  • VuePress

    • VuePress 2.x
    • 内置组件
    • markdown-container
    • markdown-ext
    • markdown-hint
    • markdown-preview
    • markdown-tab
    • Markdown扩展语法
    • 插件配置
    • prismjs
    • 样式
  • Web

    • CSS
    • JS
    • TS
    • Vue3
  • 主页

Git

本地修改了分支-远程分支已有新提交-如何拉取🌸

暂存后拉取

  1. 提交本地更改:

    使用 git add 和 git commit 将本地更改提交到本地仓库。确保本地代码没有未提交的更改。

  2. 拉取远程分支:

    # your_branch_name 更改为本地分支名称(如 master/main/develop)
    git pull origin <your_branch_name>
    
  3. 解决冲突:

    如果在执行 git pull 时发生冲突,Git 会提示你解决冲突。手动 解决冲突 并保存修改。

  4. 提交合并:

    一旦冲突解决完毕,使用 git add 命令将解决后的文件添加到暂存区,并使用 git commit 提交合并的更改。

  5. 推送到远程分支:

    git push origin <your_branch_name>
    

使用 git stash

如果本地想在不执行commit的情况下拉取远程最新提交,需要使用 git stash 储藏工作区修改,再执行拉取操作,最后恢复修改。

  1. 储藏本地修改

    git stash push -m "暂存本地修改(不包含未跟踪文件)"
    
    • git stash push:将工作目录和暂存区的修改保存到“储藏栈”中,工作区会恢复到上次提交的干净状态。
    • -m "描述":可选,添加备注便于识别(推荐使用)。
  2. 拉取远程最新提交

    # your_branch_name 更改为本地分支名称(如 master/main/develop)
    git pull origin <your_branch_name>
    
    • 此时工作区无本地修改,可安全拉取远程最新代码。
  3. 恢复本地修改

    git stash pop
    
    • 将最近一次储藏的修改恢复到工作区,并自动删除该储藏记录(若有冲突则不会自动删除)。
    • 若恢复时发生冲突(文件被远程修改且本地也修改了),需手动解决冲突(冲突文件会包含 <<<<<<< 标记)。

    重要

    当执行 git stash pop 时,Git 会尝试将储藏的修改应用到当前工作目录。文件会有以下状态:

    四种状态

    1. Auto-merging
      • 含义: 表示 Git 成功自动合并 这些文件。远程和 stash 中的修改互不干扰,Git 能够自动将这些修改合并。
      • 举例: stash 中的一个文件,修改了第10行;git pull 下来的版本修改了第20行。这两处修改 Git 就会自动合并。
    2. Changes to be committed
      • 含义: 这表示在您执行 git stash 时,有些修改已经使用 git add 添加到了暂存区。在 pop 之后,这些修改 被成功地、自动地 恢复到了暂存区状态,等待您提交。
      • 举例: 先修改了 a.txt 并 git add 了它,然后执行了 git stash。当 pop 时,这个对 a.txt 的修改就会直接出现在暂存区,而不是工作区。
    3. Unmerged paths
      • 含义: 这是最关键的一项。表示 发生了合并冲突。有些文件在 stash 的版本和 git pull 后的当前版本中,对 相同的位置 进行了修改,Git 无法自动决定该保留哪个修改,因此需要手动解决这些冲突。
      • 文件状态: 这些文件会被标记为 both modified。
      • 处理方式: 您需要打开这些文件,解决标记为 <<<<<<<, =======, >>>>>>> 的冲突部分,然后使用 git add 命令将解决后的文件标记为已解决。
    4. Untracked files
      • 含义: 这表示在您执行 git stash 时,工作目录中存在一些新增的、未被Git跟踪的文件。默认的 git stash 不会保存这些未跟踪文件(除非使用 git stash -u 或 -a 选项)。这里的提示可能是在告诉您这些文件依然存在,或者是从 stash 中恢复出来的(如果您用了 -u)。

    当发生冲突时

    存在 Unmerged paths(合并冲突),apply 操作 被认为是没有“完全成功”的。Git 出于安全考虑,中断了自动删除的步骤,并给您留下了提示:

    警告

    "The stash entry is kept in case you need it again."

    这是由于:git stash pop 在应用存储时遇到了合并冲突不会自动删除 stash 。

    这个命令实际上是两个操作的组合:

    1. git stash apply: 尝试将存储的修改应用到当前工作目录。
    2. git stash drop: 只有在 apply 成功完成(没有产生任何冲突)的情况下,才会自动删除对应的存储条目。

    在您的情况下,由于存在 “Unmerged paths” (合并冲突),apply 操作被认为是没有“完全成功”的。Git 出于安全考虑,中断了自动删除的步骤,并给您留下了提示:

    这样做的目的是:

    • 给您一个回退的机会: 如果您在解决冲突的过程中搞砸了,或者想放弃这次合并,您可以通过 git stash apply 重新应用这个被保留的 stash,或者使用 git checkout 来撤销对冲突文件的修改,然后换个方式重新尝试。
    • 防止数据丢失: 在冲突完全解决之前,Git 无法确定您的最终意图,因此它选择保留原始的存储内容,确保您的工作成果不会因为一次失败的自动合并而丢失。

⚠️ 关键注意事项

  1. 未跟踪文件(Untracked Files)

    git stash 默认不保存未跟踪文件(如新创建未 git add 的文件)。若需保存,使用:

    git stash push -u -m "暂存所有修改(含未跟踪文件)"
    

    -u 参数会包含未跟踪文件。

  2. 多批次储藏

    可多次执行 git stash push 保存多组修改,通过 git stash list 查看记录。恢复时用:

    git stash pop stash@{0}  # 恢复指定储藏(0 为栈索引)
    
  3. 冲突处理

    若 git stash pop 后出现冲突:

    • 手动编辑冲突文件(搜索 <<<<<<< 标记)。
    • 解决后执行 git add 冲突文件名 标记为已解决。
    • 最后 git stash drop 删除储藏记录(pop 失败时需手动删除)。
  4. 替代方案(谨慎使用)

    git commit -am "临时提交"  # 将修改临时提交
    git pull --rebase         # 变基拉取(将临时提交置于最新提交之上)
    git reset HEAD~1          # 撤销临时提交,保留修改在工作区
    
    • 适用于熟悉 rebase 的用户,操作更复杂但可避免储藏栈管理。

💡 操作流程图解

冲突处理

  1. 查看冲突文件状态,查看被标记为冲突(Unmerged paths) 的文件

    git status
    
  2. 手动解决文件冲突

    • 用编辑器打开冲突文件:

      code src/lib/components/layout/Sidebar/UserMenu.svelte
      
    • 在文件中查找冲突标记:

      <<<<<<< Updated upstream
      // 以下是远程版本的代码(来自最新拉取的提交)
      // 远程修改内容...
      =======
      // 以下是您本地修改的代码(来自 stash)
      // 本地修改内容...
      >>>>>>> Stashed changes
      
    • 手动编辑文件:选择保留远程版本/本地版本,或合并两者,并删除所有冲突标记:<<<<<<<、=======、>>>>>>>。

  3. 标记冲突已解决

    # 此命令告知 Git 该文件冲突已解决
    git add src/lib/components/layout/Sidebar/UserMenu.svelte
    
  4. 完成 stash 恢复

    重要

    再此之前应该先执行 git status 确认冲突文件已经全部解决。

    git stash drop
    
    • 手动删除被保留的 stash 条目(因为 pop 未自动完成,Git 不会自动删除最近一条 stash 记录。)
    • 使用 git stash list 可查看所有储藏记录。

恢复工作区更改

在使用 git stash pop 恢复储藏区更改遇到冲突之后,仓库处于混合状态:

  • 已应用:stash 中的部分修改
  • 冲突中(如果有):与远程更新冲突的文件
  • 已更新:git pull 成功拉取的远程内容

使用 git restore 放弃对已追踪文件的修改(推荐)Git 2.23+ 引入

# 恢复所有文件到最新一次提交的状态
git restore .

# 重新应用最新的一次 `stash`
git stash apply stash@{0}
  • . 表示当前目录

提示

传统命令(Git 1.6.0+)

git checkout -- .
  • -- 是一个分隔符,表示"后面是文件路径,而不是分支名"
  • . 表示当前目录

使用 git reset 撤销恢复(不推荐)

重要

放弃当前冲突恢复(恢复工作区到执行 git stash pop 之前的状态),它彻底清除了工作目录和暂存区中所有未提交的更改。这包括:

  • 执行 stash pop 之前已经存在于工作目录但尚未提交的本地修改。
  • stash pop 操作试图应用的来自暂存区的修改。
  • stash pop 操作产生的所有冲突标记。
git reset --hard HEAD

# 重新应用 `stash`
git stash apply stash@{0}

冲突预防建议:

# 下次 `stash` 时添加详细描述
git stash push -m "修改描述_日期"

# 拉取前检查差异
git diff origin/main...HEAD  # 替换 main 为您的分支名

上传本地代码到 GitHub

提示

以下默认远程仓库别名为 origin 。

  1. 在 GitHub 上创建仓库。

  2. 初始化本地仓库:

    • 进入项目文件夹内并在此打开命令行终端。
  3. 初始化 Git 仓库:

    git init -b <local_branch_name>
    

    重要

    远程仓库有内容的时候,不要加 -b 选项。

  4. 添加远程仓库链接:

    git remote add origin <remote_repository_url>
    
  5. 添加和提交代码:

    重要

    1. 如果远程仓库有初始化好的 README.md 等文件,先拉取仓库最新文件:

      # 获取远程分支信息
      git fetch origin
      
      # (推荐)从远程分支创建本地同名跟踪分支
      git checkout -t <remote_name>/<branch_name>
      
      # (不推荐)创建并切换到本地分支(自动跟踪远程分支)
      git checkout -b <local_branch_name> origin/<remote_branch_name>
      # (不推荐)使用专注切换分支的语法:
      git switch -c <local_branch_name> origin/<remote_branch_name>
      
    2. 继续下方操作。

    # 将所有本地文件添加到暂存区
    git add .
    
    # 将暂存区的更改提交到本地仓库
    git commit -m "Initial commit"
    
  6. 推送到远程仓库:

    git push -u origin <branch_name>
    

    提示

    -u 参数是 --set-upstream 的简写。

    • 设置上游分支关联:将本地的分支与远程的 origin/<branch_name> 分支建立跟踪关系。
    • 简化后续操作:设置关联后,以后执行 git push 和 git pull 时就不需要指定远程仓库和分支名了。

    参考:git branch

查看自己有权限项目

Github:Repositories

远程分支名称解析

在 Git 中,远程分支名称如 origin/main 的命名规则和本地分支 main 的关系,是由 Git 的分支管理机制决定的,核心原因在于 命名空间隔离 和 远程跟踪分支(Remote-Tracking Branch) 的设计。以下是详细解释:

1. 为什么远程分支叫 origin/main?

  • origin 是远程仓库的别名:
    当你克隆仓库时,Git 默认将远程仓库命名为 origin(可自定义)。origin 代表远程仓库的 URL 地址。

    提示

    如果你有多个Remote,则根据这个别名判断分支来自那个Remote。

  • 斜杠 / 是命名空间分隔符:
    origin/main 表示“远程仓库 origin 上的 main 分支”。斜杠的作用是:

    • 避免本地分支与远程分支同名冲突(例如本地可同时存在 main 和 origin/main)。
    • 明确标识分支的来源(远程仓库名 + 分支名)。

2. 为什么拉取后本地分支叫 main?

  • 远程跟踪分支(Remote-Tracking Branch):
    origin/main 是一个本地存在的引用,它记录远程仓库 origin 上 main 分支的最新状态。它不是真正的本地分支,而是 Git 在本地创建的“影子分支”,用于跟踪远程变化。
  • 创建本地分支:
    当你运行 git checkout main(或 git switch main)时,Git 会:
    1. 查找本地是否存在 main 分支。
    2. 若不存在,则自动基于 origin/main 创建同名的本地分支 main,并建立跟踪关系(Tracking Relationship)。
    # 示例:拉取远程 main 分支到本地
    git checkout main  
    # 等价于:git checkout -b main --track origin/main
    

3. 关键概念解析

类型名称示例作用
远程仓库别名origin指向远程仓库 URL 的简称(默认为 origin)
远程跟踪分支origin/main本地存储的远程分支状态,只读(通过 git fetch 更新)
本地分支main用户实际操作的开发分支,可提交代码
跟踪关系(Tracking)main → origin/main本地分支与远程分支的关联,git pull/git push 无需指定远程分支

4. 设计目的与优势

  • 隔离性:
    远程分支(origin/main)和本地分支(main)物理分离,防止误操作覆盖远程分支。
  • 清晰的状态管理:
    • git status 会提示本地分支与 origin/main 的提交差异(例如领先或落后)。
    • git branch -a 可同时查看本地分支和远程跟踪分支(以 remotes/origin/ 开头)。
  • 简化协作流程:
    通过跟踪关系,git pull = git fetch(更新 origin/main) + git merge origin/main(合并到本地分支)。

5. 常见操作示例

  • 查看远程跟踪分支:
    git branch -r  # 显示所有远程跟踪分支(如 origin/main)
    
  • 手动建立跟踪关系:
    git branch -u origin/main  # 将当前本地分支跟踪到 origin/main
    
  • 删除远程跟踪分支(当远程分支已删除):
    git fetch origin --prune  # 清理本地失效的远程跟踪分支
    

总结

  • origin/main 是 Git 在本地创建的远程跟踪分支,用于记录远程仓库状态,命名格式为 <远程仓库名>/<分支名>。
  • main 是用户操作的本地分支,通过 git checkout 基于 origin/main 创建并自动建立跟踪关系。
  • 斜杠 / 是 Git 的命名约定,确保本地与远程分支的命名空间互不冲突。

这种设计既保证了远程分支状态的独立性,又简化了本地开发流程,是 Git 分布式架构的核心特性之一。

——————————

文件状态

在Git中,文件的状态可以分为以下几种:

  1. 未跟踪(Untracked):文件是新的,Git之前没有跟踪过。
  2. 已修改(Modified):文件被修改了,但还没有被暂存(unstaged changes)。
  3. 已暂存(Staged):文件被修改并且已经通过 git add 命令添加到了暂存区,等待提交。
  4. 未修改(Unmodified):文件没有修改,或者修改已经被提交。

在 VS Code 的 Git 窗口中,文件被分为 "Staged Changes" 和 "Changes" 两类,这是 Git 工作流程的核心设计,具体区别如下:

  1. Changes(未暂存的修改)

    • 含义:文件已被修改(或新增),但尚未添加到 Git 的暂存区(Staging Area)。
    • 状态特点:
      • 文件处于工作目录(Working Directory)中。
      • 这些修改不会被包含在下一次 git commit 中。
      • 在 VS Code 中显示为 "Changes" 分组(通常位于顶部)。
    • 操作建议:
      • 点击文件旁的 "+" 图标 或右键选择 "Stage Changes" 可将其移至暂存区(相当于 git add)。
      • 直接编辑文件会导致此分组动态更新。
  2. Staged Changes(已暂存的修改)

    • 含义:文件已通过 git add 添加到暂存区,准备被提交。
    • 状态特点:
      • 文件已进入 Git 的暂存区(Index)。
      • 这些修改会被包含在下一次 git commit 中。
      • 在 VS Code 中显示为 "Staged Changes" 分组(位于 "Changes" 下方)。
    • 操作建议:
      • 点击文件旁的 "-" 图标 或右键选择 "Unstage Changes" 可移回未暂存状态(相当于 git restore --staged)。
      • 点击 √ 提交按钮 会提交此分组的所有文件。

⚙️ 设计目的:精准控制提交内容

Git 通过分离 工作目录 → 暂存区 → 版本库 的三级流程,实现:

  • 选择性提交:仅暂存部分文件(如只提交功能 A 的代码,暂不提交调试日志)。
  • 分批提交:将大改动拆解为多个逻辑独立的提交。
  • 减少误提交:避免未完成的修改意外进入版本历史。

💡 VS Code 操作对比表

状态VS Code 显示分组操作等效 Git 命令
未暂存的修改Changes点击 +git add <file>
已暂存的修改Staged Changes点击 -git restore --staged <file>

提示

示例场景

假设你修改了 a.js 和 b.js:

  1. 初始状态 → 两文件均显示在 "Changes" 分组。
  2. 点击 a.js 旁的 "+" → a.js 移动到 "Staged Changes"。
  3. 点击提交按钮 → 仅提交 a.js,b.js 仍保留在未暂存状态。

Repo

Repo 基于git的工具,它用来管理多个git代码库。把所有的代码用一个git库管理已经越来越力不从心。这个时候的一个很自然的思路就是分而治之,把整个project按模块分成一个个单独的git库,再用一个统一的工具管理,repo因此应运而生。

规范

常用 Commit 标签描述

标签描述示例
feat新功能feat: 添加用户注册端点
fix修复 bugfix: 修复首页数据无法加载的问题
docs仅文档更改docs: 更新 API 接口文档
style代码风格调整(不影响代码运行)style: 修正变量命名缩进
refactor代码重构(既非新功能,也非修 bug)refactor: 使用策略模式重构支付逻辑
perf性能优化perf: 使用 memo 缓存组件减少重复渲染
test添加或修改测试用例test: 为用户服务添加单元测试
build影响构建系统或外部依赖的更改build: 升级 webpack 到 v5
ci持续集成配置和脚本的更改ci: 在 GitHub Actions 中增加 SonarQube 检查
chore其他不修改源码或测试文件的杂项变更chore: 更新依赖包版本
revert回滚之前的提交revert: 回滚提交 abc123

例如:

feat: 添加用户登录功能

- 实现用户名密码登录
- 添加记住密码功能
- 优化登录错误提示

Closes #123

命令✨

命令命令示例详细解释
git initgit init在当前目录初始化一个新的Git仓库
git clone <repository_url>git clone https://github.com/user/repo.git克隆远程仓库到本地
git add <file_path>git add file.txt将文件添加到暂存区
git commit -m "<commit_message>"git commit -m "Add new feature"将暂存区的内容提交到本地仓库
git statusgit status显示工作树的状态
git push <remote_name> <branch_name>git push origin master将本地仓库的内容推送到远程仓库
git pull <remote_name> <branch_name>git pull origin master从远程仓库拉取内容到本地仓库
git branchgit branch显示本地分支列表
git checkout <branch_name>git checkout main_branch切换到指定分支
git merge <branch_name>git merge feature_branch将指定分支合并到当前分支
git remotegit remote显示远程仓库的别名列表
git fetch <remote_name>git fetch origin从远程仓库获取最新内容,但不合并到本地仓库
git loggit log显示提交历史记录
添加 --oneline 查看简洁版
git reset <commit_hash>git reset commit_hash将HEAD指针指向特定的提交,可选地重置暂存区和工作树
git revert <commit_hash>git revert commit_hash创建一个新的提交,撤销指定提交的更改
git stashgit stash push -m "暂存本地修改"
git stash pop
将当前工作树中的变更保存到堆栈中,以便稍后恢复
git cherry-pick <commit_hash>git cherry-pick commit_hash选择一个特定的提交并将其应用到当前分支
git remote add <remote_name> <repository_url>git remote add origin https://github.com/user/repo.git添加远程仓库别名
git remote remove <remote_name>git remote remove origin移除指定的远程仓库别名
git diffgit diff显示暂存区和工作树之间的差异
git diff <commit_old_hash> <commit_new_hash>git diff 343e12b1 ad494be0 --stat查看new相对于old提交之间的差异,加上 --stat 参数则只输出统计信息;
git tag <tag_name>git tag v1.0.0创建一个新的标签
git push --tagsgit push --tags将本地标签推送到远程仓库
git config --global <config_param> "<config_value>"git config --global user.name "John Doe"配置Git的全局设置
git log --graphgit log --graph以图形方式显示提交历史记录
git clean [-n]git clean -n显示将要删除的未跟踪文件列表,使用-f进行实际删除
git rm <file_path>git rm file.txt从Git中删除文件
git show <commit_hash>git show commit_hash显示特定提交的详细信息
git rebase <branch_name>git rebase main_branch将当前分支的更改在指定分支上重新应用

命令行参数符号

  1. <> 尖括号:

    • 一般代表命令必填参数,即在执行命令时必须提供的参数。
    • 示例:command <required_parameter>
  2. [] 方括号:

    • 一般代表命令可选参数,即在执行命令时可以选择性地提供的参数,但不是必需的。
    • 示例:command [optional_parameter]
  3. {} 花括号:

    • 有时也用于表示命令必填参数,与尖括号类似。这种用法可能因具体的命令规范而异。
    • 示例:command {required_parameter}
  4. () 圆括号:

    • 圆括号在命令行中通常用于表示一组选项,用户可以从中选择一个。
    • 示例:command (option1 | option2 | option3)
  5. | 管道符号:

    • 管道符号用于将一个命令的输出作为另一个命令的输入。不表示参数本身,但是在命令行操作中很常见。
    • 示例:command1 | command2
  6. ... 省略号:

    • 省略号表示参数可以重复出现多次。
    • 示例:command <required_parameter> ...

重要

HEAD 在Git概念中一个特殊的存在,它不直接指向分支,而是指向 当前检出的提交 。详细看 git log tip块中关于 HEAD 的解释。

在命令行中使用,有以下作用:

  • HEAD: 指代当前提交

  • HEAD^ / HEAD~ / HEAD~1: 均指上一次提交

  • HEAD^^ / HEAD~2: 均值上上一次提交,以此类推。

    # 例如
    git checkout main	# HEAD -> main -> 最新提交
    git show HEAD~		# 显示 main 分支的上一个提交
    

查看待commit的文件路径

待commit:已添加到暂存区

git diff --name-only --cached

提示

命令执行之后

  • 键入:q 退出命令
  • 键入:s 输入文件名称保存到项目根路径

git pull 和 git fetch 区别

提示

先总结:

  • git pull <remote_name> <branch_name> 从远程仓库获取最新代码并 直接合并 到当前分支。
  • git fetch <remote_name> <branch_name> 仅从远程仓库获取最新代码但 不进行合并 。
具体区别
  1. git pull:

    • git pull = git fetch + git merge 。

      git pull <remote_name> <branch_name>
      
      # 相当于
      git fetch <remote_name> <branch_name>
      git merge <remote_name>/<branch_name>
      
    • 如果有冲突,你需要解决冲突后才能完成合并。

    • git pull 的执行过程会直接更新当前分支的工作目录。

  2. git fetch:

    • git fetch 从远程仓库获取最新的提交记录和分支信息,但不会将这些更改直接合并到当前分支。
    • 它只会将获取的远程分支更新到本地仓库的 .git 目录下,而不会修改工作目录中的文件。
    • git fetch 可以帮助你了解远程仓库的最新状态,你可以通过查看远程分支来了解它的提交记录,然后决定是否要进行合并操作。

git merge

将远程分支的更新合并到当前的分支,需要手动执行 git merge 或者其他合并命令(例如 git rebase)来将这些更新应用到你的工作目录和分支。

下面是一种常见的步骤来将远程分支的更新合并到你当前的分支:

  1. git fetch: 从远程仓库获取最新提交记录和分支信息。
  2. git merge <remote_name>/<remote_branch_name>: 将远程分支合并到你当前的分支。

或者也可以用 git pull 命令来达到相同的目的,因为 git pull 实际上 = git fetch + git merge :

git pull <remote_name> <remote_branch_name>

无论是使用 git fetch 和手动 git merge,还是直接使用 git pull,都要确保在执行合并操作之前没有未提交的更改,以免可能产生冲突。如果有冲突,你需要先解决冲突后再提交合并的更改。

git stash

当需要 切换到其他分支 或 本地已经做了修改需要拉取远程代码,不希望携带当前工作区内未commit的内容时,stash就派上了用场,它可以储藏当前未commit的数据。

重要

在 Windows 终端或某些 shell 中,stash@{0} 中的花括号 {} 可能被错误解析。实际使用时,可以直接使用数字索引,Git2.11.0+(2016-11-29发布) 会自动识别。只需要键入 index 对应的阿拉伯数字即可。另外提供老版本兼容办法:

  • 可以使用 ^ 号进行转义:stash^@{index}
  • 加双引号:"stash@{index}"

注:最近一次 stash 记录的 index 值总是 0 。

例如:

>git stash list
stash@{0}: On main: 版本2 # 最近 `stash` 的记录
stash@{1}: On main: 版本1 # 最早 `stash` 的记录

# 查看stash{0}与当前工作区的详细行变更
## `-p` 参数表示以补丁格式显示,包含具体的代码变更
> git stash show -p 0

用法:

  • 储藏工作区内的修改,并给此次 stash 命名

    git stash push -m '暂存提交'
    

    重要

    如果需要包含以下三种文件,使用 -u 选项:

    • 已暂存文件(已经 git add 过的)
    • 未暂存文件(工作区内修改)
    • 未跟踪文件(刚新增还未跟踪的文件)
    git stash push -m '暂存提交' -u
    

    提示

    对比 git stash save 的优势?

    git stash push 支持一个非常重要的特性:路径限定符 -- <pathspec>。这允许你只暂存工作目录中特定文件或路径的更改,而不是整个工作目录。例如:

    git stash push -m "暂存src/的修改" -- src/
    git stash push -m "暂存file.txt" -- file.txt
    
  • 查看所有的 stash

    git stash list
    
  • 查看特定的 stash

    git stash show -p stash@{index}
    
    # 例如
    git stash show -p 0
    
    • -p:以补丁格式显示,包含具体的代码变更。(不加则仅查看改动了几个文件,一共新增/删除多少行,类似 git diff --stat )
  • 恢复最近一次储藏的修改,并将其从 stash 列表中移除

    git stash pop
    

    重要

    相当于

    # 恢复最新的储藏,并在成功恢复之后删除它。
    git stash apply && git stash drop
    

    如果恢复发生了冲突,Git不会自动删除这个stash,需要手动进行 冲突处理 。

  • 恢复特定的 stash

    git stash apply stash@{index}
    
  • 删除特定的 stash,但不会恢复其中的修改

    git stash drop stash@{index}
    # 例如
    git stash drop 0
    
  • 删除所有暂存的 stash

    git stash clear
    

git diff

比较不同版本、文件或分支之间差异

提示

通用选项

  • --stat:统计变更摘要(增/删行数)
  • --word-diff:显示单词级差异(非行级)

输出格式说明

  • --- 表示修改前的文件。
  • +++ 表示修改后的文件。
  • - 开头的行:被删除的内容。
  • + 开头的行:新增的内容。
  • 上下文行(无符号):未更改的代码,用于定位变更位置。
  • 冲突标记:查看这里第2条内容
  • @@ ... @@ 变更块头:例如:@@ -55,6 +55,12 @@ ,则表示这段diff:
    • 从 文件修改前 的视角看,是从 55 行开始打印,共打印 6 行。
    • 从 文件修改后 的视角看,是从 55 行开始打印,共打印了 12 行。
    • 前 -> 后 差异为 12 - 6 = 6 行,正数,代表新增了 6 行。
  1. 比较工作区与暂存区

    • 查看已修改但未暂存(未 git add )的文件内容变化:

      git diff
      
  2. 比较暂存区与最新提交

    • 查看已暂存但未提交(已 git add )的更改:

      git diff --staged
      # 或
      git diff --cached
      
  3. 比较两个提交之间的差异

    • 指定两个提交的哈希值或引用

      git diff <commit_from> <commit_to>
      

      提示

      以 commit-from 为基准,显示从 commit_from 到 commit_to 的代码变化。

      • +:commit_to 新增的内容
      • -:表示 commit_to 删除的内容
      • 整体展示的是:从 commit_from 变为 commit_to 做了什么修改
  4. 比较分支之间的差异

    • 查看两个分支的最新提交差异:

      git diff <branch1> <branch2>
      
    • 比较当前分支与另一分支:

      git diff <branch_name>
      
  5. 比较特定文件的更改

    • 限定只查看某个文件的差异:

      git diff <file_path>
      
    • 跨分支比较指定文件:

      git diff <branch1> <branch2> -- <file_path>
      

git reset

提示

如果在执行完 git commit(提交到本地)还没有执行 git push(推送到云端)时,是可以将更改撤回到工作区的。

  • HEAD^ 也可以写作 HEAD~ ,意为最近 1 次commit。
  • HEAD~2 ,意为最近 2 次commit,以此类推。
  1. 撤销提交,保留修改(最常用)

    场景:你刚刚执行了 git commit,发现代码写错了,或者需要修改一下再提交。

    命令:

    git reset --soft HEAD^
    

    效果:

    • 撤销最近一次 commit。

    • 代码的修改仍然保留在暂存区(即保留了 git add 的状态)。

    • 你可以直接修改文件,然后再次 git commit。

  2. 撤销提交,保留修改但不暂存

    场景:你想撤销 commit,并且想把修改撤回到工作区(未 add 的状态)。

    命令:

    git reset --mixed HEAD^
    # 或者简写为
    git reset HEAD^
    

    效果:

    • 撤销最近一次 commit。
    • 代码的修改保留在工作区,但不在暂存区(需要重新 git add)。
    • 你可以直接修改文件,然后再次 git commit。
  3. 彻底丢弃提交

    警告

    慎用!(剑魔不能用😂)

    场景:刚才提交的内容完全错了,你想彻底回到上一步,并丢掉刚才写的所有代码。

    命令:

    git reset --hard HEAD^
    

    效果:

    • 撤销最近一次 commit。
    • 彻底删除所有刚才的修改,代码恢复到上一次 commit 的状态。
    • 警告:这些修改无法找回(除非你之前有其他备份)。

git status

  1. 首先输出:

    On branch master
    Your branch is up to date with 'origin/master'.
    

    这段 git status 的输出非常理想,它告诉你当前的代码状态是 完全同步 的。

    你当前正处在本地的 master 分支上工作。你的本地分支 master 和远程分支 origin/master 的内容是 一模一样 的。

  2. 然后是当前工作区工作树状态,主要有以下几种状态:

    • 未跟踪(Untracked)

      • 说明:Git 尚未开始跟踪的新文件

      • 命令行显示:Untracked files

    • 已修改但未暂存(Modified - unstaged)

      • 说明:已跟踪的文件被修改,但还未添加到暂存区

      • 命令行显示:Changes not staged for commit

    • 已暂存(Staged)

      • 说明:文件修改已添加到暂存区,准备提交

      • 命令行显示:Changes to be committed

    • 未修改(Unchanged)

      • 说明:文件自上次提交后未被修改

      • 不会在 git status 中显示

git branch

查看所有分支(本地 + 远程):

  • 使用 -a (all) 参数:

    git branch -a
    

    命令输出

    * master
    remotes/orgin/HEAD -> origin/master
    remotes/origin/fix
    

    提示

    1. * master

      • 含义:这是本地分支。
      • * (星号):表示这是你当前所在的分支。你现在所有的操作(提交代码等)都会发生在这个分支上。
      • master:分支的名称。
    2. remotes/origin/HEAD -> origin/master

      • 含义:这是远程仓库的默认分支指针。
      • 解释:这表示当你克隆这个远程仓库时,如果不指定具体的分支名,Git 默认会拉取 origin/master 这个分支。
      • 比喻:它是一个快捷方式,指向了远程仓库的“主分支”。通常不需要手动修改它。
    3. remotes/origin/fix

      • 含义:这是远程分支。
      • 解释:这表示在远程仓库(origin)上有一个名为 fix 的分支。
      • 状态:这个分支目前只存在于远程服务器上,你的本地还没有一个叫 fix 的分支来跟踪它(或者你之前有但还没更新)。

查看本地分支与远程分支关联关系:

git branch -vv

# 输出依次为:`当前所在分支名称`,`上次提交hash`,`远程分支名`,`commit comment`
* master bec7edc [origin/master] 更新xxx文件

创建本地分支:

git branch <本地分支名> <远程分支名>
  • 使用 git checkout <本地分支名> 切换分支

  • 也可以使用下面的一步操作

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

删除分支:

git branch -d <branch_name>
  • -D:强制删除(不会验证 待删除分支 是否已经合并到 当前分支 )

给当前分支 设置上游分支:

提示

设置上游分支之后,在 push 和 pull 的时候就不用每次都指定远程分支的名称了。

git branch --set-upstream-to=origin/<remote_branch_name>

提示

# 或者使用简写 -u(在 push 或 pull 时设置)
git push -u <remote_name> <local_branch_name>
# 或者
git pull -u <remote_name> <remote_branch_name>

git checkout

从远程分支创建本地同名跟踪分支(最常用):

git checkout -t <remote_name>/<branch_name>
# 或者简写
git checkout main

基于当前提交创建一个全新的分支:

git checkout -b feature/new-feature

从某个提交创建分支:

git checkout -b <new_branch_name> <commit_hash>

拉取远程分支的时候重命名:

# 使用 --track 选项(更明确,但需要包含远程别名)
git checkout -b <local_branch_name> --track origin/<remote_branch_name>

# 简写
git checkout -b <local_branch_name> <remote_branch_name>

git log

  • 基础用法:

    # 输出示例
    7xxxxx3 (HEAD -> main, origin/main, origin/HEAD) 新增xxx方法
    6xxxxx7 修复xxx问题
    
    • 第一列:本次提交HASH

    • 第二列(如果有):括号内代表当前有哪些分支处于这一次提交

    • 第三列:commit comment

      提示

      第二列括号内的内容,可以比喻成每一次提交相当于创建了一个停车场,每一个分支是一辆车,括号内填了谁,就代表这辆车当前停在这。

      HEAD 比较特殊,它不直接指向分支,而是指向 当前检出的提交 ,把 HEAD 比喻成你,你现在也在这个停车场内,但是具体下来有两种情况:

      • 正常情况下:HEAD 会 间接 指向分支名。例如你 checkout main ,相当于你坐上了 main 这辆车,HEAD 通过 main 分支指向最新提交,显示为 (HEAD -> main) 。
      • 分离头指针:当你 checkout 一个具体的提交Hash时,就像你没乘坐任何车(没跟随分支)而走进了停车场(检出了代码)。HEAD 直接指向该提交,显示为 (HEAD) 。这就叫“分离头指针”状态。
  • 查看最近一次提交的简要更改(类似 git show )

    git log -1 -p
    
    • --name-only:只显示变更的文件名

    • -1:数字参数,限制显示的提交数量为 1 条

    • -p 或 --patch:显示提交引入的具体代码变更(差异对比)

      提示

      代码差异输出格式统一使用 git diff 同等样式。

git show

查看commit的详细更改

提示

代码差异输出格式统一使用 git diff 同等样式。

  1. 查看指定Hash的详细更改(不指定选项默认:最近一次)

    git show [commit_hash]
    
    • --stat:只显示变更文件统计信息
    • --name-only:只显示变更的文件名
    • --color-words:更友好的显示方式
  2. 查看指定文件的最近更改

    git show <文件名>
    
  3. 查看指定提交中某个文件的差异

    git show <commit_bash> -- <file_path>
    
    # 显示提交完成时的文件完整内容
    git show <commit_bash>:<file_path>
    
  4. 图形化方式查看

    gitk HEAD
    

git tag

标签是一个指向某个特定提交的 静态指针 。所以是可以不和代码一起提交的。

  1. 创建标签

    # 创建轻量标签(lightweight tag)
    git tag v1.0.0
    
    # 创建带注释的标签(annotated tag)- 推荐
    git tag -a v1.0.1 -m "版本v1.0.1发布说明"
    
    # 为特定提交创建标签
    git tag -a v1.0.2 <commit_hash> -m "标签说明"
    

    提示

    没指定 commit_hash 选项时的默认行为是给当前的 HEAD 所指向的提交创建标签。

  2. 查看标签

    # 查看所有标签
    git tag
    
    # 查看标签详情
    git show v1.0.1
    
    # 按模式搜索标签
    git tag -l "v1.0*"
    

推送标签到远程仓库

  1. 推送单个标签

    # 推送指定标签
    git push origin v1.0.1
    
    # 或者使用完整格式
    git push origin refs/tags/v1.0.1
    
  2. 推送所有标签

    git push origin --tags
    
  3. 同时推送代码和标签

    # 先推送代码分支
    git push origin main
    
    # 再推送标签
    git push origin --tags
    
    # 或者一步完成(如果分支和标签都需要推送)
    git push origin main --tags
    

    提示

    如果需要给当前还在工作区的代码打tag,需要先commit之后再执行 git tag 。最后执行上方相关 push 命令。

其他常用操作

# 删除本地标签
git tag -d v1.0.0

# 删除远程标签()
git push origin --delete v1.0.0

# 拉取远程标签
git fetch --tags

代理

配置代理

# Set HTTP proxy
git config --global http.proxy "http://127.0.0.1:7890"

# Set HTTPS proxy
git config --global https.proxy "http://127.0.0.1:7890"

# 验证
git config --get http.proxy
git config --get https.proxy

取消代理

# Unset HTTP proxy
git config --global --unset http.proxy

IDEA的 Update Project

场景一:默认/使用 Merge 方式更新

相当于 git pull ,这是最传统、最安全的方式。

执行的 Git 指令序列大致如下:

  1. git fetch origin
    • 这是最关键的一步。它从远程仓库(通常是 origin)获取所有最新的分支、标签信息,但不会自动合并到你的当前工作分支。它只是让你本地知道远程仓库现在是什么样子。
  2. git merge origin/your-current-branch-name (例如 git merge origin/main)
    • 在 fetch 获取到远程的最新信息后,IDEA 会将远程分支(origin/main)合并 到你当前检出的本地分支(main)。

简单来说,Merge 方式 = git fetch + git merge

结果: 如果远程有新的提交,你的本地分支会生成一个额外的 合并提交,将两条开发线汇合。

场景二:使用 Rebase 方式更新

这是一种更线性和整洁的更新方式,也是目前很多团队推荐的做法。

执行的 Git 指令序列大致如下:

  1. git fetch origin
    • 同样,第一步永远是先获取远程的最新信息。
  2. git rebase origin/your-current-branch-name (例如 git rebase origin/main)
    • 在 fetch 之后,IDEA 不是执行合并,而是执行 变基。它会将你本地尚未推送到远程的提交“暂时取下”,然后基于远程最新的提交重新应用你的提交。

简单来说,Rebase 方式 = git fetch + git rebase

结果: 你的本地提交会“移动”到项目最新提交的顶端,形成一条直线式的历史,没有合并提交。

如何查看和配置更新类型?

在 IDEA 中,路径是:
File -> Settings -> Version Control -> Git
在右侧找到 Update Method 进行选择。

  • Merge: 默认选项。
  • Rebase: 更干净的提交历史。
  • Branch Default: 使用为该分支配置的默认策略。

特殊情况下的行为

  • 如果当前分支有未提交的更改:
    IDEA 非常智能,它不会直接执行 merge 或 rebase,因为这会与未提交的更改冲突。它会先尝试 Stash(储藏) 你的更改。
    • 相当于执行:git stash push -m "IDEA Stash before Update"
    • 然后执行 git fetch + git merge/rebase
    • 最后再尝试 Unstash(应用储藏):git stash pop
    • 如果应用储藏时发生冲突,IDEA 会提示你解决冲突。
  • 如果当前分支在远程没有对应的上游分支:
    IDEA 的 Update Project 按钮可能会变灰或无法正常工作,因为它不知道要和哪个远程分支进行同步。

总结对比

特性Merge 方式Rebase 方式
等效命令git fetch + git mergegit fetch + git rebase
提交历史保留合并提交,呈网状线性整洁,像一条直线
安全性较高,不重写历史较低,重写了本地提交历史
适用场景公共分支(如 main/master),团队新手友好个人功能分支,追求清晰历史

多 Remote

(一个本地项目,关联两个远程仓库)

1. 准备本地仓库

可以用现有仓库,也可以新建一个:

git init

如果已经连接了 GitHub 或其他远程,执行:

git remote -v

会看到类似输出:

origin  git@github.com:username/repo.git (fetch)
origin  git@github.com:username/repo.git (push)

2. 添加第二个远程仓库

假设第二个远程是 GitLab,远程名可以自定义,常见如 origin2 或 gitlab,这里用清晰的 gitlab :

git remote add gitlab git@gitlab.com:username/repo.git

再次查看:

git remote -v

输出会是:

origin  git@github.com:username/repo.git (fetch)
origin  git@github.com:username/repo.git (push)
gitlab  git@gitlab.com:username/repo.git (fetch)
gitlab  git@gitlab.com:username/repo.git (push)

这样你就同时关联了两个远程。

3. 同时推送到两个远程仓库

有两种方式:

方式一:手动分别推送

git push origin main
git push gitlab main

灵活且安全,但要多执行一次命令。

方式二:配置多推送地址,实现一次命令推送两个远程

编辑 .git/config,在 [remote "origin"] 下添加多条 pushurl:

[remote "origin"]
    url = git@github.com:username/repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    pushurl = git@github.com:username/repo.git
    pushurl = git@gitlab.com:username/repo.git

此后执行:

git push origin main

就会同步推送到 GitHub 和 GitLab 两个仓库。

警告

如果用于 CI/CD,请确保两个仓库的流水线不会因同一提交重复触发,这点很重要。

额外操作:重命名或删除远程

重命名远程:

git remote rename origin github1

删除远程:

git remote remove gitlab1

提示

为什么要用两个远程?

  • 备份:GitHub 崩了,GitLab 还在;
  • 迁移:试用新平台,旧平台继续线上运行;
  • 镜像:想多地方展示代码,简单又直观;
  • CI/CD 分离:GitHub 管理代码,GitLab 管理部署。

Submodule

核心概念

定义:Git Submodule 允许一个 Git 仓库引用另一个 Git 仓库作为子目录。子模块本质上是一个指向特定提交哈希的指针,而非文件本身。

特点:

  • 子模块拥有独立的版本历史、分支和标签
  • 父仓库仅记录子模块的提交 ID(Commit Hash)
  • 主仓库不负责子模块的代码维护,但需要手动同步更新版本

提示

父仓库“看”不到子模块的分支,它只记录一个特定的 Commit ID(哈希值)。

  • 通过命令 git submodule status 查看

所以无论你在子模块里切到了哪个分支,父仓库只认准那个哈希值对应的代码状态。

警告

如果子模块没有显式指定分支的话,默认子模块处于 分离头指针 状态。

Git 将子模块信息存储在两个关键位置
  1. .gitmodules (仓库内)

    • 受版本控制,随仓库同步。使用 submodule 相关命令的时候会自动更新。
    • 记录子模块路径与 URL 的映射关系
  2. .git/config (本地)

    • 本地配置,不随仓库同步
    • 记录具体的克隆 URL 和设置
主仓库记录子模块哈希值的地方有两个
  1. .gitmodules 文件(配置信息:URL、路径、跟踪分支等)

    [submodule "path/to/submodule"]
        path = path/to/submodule
        url = https://github.com/user/repo.git
        branch = main
    
  2. .git/modules 目录(实际哈希存储)

    .git/modules/<子模块路径>/HEAD
    .git/modules/<子模块路径>/refs/heads/  # 分支引用
    .git/modules/<子模块路径>/refs/remotes/  # 远程引用
    

提示

使用 git submodule sync 可将 .gitmodules 中的 URL 更新同步到本地 .git/config(仅影响已经注册过的子模块)。

  • 指定 --recursive 选项后,将 递归 更新注册过的子模块。

添加

# 基本用法
git submodule add <repository_url> <path>

# 指定分支添加
git submodule add -b <branch_name> <repository_url> <path>

后续步骤:

  • 执行后会产生 .gitmodules 文件和子模块目录
  • 必须提交这两个更改到主仓库:
git add .gitmodules <path>
git commit -m "添加子模块"

克隆

方式一:一键克隆(推荐)

git clone --recurse-submodules <repository_url>

方式二:分步操作

# 1. 克隆主仓库
git clone <repository_url>
cd <project_name>

# 2. 初始化并递归更新子模块
git submodule update --init --recursive

或者

# 1. 克隆主仓库
git clone <repository_url>
cd <project_name>

# 初始化并递归更新子模块
git submodule init
git submodule update --recursive
## 上方两步可以省略为一步操作
git submodule update --init --recursive

更新

重要

  • 这里的更新指的是 更新子模块到远程最新状态 ,而不是提交代码到子模块的意思。
  • 有很多解释说,执行完下面的更新命令需要执行 git add <submodule_path> ,如果你并不是主项目的维护者,完全不必做这一步操作。这一步的作用只是为了更新主项目中记录的子模块HASH,但此时子模块中的代码已经是最新的了。

更新所有子模块:

提示

默认只更新第一层子模块,需要递归一般都需要加上 --recursive 选项。

  • 更新子模块到主项目记录的状态(需要先手动更新主项目)

    git submodule update
    
    • --checkout:该选项即是该命令的默认行为,可以省略。
  • 更新子模块到子模块远程的最新状态

    git submodule update --remote
    
    • --remote:本地如果有修改,远程会直接覆盖。
    • --merge:本地有修改,远程尝试合并,如果有冲突会提示。

    提示

    使用 git submodule update 命令来让子模块回到主项目记录的版本。

  • 更新子模块到主项目记录的状态(更新主项目的同时更新子模块)

    git pull --recurse-submodules
    

    重要

    先更新 主项目,成功 之后再递归更新子模块到 主项目记录的提交 。如果需要使用最新的子模块,需要使用上一项命令。

    对于这条命令有一个 Git 配置可以实现:

    # 1. 仅在当前仓库生效
    git config submodule.recurse true
    
    # 2. 在全局所有仓库生效
    git config --global submodule.recurse true
    

    这样普通的 git pull 也会自动更新子模块到主项目记录的提交。

更新特定子模块:

  • 指定子模块路径(推荐)

    git submodule update --remote <submodule_path>
    git add <submodule_path>
    
  • 手动拉取子模块更新(不推荐)

    cd <submodule_path>
    git pull origin main
    cd ..
    git add <submodule_path>
    git commit -m "更新子模块版本"
    

状态

# 查看子模块状态(当前 commit ID)
git submodule status

# 查看子模块提交摘要
git submodule summary

# 查看配置文件
cat .gitmodules

提示

git submodule status 命令输出解释:

+53b2dbba libs/core (heads/main)
部分示例值含义
前缀符号+- 号表示未初始化( git submodule init 未执行)。
+ 号表示子模块内有新提交但未更新到父仓库。
空格:表示正常,子模块代码与父仓库记录完全一致。
Commit Hash53b2dbba子模块当前实际指向的提交 ID。
路径libs/core子模块在父项目中的目录路径。
引用信息(heads/main)(可选) 显示子模块当前所在的远程分支名称。说明你是基于远程的 main 分支更新的。

删除

# 1. 反初始化
git submodule deinit -f <path>

# 2. 移除文件与缓存
rm -rf .git/modules/<path>
git rm -f <path>

# 3. 提交更改
git commit -m "删除子模块"

批量操作

# 对所有子模块执行相同命令
git submodule foreach <command>

# 示例:拉取所有子模块代码
git submodule foreach git pull

跟踪分支

重要

影响 git submodule update --remote 命令实际从哪个分支拉取的代码配置为:

  • 项目根路径下 .gitmodules -> [submodule] -> branch 。

以下命令中的 submodule_path 的值,例如 libs/utils 中的斜杠为 正斜杠 不要错。

  • 查看子模块当前跟踪的分支:

    # 查看配置中子模块的跟踪分支(默认应该是空)
    git config -f .gitmodules submodule.<submodule_path>.branch
    
    # 查看本地代码跟踪分支
    cd <submodule_path>
    # `*` 号后面即是当前跟踪分支
    git branch -a
    
  • 修改子模块跟踪分支:

    1. 一步完成配置和切换(推荐:仅在拉取操作时拉取指定分支,而不切换子模块的本地分支)

      git submodule set-branch -b <branch_name> -- <submodule_path>
      git submodule update --remote
      

      提示

      保留子模块默认行为:仍然是 detached HEAD (分离头指针)。

    2. 修改 .gitmodules 配置中子模块的默认跟踪分支(不推荐:会切换子模块的本地分支)

      # 先拉取远程分支
      git checkout -b <branch_name> origin/<branch_name>
      # 更改配置
      git config -f .gitmodules submodule.<submodule_path>.branch <branch_name>
      # 提交更改
      git add .gitmodules && git commit -m "xxx"
      
    3. 在子模块内切换分支(不推荐:会切换子模块的本地分支)

      cd <submodule_path>
      git checkout <branch_name>
      
      # 如果上一步失败,先创建本地分支跟踪远程分支:
      git checkout -b <branch_name> origin/<branch_name>
      
      # 然后提交子模块更改
      git add .gitmodules && git commit -m "xxx"
      

常见问题

Q: 如何识别文件夹是子模块?

A: 使用 ls -la 查看,子模块文件夹显示为 commitHash path 格式(如 @b9b8c6f libs/mylibrary)。

Q: 子模块更新出现冲突怎么办?

A:

# 强制重置子模块
git submodule update --init --force

# 或进入子模块目录手动解决
cd <submodule_path>
git status  # 查看冲突
# ...解决冲突...

Sparse Checkout

稀疏检出(Sparse Checkout)允许只检出仓库中的特定子目录或文件,而不必下载整个仓库的内容(仅影响工作目录,.git 等历史记录仍完整保留)。

例如 NativeTemplateDemo · OpenHarmony-codelabs 项目是仓库中的一个文件夹,但它同时又是一个单独的仓库,我们只需要其中这一个的仓库,但没必要Clone下来整个仓库。这时就用到了稀疏检出功能。

首次克隆

(推荐):

git clone --filter=blob:none --sparse https://gitcode.com/openharmony/codelabs.git
cd codelabs
git sparse-checkout set NativeAPI/NativeTemplateDemo

# 后续更新
git pull --filter=blob:none

提示

命令作用
git sparse-checkout add <dir>新增目录
git sparse-checkout set <dir>覆盖设置
git sparse-checkout remove <dir>删除单个目录
git sparse-checkout list查看当前目录列表
git sparse-checkout disable禁用稀疏检出(检出所有文件)

或者使用以下步骤(不推荐)

# 1. 初始化本地仓库
git init

# 2. 启用稀疏检出模式
git config core.sparsecheckout true

# 3. 指定要检出的路径(结尾正斜杠表示匹配目录;后续需要更多目录使用 `>>` 追加)
echo "NativeAPI/NativeTemplateDemo/" > .git/info/sparse-checkout

# 4. 添加远程仓库地址
git remote add origin https://gitcode.com/openharmony/codelabs.git

# 5. 拉取代码(只拉取指定路径的内容)
git pull origin master

现有仓库

  1. 启用 Sparse Checkout,在仓库根目录执行:

    git config core.sparseCheckout true
    
  2. 指定要检出的目录/文件

    在 .git/info/sparse-checkout 文件中列出需要保留的路径(每行一个路径):

    # 示例:仅保留 src 和 docs 目录
    echo "src/" >> .git/info/sparse-checkout
    echo "docs/" >> .git/info/sparse-checkout
    

    重要

    • 路径是目录的,结尾应该加上 / ,这样不会包含意外文件。例如 src-file.txt 。
    • 支持通配符(如 *.js)。但不会递归匹配,需要多层就要显式列出各层的目录配置。
  3. 应用稀疏检出配置

    git checkout HEAD
    

    提示

    手动更改了 .git/info/sparse-checkout 配置文件也需要使用此命令重新检出。

    • 或强制刷新工作区:

      git read-tree -mu HEAD
      

恢复完整检出

# 关闭稀疏检出
git config core.sparsecheckout false
# 删除稀疏检出配置
rm .git/info/sparse-checkout

# (推荐)恢复全量文件
git read-tree -mu HEAD

# (不推荐)者 重新拉取完整仓库
git reset --hard

提示

Windows删除使用 del 命令。

Github Actions

修改

例如多平台统一直播,逍遥橙子大佬的项目:xiaoyaocz/dart_simple_live: 简简单单的看直播 。默认在推送 tag 的时候,才执行。这样 fork 项目之后,默认不会执行Actions,则需要修改一下 workflows 。

禁用 dev 分支的工作流:

on:
  # 禁用所有触发事件
  push:
  	disabled: true
  pull_request:
  	disabled: true

启用 release 分支的手动触发:

on:
  # 添加手动触发
  workflow_dispatch:

需要在 GitHub 仓库中配置的 Secrets:

  1. 密钥文件

    KEYSTORE_BASE64          # 将 keystore.jks 文件进行 base64 编码后的字符串
    
  2. 密钥密码信息

    STORE_PASSWORD           # keystore 的密码
    KEY_PASSWORD             # 密钥的密码  
    KEY_ALIAS                # 密钥别名
    
  3. GitHub Token

    TOKEN                    # GitHub 个人访问令牌,用于发布 Release
    
  4. 执行工作流

如何生成和配置?
  1. 生成 keystore:

    keytool -genkey -v -keystore keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your_alias_name>
    

    提示

    certutil 命令如果没有显式指定密钥密码(通过 -keypass 参数),工具只会提示用户输入 密钥库密码 ,不会单独提示输入 密钥密码 ,默认使用与 密钥库相同 的密码来保护密钥。

    如果想要单独设置密钥密码,需要在命令后拼接上 -keypass 你的密钥密码 。

  2. 将 keystore 转换为 base64:

    # Linux/Mac
    base64 -i keystore.jks | tr -d '\n'
    
    # Windows
    certutil -encode keystore.jks temp.txt && type temp.txt | findstr /v /c:"-" | findstr /v /c:"certificate" > base64.txt
    
  3. 在 GitHub 中配置:

    1. 进入仓库 → Settings → Secrets and variables → Actions
    2. 点击 New repository secret
    3. 分别添加上述所有 secrets

提示

请妥善保管原始的 keystore.jks 文件和密码,这些是应用签名的关键,丢失后将无法更新应用。

最近更新: 2026/2/4 23:29
Contributors: Enlin
Prev
docker
Next
Nginx