git分支

上一篇我们彻底搞懂了 Git 的底层核心:它是一个基于 SHA-1 的内容寻址数据库,所有文件和目录最终都转化为 Blob、Tree、Commit 对象存储,而版本历史本质上是由 Commit 对象通过父指针串联起来的链表。

这一篇我们将完全聚焦于 Git 最核心的能力 ------ 分支,不涉及任何远程仓库和多人协作内容。很多人用 Git 多年,依然对分支一知半解,本质是没有穿透到 "指针" 这个底层本质。我们依然延续 "操作 + 底层验证" 的风格,从分支的本质出发,深入讲解所有分支操作的底层原理、高级技巧和常见问题解决方案。

1:分支本质

1:分支不是代码副本

这是所有 Git 学习者必须打破的第一个误区。如果分支是代码的副本,那么创建一个包含 10 万个文件的项目分支,应该需要复制几十 GB 的数据,耗时几分钟。但实际上,Git 创建分支只需要 不到 1 毫秒

真相是:Git 的分支本质上只是一个纯文本文件,里面只存储了一个 40 位的 SHA-1 哈希值,指向某个 Commit 对象 。它就像一本书里的书签,标记了你当前在历史中的位置。创建分支的过程,就是在 .git/refs/heads/ 目录下新建一个 40 字节的文本文件,仅此而已。

2:动手验证:创建分支的底层变化

我们用一个简单的项目来验证。假设当前我们有 3 次提交,master 分支指向最新的提交:

bash 复制代码
# 查看当前分支
git branch
# 输出:* master

# 查看 master 分支指向的提交哈希
cat .git/refs/heads/master
# 输出:7c3d8f9a2b4e6c8d0f1a3b5c7e9f1a3b5c7e9f1

现在我们创建一个dev分支

bash 复制代码
git branch dev

立即查看 .git 目录的变化

bash 复制代码
# 查看 refs/heads 目录,多了一个 dev 文件!
ls -l .git/refs/heads/
# 输出:
# -rw-r--r--  1 user  group  41 May 22 10:00 dev
# -rw-r--r--  1 user  group  41 May 22 09:50 master

# 查看 dev 文件的内容,和 master 完全一样!
cat .git/refs/heads/dev
# 输出:7c3d8f9a2b4e6c8d0f1a3b5c7e9f1a3b5c7e9f1

就是这么简单!Git 没有复制任何代码文件,只是新建了一个 41 字节的文本文件(包含换行符)。这就是 Git 分支轻量高效的根本原因。

3:分支的核心特性:可移动指针

分支不是固定不变的,它会随着你的提交自动向前移动。当你在某个分支上执行 git commit 时,Git 会做两件事:

  1. 创建新的 Commit、Tree、Blob 对象
  2. 将当前分支的指针移动到这个新的 Commit 对象上

我们在 dev 分支上做一次提交来验证:

bash 复制代码
# 切换到 dev 分支
git checkout dev

# 修改文件并提交
echo "dev branch change" >> README.md
git add README.md
git commit -m "dev: first commit"

现在查看两个分支的指针变化

bash 复制代码
# dev 分支已经指向了新的提交
cat .git/refs/heads/dev
# 输出:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b

# master 分支仍然停留在原来的提交
cat .git/refs/heads/master
# 输出:7c3d8f9a2b4e6c8d0f1a3b5c7e9f1a3b5c7e9f1

两个分支现在指向了不同的提交,这就是分支 "分叉" 的本质。

2:HEAD指针

理解了分支是指针之后,我们必须深入理解另一个更重要的指针 ------HEAD。它是 Git 中最核心的指针,没有之一。

1:HEAD的本质:指向指针的指针

HEAD 存储在 .git/HEAD 文件中,它的作用是告诉 Git:"你现在在哪个分支上"。

正常情况下,HEAD 不是直接指向 Commit 对象,而是指向一个分支引用。这种状态叫做符号引用(Symbolic Reference)

bash 复制代码
cat .git/HEAD
# 输出:ref: refs/heads/dev

这行内容的意思是:"当前 HEAD 指向 refs/heads/dev 这个分支引用"。当你执行 git commit 时,Git 会先通过 HEAD 找到当前分支的引用文件,然后更新这个文件里的哈希值。

2:切换分支的本质:只是修改 HEAD 的指向

很多人以为切换分支是 "替换工作区的所有文件",但这只是结果,不是本质。切换分支的本质只有一件事:修改 .git/HEAD 文件的内容,让它指向目标分支的引用

bash 复制代码
# 切换到 master 分支
git checkout master

# 查看 HEAD 的内容,已经变成了指向 master
cat .git/HEAD
# 输出:ref: refs/heads/master

当然,切换分支不仅仅是修改 HEAD,Git 还会做两件重要的事:

  1. 更新暂存区:用目标分支指向的 Commit 对应的 Tree 对象重置暂存区
  2. 更新工作区:用暂存区的内容更新工作区的文件

这就是为什么切换分支后,你工作区的文件会变成目标分支的样子。但 Git 非常高效,它只会更新那些在两个分支之间有差异的文件,而不是全部替换。

3:分离头指针状态:HEAD直接指向Commit

正常情况下 HEAD 指向分支,但有时候我们会不小心进入 ** 分离头指针(Detached HEAD)** 状态:

bash 复制代码
# 直接切换到某个历史提交,而不是分支
git checkout 7c3d8f9  # 第一次提交的哈希值
# 输出:
# Note: switching to '7c3d8f9'.
# You are in 'detached HEAD' state...

此时 HEAD 的内容发生了根本变化

bash 复制代码
cat .git/HEAD
# 输出:7c3d8f9a2b4e6c8d0f1a3b5c7e9f1a3b5c7e9f1

现在 HEAD 不再指向任何分支,而是直接指向一个 Commit 对象。这就是 "分离头指针" 这个名字的由来。

分离头指针状态的风险

在分离头指针状态下,如果你做了新的提交,会发生什么?

bash 复制代码
# 在分离头指针状态下修改并提交
echo "detached HEAD change" >> test.txt
git add test.txt
git commit -m "commit in detached HEAD"

Git 会正常创建新的提交,但这个提交不属于任何分支!它是一个 "悬空提交"。

bash 复制代码
# 查看 HEAD 指向的新提交
cat .git/HEAD
# 输出:x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3h2g1f

# 查看所有分支,没有任何分支指向这个新提交
git branch
# 输出:
# * (HEAD detached at x9y8z7w)
#   dev
#   master

现在如果你切换回其他分支:

bash 复制代码
git checkout master

那个新提交就变成了 "孤儿",没有任何指针指向它。Git 的垃圾回收机制会在一段时间后(默认 30 天)自动删除这个提交,你写的代码就会丢失。

分离头指针的正确用途和补救方法

分离头指针状态不是错误,它有合法的用途:

  • 临时查看某个历史版本的代码
  • 基于某个历史版本做实验性修改,不需要保留

如果你不小心在分离头指针状态下做了重要的提交,想要保留这些修改,只需要基于当前 HEAD 创建一个新分支即可:

bash 复制代码
# 在分离头指针状态下创建新分支,会保留所有提交
git checkout -b temp-branch

现在 temp-branch 分支指向了那个新提交,它就不会被垃圾回收了。

3:分支合并

分支的价值在于并行开发,而开发完成后最终都需要合并。Git 有两种完全不同的合并模式,理解它们的区别是掌握分支的关键。

1:快进合并

当两个分支没有分叉,也就是待合并分支是当前分支的直接祖先时,Git 会采用快进合并模式。

我们来构造这种场景:

bash 复制代码
# 当前在 master 分支
git checkout master

# 创建并切换到 feature 分支
git checkout -b feature

# 在 feature 分支做两次提交
echo "feature 1" >> feature.txt
git add feature.txt
git commit -m "feature: add feature 1"

echo "feature 2" >> feature.txt
git add feature.txt
git commit -m "feature: add feature 2"

现在分支结构是这样的:

bash 复制代码
master: A -- B
               \
feature:        C -- D

master 停留在提交 B,feature 在 B 的基础上前进了两步到 D。现在我们切换回 master 合并 feature

bash 复制代码
git checkout master
git merge feature
# 输出:
# Updating 7c3d8f9..a1b2c3d
# Fast-forward
#  feature.txt | 2 ++
#  1 file changed, 2 insertions(+)
#  create mode 100644 feature.txt

注意输出里的 Fast-forward 字样,这就是快进合并。

快进合并的底层原理

快进合并是最简单的合并方式,Git 不需要做任何复杂的计算,只需要做一件事:将当前分支的指针直接移动到待合并分支的最新提交上

验证一下:

bash 复制代码
# master 分支现在和 feature 分支指向同一个提交
cat .git/refs/heads/master
# 输出:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b

cat .git/refs/heads/feature
# 输出:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b

快进合并的优点是速度极快,不会产生额外的提交,历史线是完全线性的。但它有一个致命缺点:合并完成后,原来的分支信息就丢失了。你无法从历史中看出这些提交是来自一个独立的功能分支。

2:三方合并

当两个分支都有新的提交,也就是分支分叉时,Git 无法进行快进合并,只能采用三方合并模式。

我们来构造分叉场景:

bash 复制代码
# 先回到合并前的状态
git reset --hard HEAD^
git branch -D feature

# 重新创建 feature 分支并做一次提交
git checkout -b feature
echo "feature change" >> common.txt
git add common.txt
git commit -m "feature: modify common.txt"

# 切换回 master 分支,也做一次提交
git checkout master
echo "master change" >> common.txt
git add common.txt
git commit -m "master: modify common.txt"

现在分支结构变成了这样:

bash 复制代码
          C (master)
         /
A -- B --
         \
          D (feature)

两个分支从 B 点分叉,各自有了新的提交 C 和 D。现在我们合并:

bash 复制代码
git merge feature
# 输出:
# Auto-merging common.txt
# Merge made by the 'recursive' strategy.
#  common.txt | 1 +
#  1 file changed, 1 insertion(+)

这次没有 Fast-forward,Git 采用了 "递归合并策略"(也就是三方合并),并且自动创建了一个新的提交。

三方合并的底层逻辑

Git 会找到三个关键的提交来进行合并:

  1. Ours:当前分支的最新提交(C)
  2. Theirs:待合并分支的最新提交(D)
  3. Base:两个分支的共同祖先提交(B)

然后 Git 会做两次差异比较:

  • 比较 Base 和 Ours,得到差异 Δ1
  • 比较 Base 和 Theirs,得到差异 Δ2

最后 Git 会将 Δ1 和 Δ2 合并在一起,生成一个新的合并提交(Merge Commit)

合并提交的特殊结构

合并提交和普通提交有一个本质区别:它有两个父提交

bash 复制代码
# 查看最新的合并提交
git log -1
# 输出:
# commit m7n8o9p0q1r2s3t4u5v6w7x8y9z0a1b2c3d4e5f (HEAD -> master)
# Merge: c3d4e5f x9y8z7w  # 注意这一行!两个父提交
# Author: Your Name <your@email.com>
# Date:   Wed May 22 11:00:00 2024 +0800
# 
#     Merge branch 'feature'

第一个父提交是合并前当前分支的最新提交(C),第二个父提交是待合并分支的最新提交(D)。Git 通过这种方式记录了合并的历史。

现在分支结构变成了这样

bash 复制代码
          C -- E (master, 合并提交)
         /    /
A -- B --    /
         \  /
          D (feature)

3:禁用快进合并

如果你希望即使在可以快进合并的情况下,也保留分支的开发轨迹,可以使用 --no-ff 参数禁用快进合并:

bash 复制代码
git merge --no-ff feature

这样 Git 即使可以快进,也会强制生成一个合并提交。这是企业开发中的最佳实践,因为它完整保留了功能分支的历史,方便后续的代码审查、问题追溯和版本回退。

4:合并策略

Git 提供了多种合并策略,默认会根据场景自动选择最合适的策略:

策略 适用场景 说明
recursive(递归) 两个分支的合并 Git 的默认策略,会递归处理子树合并
resolve 两个分支的合并 比 recursive 更简单的三方合并算法,速度更快,但处理复杂合并的能力稍弱
octopus(章鱼) 三个及以上分支的合并 专门用于一次性合并多个分支,不支持处理冲突
ours 任何场景 完全忽略待合并分支的所有修改,合并结果就是当前分支的内容
theirs 任何场景 和 ours 相反,完全保留待合并分支的内容,丢弃当前分支的修改

最常用的是 recursive 策略,它也是 Git 的默认策略。ourstheirs 策略在某些特殊场景下非常有用,比如你想要完全覆盖某个分支的修改。

4:变基

变基(Rebase)是 Git 中另一个合并分支的方式,也是最容易被误解和误用的功能。很多人谈 Rebase 色变,但只要理解了它的本质,就能用好这个强大的工具。

1:Rebase的本质:"搬运的提交"

我们还是用之前分叉的分支结构来演示 Rebase:

bash 复制代码
          C (master)
         /
A -- B --
         \
          D (feature)

之前用 Merge 的方式合并,会生成一个新的合并提交 E,历史线变成网状。现在我们用 Rebase 的方式来合并:

bash 复制代码
# 切换到 feature 分支
git checkout feature

# 执行 rebase 命令,将 feature 分支变基到 master 分支上
git rebase master

Rebase 的执行步骤

Rebase 的执行过程可以拆解为 4 步:

  1. 找到共同祖先:Git 找到两个分支的共同祖先 B
  2. 提取提交:提取当前分支(feature)从 B 之后的所有提交(也就是 D),并将它们保存为临时补丁文件
  3. 切换到目标分支:将当前分支的指针移动到目标分支(master)的最新提交 C 上
  4. 应用补丁:将之前提取的补丁文件一个个应用到 C 上,生成新的提交 D'

变基完成后,分支结构变成了这样:

bash 复制代码
A -- B -- C -- D' (feature)
         (master)

现在 feature 分支是 master 分支的直接后继,我们可以切换回 master,进行快进合并:

bash 复制代码
git checkout master
git merge feature

合并完成后,历史线是完全线性的,非常干净,没有任何额外的合并提交。

2:Rebase VS Merge

现在我们可以清晰地对比 Merge 和 Rebase 的本质区别:

特性 Merge Rebase
历史线 网状,保留所有分支历史和合并轨迹 线性,重写提交历史
提交哈希 所有原提交的哈希保持不变 变基后的提交是全新的,哈希值会改变
额外提交 生成合并提交 不生成额外提交
冲突处理 一次性解决所有冲突 逐个提交解决冲突
适用场景 公共分支合并、需要保留完整历史 个人开发分支整理、让历史更清晰

3:Rebase的黄金法则

永远不要在公共分支上使用 Rebase!

这是 Git 世界中最重要的法则,没有之一。原因很简单:Rebase 会重写提交历史,改变提交的哈希值。如果你的分支已经推送到了远程仓库,并且有其他人正在基于这个分支开发,你把重写后的历史推上去,会导致其他人的本地仓库和远程仓库不一致,引发灾难性的冲突和代码丢失。

可以安全使用 Rebase 的场景

  • 你在自己的本地分支上开发,还没有推送到远程
  • 你想要整理自己的提交历史,让它更清晰易读
  • 你需要将一个分支的修改应用到另一个分支的最新版本上

绝对不能使用 Rebase 的场景

  • 分支已经推送到远程仓库
  • 有其他人正在基于这个分支开发
  • 公共分支(如 master、develop)之间的合并

4:Rebase过程的冲突处理

在 Rebase 过程中,如果某个提交和目标分支有冲突,Git 会暂停 Rebase,让你解决冲突:

bash 复制代码
git rebase master
# 输出:
# Auto-merging common.txt
# CONFLICT (content): Merge conflict in common.txt
# error: could not apply a1b2c3d... feature: modify common.txt
# hint: Resolve all conflicts manually, mark them as resolved with
# hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
# hint: You can instead skip this commit: run "git rebase --skip".
# hint: To abort and get back to the state before "git rebase", run "git rebase --abort".

解决 Rebase 冲突的步骤:

  1. 打开冲突文件,手动解决冲突
  2. 执行 git add 冲突文件,标记冲突已解决
  3. 执行 git rebase --continue,继续应用下一个提交

如果有多个提交有冲突,你需要逐个解决,直到所有提交都应用完成。

如果你不想解决冲突,想要放弃这次 Rebase,可以执行:

bash 复制代码
git rebase --abort

这个命令会将仓库恢复到 Rebase 开始前的状态,就像什么都没发生过一样。

5:交互式Rebase

bash 复制代码
pick 7c3d8f9 first commit
pick a1b2c3d second commit
pick x9y8z7w third commit

# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但修改提交信息
# e, edit <提交> = 使用提交,但停下来修改内容
# s, squash <提交> = 使用提交,但合并到上一个提交
# f, fixup <提交> = 类似于 squash,但丢弃提交信息
# d, drop <提交> = 删除提交
# x, exec <命令> = 执行 shell 命令
# b, break = 在此处停止(用 --continue 继续 rebase)

交互式 Rebase 是 Git 最强大的功能之一,它允许你对已经提交的历史进行任意修改:压缩提交、修改提交信息、删除提交、拆分提交、重新排列提交顺序等。它就像一个提交历史的编辑器,让你可以把零散、混乱的开发历史整理成清晰、逻辑化的提交历史。

1:基本用法

交互式 Rebase 的命令格式是:

bash 复制代码
git rebase -i <目标提交>

其中 <目标提交> 是你想要整理的提交范围的前一个提交。例如,想要整理最近 3 次提交,可以执行:

bash 复制代码
git rebase -i HEAD~3

执行后会进入一个编辑界面,显示最近 3 次提交,每个提交前面有一个命令,默认是 pick

bash 复制代码
pick 7c3d8f9 first commit
pick a1b2c3d second commit
pick x9y8z7w third commit

# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但修改提交信息
# e, edit <提交> = 使用提交,但停下来修改内容
# s, squash <提交> = 使用提交,但合并到上一个提交
# f, fixup <提交> = 类似于 squash,但丢弃提交信息
# d, drop <提交> = 删除提交
# x, exec <命令> = 执行 shell 命令
# b, break = 在此处停止(用 --continue 继续 rebase)

2:5种常用操作

1:修改提交信息

如果你发现之前的提交信息写错了,或者写得不够清晰,可以用 reword 命令修改:

bash 复制代码
reword 7c3d8f9 first commit  # 修改这个提交的信息
pick a1b2c3d second commit
pick x9y8z7w third commit

保存退出后,Git 会停下来,让你编辑这个提交的信息,编辑完成后保存即可。

2:压缩提交

开发过程中我们经常会产生很多零散的提交,比如 "修复 bug"、"再修复 bug"、"第三次修复 bug"。这些提交对项目历史没有价值,可以用 squash 命令把它们合并成一个提交。

bash 复制代码
pick 7c3d8f9 first commit
squash a1b2c3d second commit  # 合并到上一个提交
squash x9y8z7w third commit   # 合并到上一个提交

保存退出后,Git 会让你编辑新的合并提交的信息,编辑完成后保存即可。

fixupsquash 类似,区别是 fixup 会直接丢弃被合并提交的信息,只保留内容,不需要编辑新的提交信息。

3:删除提交

如果你想删除某个提交,只需要把 pick 改成 drop,或者直接删除那一行即可:

bash 复制代码
pick 7c3d8f9 first commit
drop a1b2c3d second commit  # 删除这个提交
pick x9y8z7w third commit

注意:删除提交会丢弃该提交的所有修改,如果这个提交和其他提交有依赖,可能会导致后续冲突。

4:重新排列提交顺序

你可以直接在编辑界面中拖动行,改变提交的顺序:

bash 复制代码
pick x9y8z7w third commit   # 原来的第三个提交放到第一个
pick 7c3d8f9 first commit
pick a1b2c3d second commit

保存退出后,Git 会按照新的顺序重新应用这些提交。

5:修改提交内容

如果你发现某个提交的内容有错误,想要修改,可以用 edit 命令:

bash 复制代码
pick 7c3d8f9 first commit
edit a1b2c3d second commit  # 停下来修改这个提交
pick x9y8z7w third commit

保存退出后,Git 会应用到这个提交时停下来,你可以修改工作区的文件,然后执行:

bash 复制代码
git add .
git commit --amend  # 修改这个提交
git rebase --continue  # 继续 Rebase

3:拆分提交

交互式 Rebase 最神奇的操作之一是拆分提交。如果你把多个不相关的修改写到了一个提交里,可以用 edit 命令把它拆分成多个提交:

  1. edit 标记想要拆分的提交
  2. 当 Git 停下来时,执行 git reset HEAD^,将提交的修改撤回到工作区
  3. 然后分多次 git addgit commit,把修改拆分成多个提交
  4. 最后执行 git rebase --continue 完成 Rebase

6:分支的高级操作

1:分支的重命名

重命名本地分支非常简单:

bash 复制代码
# 将 old-name 分支重命名为 new-name
git branch -m old-name new-name

底层原理 :Git 只是将 .git/refs/heads/ 目录下的 old-name 文件重命名为 new-name,文件内容保持不变。

2:比较两个分支的差异

Git 提供了多种方式来比较分支之间的差异:

bash 复制代码
# 比较两个分支的整体差异
git diff branch1 branch2

# 只比较某个文件在两个分支的差异
git diff branch1 branch2 -- path/to/file

# 查看哪些提交在 branch2 有,但在 branch1 没有
git log branch1..branch2

# 查看哪些文件在两个分支之间有差异
git diff --name-only branch1 branch2

3:查看分支的演化历史

git log--graph 参数可以直观地查看分支的演化历史:

bash 复制代码
git log --graph --oneline --decorate --all

这个命令会输出一个 ASCII 图形化的分支历史,非常清晰。推荐把它设置为一个别名,方便日常使用:

bash 复制代码
git config --global alias.lg "log --graph --oneline --decorate --all"

以后只需要执行 git lg 就可以查看漂亮的分支历史了。

4:恢复被删除的分支

如果你不小心删除了一个重要的分支,不要慌,Git 几乎可以恢复所有丢失的提交和分支。

恢复被删除分支的步骤:

  1. git reflog 找到被删除分支的最后一个提交的哈希值
  2. 基于这个哈希值创建一个新分支:git checkout -b 新分支名 哈希值
bash 复制代码
# 查看所有操作历史,找到被删除分支的最后一个提交
git reflog
# 输出:
# a1b2c3d (HEAD -> master) HEAD@{0}: commit: last commit on feature
# 7c3d8f9 HEAD@{1}: checkout: moving from feature to master
# x9y8z7w HEAD@{2}: branch: deleted feature
# ...

# 基于这个哈希值恢复分支
git checkout -b feature-recovered a1b2c3d

7:常见分支问题

1:不小心在错误分支上提交代码

这是最常见的错误之一。如果你在 master 分支上提交了代码,但这些代码应该在 feature 分支上,可以用下面的方法修复:

bash 复制代码
# 1. 在当前提交的基础上创建 feature 分支
git checkout -b feature

# 2. 切换回 master 分支,回退到上一个提交
git checkout master
git reset --hard HEAD^

这样所有的修改都被转移到了 feature 分支,master 分支恢复到了原来的状态。

2:撤销一个合并提交

如果你合并了一个错误的分支,想要撤销这个合并,可以用 git revert 命令:

bash 复制代码
# 撤销合并提交,-m 1 表示保留第一个父提交的内容
git revert -m 1 <合并提交的哈希值>

注意git revert 不会删除原来的合并提交,而是会创建一个新的提交,撤销合并提交带来的所有修改。这是因为合并提交已经存在于历史中,如果删除它会导致其他基于这个合并的提交出现问题。

3:只合并某个分支的部分提交

有时候你不想合并整个分支,只想合并其中的某几个提交。这时候可以用 git cherry-pick 命令:

bash 复制代码
# 合并单个提交
git cherry-pick <提交哈希值>

# 合并多个提交
git cherry-pick <提交1> <提交2> <提交3>

# 合并一个范围的提交(从 commitA 到 commitB,不包含 commitA)
git cherry-pick commitA..commitB

合并单个提交 git cherry-pick <提交哈希值> # 合并多个提交 git cherry-pick <提交1> <提交2> <提交3> # 合并一个范围的提交(从 commitA 到 commitB,不包含 commitA) git cherry-pick commitA..commitB

相关推荐
我先去打把游戏先1 小时前
Ubuntu虚拟机(服务器版本)Git卸载完全教程——彻底移除与清理配置
服务器·git·单片机·嵌入式硬件·物联网·ubuntu·51单片机
不做无法实现的梦~3 小时前
Git Clone 使用 Watt/Steam++ 加速时报证书错误的原因与解决方法
大数据·git·elasticsearch
黑猫警长丶4 小时前
Git 操作笔记
笔记·git
MageGojo4 小时前
Whois 域名查询 API 接入实战:用一个 GET 请求获取域名注册信息
java·git·github
黑猫警长丶4 小时前
Git 本地操作基础
git
白狐_7984 小时前
从功能开发到开源维护:一个 Python 可视化项目的 Git 分支、维护文件与 PR 流程实践
git·python·开源
江华森5 小时前
Git + Maven Java 项目部署实战全指南
运维·笔记·git·学习·maven
火车叼位20 小时前
用脚本固化 Git Squash 合并与文件排除流程
git
wunaiqiezixin1 天前
git常用命令总结
git