Git stash vs Git commit:何时使用哪个?

Git Stash vs. Git Commit:何时使用哪个?

在 Git 的世界里,版本控制是核心。我们通过创建提交(commit)来记录项目的演进,每一个提交都代表了代码库在特定时间点的一个快照。然而,在日常开发中,我们经常会遇到这样的情况:正在进行的工作尚未完成,但又需要切换到另一个分支去处理紧急任务,或者临时查看之前的某个版本。这时,git stash 就派上了用场。本文将深入探讨 git stashgit commit 这两个命令的区别,以及在各种场景下如何明智地选择使用哪一个。

1. Git Commit:版本控制的基石

git commit 是 Git 中最基础也是最重要的命令之一。它将暂存区(staging area)中的更改保存到本地仓库的历史记录中。每次提交都伴随着一条提交消息,用于描述本次更改的内容和目的。

1.1 提交的本质

  • 快照: 提交本质上是代码库在某个时间点的完整快照。Git 使用一种高效的存储机制,只保存文件之间的差异,而不是每次都复制整个代码库。
  • 历史记录: 提交构成了一条线性的历史记录,我们可以通过 git log 命令查看。每个提交都有一个唯一的 SHA-1 哈希值作为标识符。
  • 分支基础: 分支是基于提交创建的。一个分支实际上就是一个指向特定提交的指针。

1.2 何时使用 Git Commit

一般来说,当你的工作达到一个相对稳定、逻辑完整的状态时,就应该创建一个提交。以下是一些具体的场景:

  • 完成一个功能: 当你实现了一个新的功能,并且经过测试,确认没有问题时,就应该创建一个提交。
  • 修复一个 bug: 当你修复了一个 bug,并且验证了修复的有效性时,就应该创建一个提交。
  • 重构代码: 当你对代码进行了重构,提高了代码的可读性、可维护性或性能时,就应该创建一个提交。
  • 阶段性进展: 即使一个功能尚未完全完成,但你已经取得了一定的阶段性进展,也可以创建一个提交,以便记录你的工作进度。这有助于你在后续工作中回溯到之前的状态。
  • 定期提交: 养成定期提交的习惯,即使你的工作没有达到一个明显的里程碑。这可以避免因为意外情况(如电脑死机、断电等)导致大量工作丢失。

1.3 提交的最佳实践

  • 原子性提交: 每个提交应该只包含一个逻辑上相关的更改。避免将多个不相关的功能或 bug 修复放在同一个提交中。
  • 清晰的提交消息: 提交消息应该清晰、简洁地描述本次更改的内容和目的。使用现在时态,以动词开头,例如 "Add feature to handle user authentication"。
  • 频繁提交: 不要等到工作完成一大半才提交。频繁提交可以让你更好地跟踪项目的演进,也更容易回滚到之前的状态。

2. Git Stash:暂存未提交的更改

git stash 命令用于将当前工作目录和暂存区中的未提交更改保存到一个特殊的存储区域(称为 "stash"),并将工作目录和暂存区恢复到干净的状态(即与最近一次提交一致)。

2.1 Stash 的工作原理

  • 存储更改: git stash 会创建一个或多个特殊的提交对象,用于存储未提交的更改。这些提交对象不会出现在常规的提交历史记录中。
  • 恢复工作目录: 在存储更改后,git stash 会将工作目录和暂存区恢复到干净的状态。
  • 多个 stash: 你可以创建多个 stash,每个 stash 都有一个唯一的标识符(例如 stash@{0}stash@{1} 等)。
  • 应用 stash: 可以使用 git stash applygit stash pop 命令将 stash 中的更改重新应用到工作目录。git stash pop 在应用更改后会删除对应的 stash。

2.2 何时使用 Git Stash

git stash 主要用于以下场景:

  • 临时切换分支: 当你正在进行一项工作,但需要紧急切换到另一个分支去处理其他任务(例如修复一个紧急 bug)时,可以使用 git stash 来暂存当前的更改。
  • 拉取远程更改: 当你试图拉取(git pull)远程分支的更改时,如果本地有未提交的更改,Git 会阻止你拉取,以避免冲突。这时可以使用 git stash 来暂存本地更改,然后再拉取远程更改。
  • 临时保存工作: 当你需要临时保存当前的工作状态,但又不想创建一个提交时(例如,你需要尝试一个不同的解决方案,但不确定是否可行),可以使用 git stash
  • 清理工作目录: 当你希望清理工作目录,但又不想丢失未提交的更改时,可以使用 git stash

2.3 Stash 的常用命令

  • git stash: 将未提交的更改保存到 stash。
  • git stash save "message": 保存 stash 并添加一条描述消息。
  • git stash list: 列出所有的 stash。
  • git stash apply stash@{n}: 应用指定的 stash(n 是 stash 的序号)。
  • git stash pop stash@{n}: 应用指定的 stash,并在应用后删除该 stash。
  • git stash drop stash@{n}: 删除指定的 stash。
  • git stash clear: 删除所有的 stash。
  • git stash branch <branchname> stash@{n}:从指定的 stash 创建一个新分支。
  • git stash show -p stash@{n}:以补丁(patch)的形式显示指定 stash 的内容。
  • git stash -ugit stash --include-untracked:同时保存未跟踪的文件。
  • git stash -agit stash --all:同时保存未跟踪和被忽略的文件(通常不推荐)。

3. Git Stash vs. Git Commit:深入对比

特性 Git Commit Git Stash
目的 记录项目的历史演进,保存代码库在特定时间点的快照。 临时保存未提交的更改,以便切换分支、拉取远程更改或清理工作目录。
存储位置 本地仓库的历史记录中。 特殊的存储区域(stash)。
可见性 通过 git log 可以查看。 通过 git stash list 可以查看。
持久性 永久保存,除非使用 git resetgit rebase 等命令进行修改。 临时保存,可以通过 git stash dropgit stash clear 删除。
对分支的影响 提交是分支的基础,分支是基于提交创建的。 Stash 不属于任何分支,它独立于分支存在。
协作 提交可以通过 git push 推送到远程仓库,与其他开发者共享。 Stash 通常只保存在本地,不与其他开发者共享。
回滚 可以通过 git resetgit revert 回滚到之前的提交。 可以通过 git stash applygit stash pop 将 stash 中的更改重新应用到工作目录。
适用场景 当工作达到一个相对稳定、逻辑完整的状态时,例如完成一个功能、修复一个 bug、重构代码等。 当需要临时切换分支、拉取远程更改、临时保存工作或清理工作目录时。
原子性 强调原子性,每个提交应该只包含一个逻辑上相关的更改。 不强调原子性,一个 stash 可以包含多个不相关的更改。
提交消息 必须有提交消息,用于描述本次更改的内容和目的。 可以选择性地添加描述消息(git stash save "message")。
最佳实践 原子性提交、清晰的提交消息、频繁提交。 及时清理不再需要的 stash,避免 stash 列表过长。

4. 常见问题与误区

4.1 我应该频繁使用 git stash 吗?

不应该。git stash 应该只用于临时保存未提交的更改。如果你发现自己频繁使用 git stash,那么很可能你的提交策略有问题。你应该考虑更频繁地提交你的工作,或者将你的工作分解成更小的、逻辑上独立的单元。

4.2 git stash 会丢失吗?

git stash 不会轻易丢失,但也不是绝对安全。如果你的电脑发生严重故障(例如硬盘损坏),那么你可能会丢失你的 stash。此外,如果你不小心使用了 git stash dropgit stash clear 命令,也会丢失你的 stash。

4.3 我可以将 git stash 推送到远程仓库吗?

不能。git stash 只保存在本地,不能通过 git push 推送到远程仓库。如果你需要与其他开发者共享你的未提交更改,你应该创建一个新的分支,并在该分支上提交你的更改。

4.4 git stash applygit stash pop 有什么区别?

git stash apply 会将 stash 中的更改重新应用到工作目录,但不会删除对应的 stash。git stash pop 在应用更改后会删除对应的 stash。

4.5 我可以将未跟踪的文件保存到 stash 中吗?

可以。使用 git stash -ugit stash --include-untracked 命令可以同时保存未跟踪的文件。

4.6 我是否可以使用stash来打补丁?
可以。git stash show -p stash@{n} > my_changes.patch 可以将stash的内容输出为补丁文件。

5. 总结

git commitgit stash 是 Git 中两个非常有用的命令,但它们的目的和使用场景完全不同。git commit 用于记录项目的历史演进,保存代码库在特定时间点的快照。git stash 用于临时保存未提交的更改,以便切换分支、拉取远程更改或清理工作目录。

选择使用哪个命令取决于你的具体需求。如果你的工作已经达到一个相对稳定、逻辑完整的状态,那么你应该创建一个提交。如果你只是需要临时保存未提交的更改,那么你应该使用 git stash

理解这两个命令的区别和联系,并根据实际情况做出明智的选择,是高效使用 Git 的关键。希望本文能够帮助你更好地掌握 git stashgit commit 这两个命令,并在你的开发工作中发挥它们的作用。记住,良好的版本控制习惯是软件开发成功的基石之一。

THE END