🩺 医生有听诊器。🔧
机械师有扳手。👨💻
我们开发人员有 Git。
您是否注意到,Git 对于代码处理如此重要,以至于人们几乎从未将其纳入他们的技术栈或简历中?人们假设您已经了解它,或者至少了解得足够多,但您真的了解吗?
Git 是一个版本控制系统 (VCS)。这项无处不在的技术使我们能够存储、更改代码并与他人协作。
🚨 作为免责声明,我想指出 Git 是一个庞大的话题。Git 书籍已经出版,博客文章也可能被误认为是学术论文。这不是我在这里要说的。我不是 Git 专家。我在这里的目的是写一篇我在学习 Git 时希望能有的 Git 基础知识文章。
作为开发人员,我们的日常工作围绕着阅读、编写和审查代码。Git 可以说是我们使用的最重要的工具之一。掌握 Git 提供的特性和功能是作为开发人员可以为自己做出的最佳投资之一。
让我们开始吧
如果您觉得我遗漏了或者应该更详细地介绍某个特定命令,请在下面的评论中告诉我。我会相应地更新这篇文章。🙏
当我们讨论这个话题时
如果您希望发挥您的 Git 技能并希望为 Glasskube 做出贡献,我们于 2 月正式启动,旨在成为 Kubernetes 包管理的默认解决方案。有了您的支持,我们可以实现这一目标。表达支持的最佳方式是在 GitHub 上为我们点赞⭐
让我们奠定基础
Git 是否曾让您感觉像 Peter Griffin?
如果您没有以正确的方式学习 Git,您就会面临不断挠头、陷入相同问题或后悔在终端中再次出现合并冲突的风险。让我们通过定义一些基本的 Git 概念来确保这种情况不会发生。
分支
在 Git 存储库中,您会发现一条开发主线,通常名为“main”或“master”(已弃用),从该主线分支出多个分支。这些分支代表同步工作流,使开发人员能够在同一项目中同时处理多个功能或修复。
提交
Git 提交可作为更新代码的集合,捕获特定时间点的项目代码快照。每次提交都会记录自上次提交以来所做的更改,所有这些共同构成了项目开发历程的全面历史记录。
当引用提交时,您通常会使用其唯一标识的加密哈希。
例子:
git show abc123def456789
这显示了有关该哈希的提交的详细信息。
标签
Git标签是 Git 历史记录中的里程碑,通常标记项目开发中的重要里程碑,例如releases
、versions
或standout commits
。这些标签对于标记特定时间点非常有用,通常代表项目旅程中的起点或主要成就。
头
当前签出的分支上的最新提交由 表示HEAD
,用作指向存储库中任何引用的指针。当您位于特定分支上时,HEAD
指向该分支上的最新提交。有时,它不是指向分支的尖端,而是HEAD
直接指向特定提交(detached HEAD
状态)。
阶段
了解 Git 阶段对于导航您的 Git 工作流程至关重要。它们代表在文件提交到存储库之前对文件进行更改的逻辑转换。
让我们深入研究 Git 阶段的概念:
工作目录👷
这working directory
是您编辑、修改和创建项目文件的地方。表示本地计算机上文件的当前状态。
暂存区 🚉
该staging
区域就像一个存放区或预提交区,您可以在其中准备更改,然后再将其提交到存储库。
此处有用的命令:
git add
也git rm
可用于取消暂存更改
本地存储库
本地存储库是 Git 永久存储已提交更改的地方。它允许您查看项目的历史记录、恢复到以前的状态以及与同一代码库上的其他人协作。
您可以使用以下方式提交暂存区中已准备好的更改:
git commit
远程存储库
远程存储库是一个集中位置,通常托管在服务器上(如 GitHub、GitLab 或 Bitbucket),您可以在其中与其他人共享和协作您的项目。
git push
您可以使用诸如和之类的命令git pull
将已提交的更改从本地存储库推送/拉到远程存储库。
Git 入门
好吧,你必须从某个地方开始,在 Git 中,那就是你的workspace
。你可以fork
创建clone
一个现有存储库并拥有该工作区的副本,或者,如果你在计算机上的新本地文件夹中完全重新开始,则必须使用 将其转换为 git 存储库git init
。下一步,至关重要的不容忽视的是设置你的凭据。
凭证设置
当运行推送和拉取到远程存储库时,您不想每次都必须输入用户名和密码,只需执行以下命令即可避免这种情况:
git config --global credential.helper store
首次与远程存储库交互时,Git 将提示您输入用户名和密码。此后,将不再提示您
值得注意的是,凭证以纯文本格式存储在
.git-credentials
文件中。
要检查配置的凭据,您可以使用以下命令:
git config --global credential.helper
使用分支
在本地工作时,了解当前在哪个分支至关重要。以下命令很有用:
# Will show the changes in the local repository
git branch
# Or create a branch directly with
git branch feature-branch-name
要在分支之间转换,请使用:
git switch
除了在它们之间转换之外,您还可以使用:
git checkout
# A shortcut to switch to a branch that is yet to be created with the -b flag
git checkout -b feature-branch-name
要检查存储库的状态,请使用:
git status
始终清晰查看当前分支的一个好方法是直接在终端中查看它。许多终端插件可以帮助实现这一点。下面就是其中一个。
使用提交
处理提交时,使用 git commit -m 记录更改,使用 git amend 修改最近的提交,并尽量遵守提交消息约定。
# Make sure to add a message to each commit
git commit -m "meaningful message"
如果你对上一次提交进行了更改,则不必完全创建另一个提交,你可以使用 -—amend 标志来修改最近的提交,其中包含暂存的更改
# make your changes
git add .
git commit --amend
# This will open your default text editor to modify the commit message if needed.
git push origin your_branch --force
⚠️ 使用时请谨慎
--force
,因为它可能会覆盖目标分支的历史记录。通常应避免在主分支上应用它。根据经验,最好多提交几次,以避免丢失进度或意外重置未暂存的更改。之后可以通过压缩多个提交或执行交互式变基来重写历史记录。
用于git log
显示按时间顺序排列的提交列表,从最近的提交开始,按时间倒序排列
操纵历史
操作历史涉及一些强大的命令。Rebase
重写提交历史,Squashing
将多个提交合并为一个,并Cherry-picking
选择特定的提交。
重新定基和合并
将变基与合并进行比较是有意义的,因为它们的目的是相同的,但它们实现的方式不同。关键的区别在于变基会重写项目的历史。对于重视清晰且易于理解的项目历史的项目来说,这是一种理想的选择。另一方面,合并通过生成新的合并提交来维护两个分支的历史记录。
在重新定基期间,功能分支的提交历史记录在移至HEAD
主分支时被重组
这里的工作流程非常简单。
确保您位于要重新设定的分支上,并从远程存储库获取最新更改:
git checkout your_branch
git fetch
现在选择你想要重新定位的分支并运行以下命令:
git rebase upstream_branch
重新定基后,如果分支已被推送到远程存储库,则可能需要强制推送更改:
git push origin your_branch --force
⚠️ 使用时请谨慎
--force
,因为它可能会覆盖目标分支的历史记录。通常应避免在主分支上应用它。
挤压
Git 压缩用于将多个提交压缩为一个单一的、有凝聚力的提交。
这个概念很容易理解,如果统一代码的方法是变基,则尤其有用,因为历史记录将被更改,因此务必注意对项目历史记录的影响。有些时候,我很难执行压缩,尤其是使用交互式变基时,幸运的是,我们有一些工具可以帮助我们。这是我首选的压缩方法,它涉及将 HEAD 指针向后移动 X 个提交,同时保留暂存的更改。
# Change to the number after HEAD~ depending on the commits you want to squash
git reset --soft HEAD~X
git commit -m "Your squashed commit message"
git push origin your_branch --force
⚠️ 使用时请谨慎
--force
,因为它可能会覆盖目标分支的历史记录。通常应避免在主分支上应用它。
挑选
挑选功能对于选择性地将更改从一个分支合并到另一个分支非常有用,尤其是在合并整个分支不可取或不可行时。但是,谨慎使用挑选功能非常重要,因为如果使用不当,可能会导致重复提交和不同的历史记录
要执行此操作,首先您必须识别要选择的提交的提交哈希,您可以使用 执行此操作git log
。一旦确定了提交哈希,您就可以运行:
git checkout target_branch
git cherry-pick <commit-hash> # Do this multiple times if multiple commits are wanted
git push origin target_branch
高级 Git 命令
签署提交
签署提交是验证 Git 中提交的真实性和完整性的一种方法。它允许您使用 GPG(GNU 隐私保护)密钥对提交进行加密签名,从而向 Git 保证您确实是提交的作者。您可以通过创建 GPG 密钥并将 Git 配置为在提交时使用该密钥来实现这一点。
步骤如下:
# Generate a GPG key
gpg --gen-key
# Configure Git to Use Your GPG Key
git config --global user.signingkey <your-gpg-key-id>
# Add the public key to your GitHub account
# Signing your commits with the -S flag
git commit -S -m "Your commit message"
# View signed commits
git log --show-signature
Git 引用日志
我们尚未探讨的一个主题是 Git 引用,它们是指向存储库中各种对象的指针,主要是提交,但也包括标签和分支。它们充当 Git 历史记录中的命名点,允许用户浏览存储库的时间线并访问项目的特定快照。知道如何浏览 git 引用非常有用,他们可以使用 git reflog 来做到这一点。
以下是一些好处:
恢复丢失的提交或分支
调试和故障排除
消除错误
交互式变基
交互式变基是 Git 的一项强大功能,可让您以交互方式重写提交历史记录。它使您可以在将提交应用于分支之前对其进行修改、重新排序、合并或删除。
为了使用它,您必须熟悉可能的操作,例如:
选择(“p”)
重新表述(“r”)
编辑(“e”)
压扁(“s”)
放下(“d”)
这里有一个有用的视频,可以帮助您了解如何在终端中执行交互式变基,我还在博客文章的底部链接了一个有用的工具。
与 Git 协作
原产地与上游
源是克隆本地 Git 存储库时与其关联的默认远程存储库。如果您已分叉存储库,则该分叉默认成为您的“源”存储库。
另一方面,上游是指您的存储库分叉的原始存储库。
为了使您的分叉存储库与原始项目的最新更改保持同步,您可以git fetch
从“上游”存储库中进行更改,然后将其合并或重新定位到本地存储库中。
# By pulling the pulled changes will be merged into your working branch
git pull <remote_name> <branch_name>
# If you don't want to merge the changes use
git fetch <remote_name>
要查看与本地 Git 存储库关联的远程存储库,请运行:
git remote -v
冲突
不要惊慌,当尝试合并或重新设置分支并检测到冲突时,这仅意味着存储库中同一个文件的不同版本之间存在冲突的更改,并且可以轻松解决(大多数时候)。
它们通常在受影响的文件中指示,Git 在其中插入冲突标记<<<<<<<
,=======
并>>>>>>>
突出显示冲突部分。
决定保留、修改或删除哪些更改,确保生成的代码有意义并保留预期的功能。
手动解决冲突文件中的冲突后,删除冲突标记<<<<<<<
、=======
和 ,>>>>>>>
并根据需要调整代码。
一旦您对解决方案满意,请保存冲突文件中的更改。
如果您在解决冲突时遇到问题,此视频可以很好地解释它。
流行的 Git 工作流程
存在各种 Git 工作流程,但需要注意的是,没有普遍适用的“最佳”Git 工作流程。相反,每种方法都有自己的优缺点。让我们探索这些不同的工作流程,了解它们的优点和缺点。
功能分支工作流程🌱
每个新功能或错误修复都在其自己的分支中开发,然后通过打开 PR 将其合并回主分支。
优点:隔离变化并减少冲突。
缺点:可能变得复杂并需要勤勉的分支机构管理。
Gitflow 工作流程
Gitflow 定义了一个严格的分支模型,其中为不同类型的开发任务预定义了分支。
它包括主分支、开发分支、功能分支、发布分支和修补程序分支等长期分支。
优点:适合有计划发布和长期维护的项目。
缺点:对于较小的团队来说可能过于复杂
分叉工作流程🍴
在此工作流程中,每个开发人员都会克隆主存储库,但不会直接将更改推送到主存储库,而是将更改推送到他们自己的存储库分支。然后,开发人员创建拉取请求以向主存储库提出更改,以便在合并之前进行代码审查和协作。
这是我们用于协作开源 Glasskube 存储库的工作流程。
优点:鼓励外部贡献者的合作,而无需授予对主存储库的直接写权限。
缺点:维护分支和主存储库之间的同步可能具有挑战性。
基于主干的开发🪵
如果您所在的团队专注于快速迭代和持续交付,您可能会使用基于主干的开发,即开发人员直接在主分支上工作,提交小而频繁的更改。
优势:促进快速迭代、持续集成,并注重向生产中提供小而频繁的变更。
缺点:需要强大的自动化测试和部署管道来确保主分支的稳定性,可能不适合具有严格发布时间表或复杂功能开发的项目。
什么叉子?
对于开源项目的协作,我们强烈建议您使用分叉,因为您可以完全控制自己的存储库副本。您可以进行更改、尝试新功能或修复错误,而不会影响原始项目。
💡 我花了很长时间才弄清楚,虽然分叉的存储库一开始是独立的实体,但它们仍与原始存储库保持着联系。通过这种联系,您可以跟踪原始项目中的更改,并将您的分叉与其他人所做的更新同步。
这就是为什么即使你推送到你的原始存储库,你的更改也会显示在远程存储库上。
Git 备忘单
# Clone a Repository
git clone <repository_url>
# Stage Changes for Commit
git add <file(s)>
# Commit Changes
git commit -m "Commit message"
# Push Changes to the Remote Repository
git push
# Force Push Changes (use with caution)
git push --force
# Reset Working Directory to Last Commit
git reset --hard
# Create a New Branch
git branch <branch_name>
# Switch to a Different Branch
git checkout <branch_name>
# Merge Changes from Another Branch
git merge <branch_name>
# Rebase Changes onto Another Branch (use with caution)
git rebase <base_branch>
# View Status of Working Directory
git status
# View Commit History
git log
# Undo Last Commit (use with caution)
git reset --soft HEAD^
# Discard Changes in Working Directory
git restore <file(s)>
# Retrieve Lost Commit References
git reflog
# Interactive Rebase to Rearrange Commits
git rebase --interactive HEAD~3
# Pull changes from remote repo
git pull <remote_name> <branch_name>
# Fetch changes from remote repo
git fetch <remote_name>