初学者 Git 指南






4.59/5 (15投票s)
为初学者简要介绍 GIT
引言
无论你做什么,只要你是一名程序员,你就肯定需要使用某种类型的版本控制系统。市面上有几种,但最流行的是 GIT,如果你理解了 GIT 的逻辑,你就能轻松理解任何版本控制系统的逻辑。
我曾与许多优秀且经验丰富的程序员共事,但他们无法正确使用 GIT,因此与他们一起工作简直是一场噩梦。我经常看到的另一种典型情况是,有人没有任何 GIT 经验,但他们不去尝试理解核心逻辑,反而去死抠每个命令如何工作。问题在于,如果不理解核心逻辑,你就永远无法正确使用 GIT。
网上有成千上万的 GIT 教程,但在我看来,大多数要么过于详细,要么结构不正确。所以,我将尝试在一个长帖中总结 GIT 的核心逻辑。本帖的目标不是提供深入的理解。很可能,你需要搜索特定的 GIT 命令来查找它们的具体参数。在某些情况下,我还尝试尽可能地简化描述,避免提及一些细节。目标是提供一个快速的概览和核心理解,以便你能开始在你的项目中使用 GIT。
作为练习,你可以在这里在线尝试所有内容:http://git-school.github.io/visualizing-git/。
GIT 的目的是什么?
GIT 的目的是让多个开发者能够协作处理同一个文件,并以安全舒适的方式共享他们的成果。此外,GIT 会跟踪更改,因此你可以随时切换到任何旧版本。
GIT 的一个巨大优势是你可以完全离线工作。你只需要在与所谓的远程仓库同步时才需要联网。GIT 也在多个平台上运行。默认情况下,GIT 是一个命令行工具,但有数以千计的 GUI 工具可供使用。
GIT 中与远程仓库的通信
核心逻辑是有一个所谓的远程仓库。可以通过调用 git init --bare
来创建。这个仓库将被克隆到多个计算机上(例如,为每个开发者)。这个步骤可以通过 git clone
命令完成。在 git clone
之后,你将在本地拥有远程仓库的最新状态。
此时,你可以开始对代码进行更改,这些更改仅存在于你的本地,被称为未暂存的更改。你想要保留的更改需要通过 git add
命令暂存。这将把你的更改移到暂存区域。此时,我们还没有完成,它还没有真正成为你本地 GIT 仓库的一部分。
首先,你需要执行一次 GIT commit
。一个 commit
包含多个属于一起的更改。它也可以包含多个文件的更改。当你调用 git commit
命令时,它会为所有暂存的更改创建一个 commit
。一个 commit
也是你代码的一个版本快照,你可以随时切换回该版本。一旦提交,你就可以调用 git log
,这个命令会列出仓库的 commit
记录,从最后一个 commit
开始,在这个例子中应该是你的 commit
。
现在,这已经是你本地 git 仓库的一部分了,但对其他人来说仍然不可见。要让其他人看到,你需要将你的更改推送到远程。你可以通过 git push
来实现。它会将你仓库中存在但服务器上不存在的所有更改推送到服务器。
要从服务器获取其他人的更改,你需要调用 git fetch
。这个命令会获取你尚未拥有的所有远程更改。但重要的是,它仍然不会覆盖你本地的当前代码。要做到这一点,你需要执行 git merge
。稍后,我们将详细介绍它到底意味着什么。由于 git fetch
和 git merge
经常一起使用,还有一个 git command: git pull
,它等同于 git fetch + git merge
。在大多数情况下,程序员只是使用这个命令。
作为总结,我用画图工具随意画了一下,生成了这张图
什么是 Commit?
在 GIT 中,每个 commit
都有一个内容,即仓库中一系列更改的集合。它还有一个 commit
消息,是对 commit
的描述。你需要在提交时使用 git commit
添加它。一个 commit
还有一个哈希键,这是自动生成的,是 commit
的标识符。commit
还有一个父级,即一个(或多个) git commit
的哈希。通过使用 git commit
操作,就会生成这样一个 commit
,其父级自动就是你当前的 HEAD commit
。
Commits 之间如何连接?
想法很简单,每当你有了新更改,你就创建一个 commit
并推送它,这样其他人就能获取到。但如何知道哪个是最后一个 commit
呢?通常,每个 commit
都知道它的父级,并且 commit
形成一个链条。在这种情况下,情况很简单。但有时,情况会变得更复杂。
分支
让我们看看当多个 commit
具有相同父级时的情况。这可能发生在两个同事基于同一个 commit
开发两个不同的功能时。而这两个 commit
也各自有子 commit
,以此类推。这是相当典型的,但如果你的 git 历史中有太多这样的“分支”,就会变得混乱。为了解决这个问题,你可以引入分支。分支本质上就是一个命名的标签(或指针),指向一个 commit
。主分支默认称为 master。master 分支始终指向主链的最后一个 commit
。你可以随时通过调用 git branch branch_name
来创建一个新的分支,一个指向当前 commit
的新命名标签。这样以后,你就可以随时通过 git checkout branch_name
来获取那个 commit
。你可以通过 git branch -l
列出所有本地分支,并通过 git branch -D branch_name
删除任何分支。重要的是,如果你的分支有了子 commit
(即父级是这个标签 commit
的 commit
),那么分支标签就会指向这个子 commit
。换句话说:它总是指向你分支在历史中的最新 commit
。多亏了这一点,你也可以只推送或拉取特定的分支。
Merge Commits (合并提交)
现在,我们举个例子:你和你的同事正在开发两个功能,你们同时开始,并且基于同一个 commit
。你们都在开发,然后推送新的代码。所以,你会有两个具有相同父级的 commit
,但其中任何一个都不会包含所有的功能。如何解决这个问题?
很简单,你应该创建一个包含两个更改的 commit
。在 GIT 中,这被称为 merge commit
(合并提交)。一个 merge commit
有两个父级。幸运的是,大部分工作由 GIT 完成,它可以接管两个 commit
的更改。你可以通过调用 git merge
来实现此功能。
当同一段代码在两个 commit
中都被修改时,GIT 无法决定哪个版本是正确的。这被称为 merge conflict
(合并冲突)。Git 会告诉你哪些文件包含 merge conflict
。在这种情况下,它会在文件中显示两个版本。你需要手动打开这些文件,删除不需要的版本,保存它,然后对该文件调用 git add
。一旦所有合并冲突都得到解决,你应该输入 git merge --continue
。
Amend Commits (修改提交)
有一种方法可以修改已存在的提交。要做到这一点,你需要检出该 commit
。进行你的更改,使用 git add
暂存你的更改,然后像往常一样使用 git commit --amend
命令提交。在这种情况下,你的更改将被应用于你当前的 commit
。但请注意,如果不同的人在本地拥有同一个 commit
但内容不同,这可能会很危险。所以尽量避免这种方法。
如何移动 Commits?
还可以将一个或多个 commit
在分支之间移动,从链条中删除 commit
,或者改变 commit
的顺序。
变基 (Rebase)
Rebasing (变基) 意味着改变一个 commit
或一个分支的父级。首先,你需要检出你想要进行变基的分支。然后执行 git rebase new_base
,其中 new_base
可以是一个 commit
或一个分支名。它会将分支的父级更改为给定分支的顶部。这也意味着所有更改将应用于我们位于新基础中的 commit
。变基时也会发生冲突。它们可以像合并一样解决。请注意,不一致地使用合并和变基很容易导致混乱。
Interactive Rebase (交互式变基)
交互式变基是一个非常高级的功能。通过它,你可以将多个提交合并成一个,删除现有 commit
,更改 commit
顺序等。你可以通过输入 git rebase --interactive HEAD~10
来完成,其中 10 表示你想要在最近的 10 个 commit
中进行更改(当然,你可以添加任何数字)。输入此命令后,你将看到 commit
列表,每行开头都有 pick
关键字。下方会列出可能的关键字。最重要的可能是 drop
,它可以直接删除一个 commit
,以及 squash
,它可以将一个 commit
合并到前一个 commit
中。如果我们有很多小的 commit
并且想稍微清理一下历史记录,这会很有用。如果你改变这些行的顺序,commit
的顺序也会改变。一旦你保存了这个文本文件,更改就会被应用。当然,可能会出现冲突,你可以像往常一样解决它们。
Cherry-pick (挑选)
使用 git cherry-pick
,你可以将一个特定 commit
中的更改应用到另一个分支。如果你只需要另一个分支中的一两个 commit
,这会很有用。你需要检出你想要应用更改的分支。然后简单地输入 git cherry-pick commit_id
。
其他有用的功能
还有一些其他有用的功能可以让你在使用 git 时更轻松。
Git reflog
Git reflog 存储了你在本地仓库中进行的所有操作。你可以通过 git reflog
访问它。它会列出你做的每一步操作。你可以通过检出行开头提到的 commit_id
来回退到任何状态。这是一个在犯错后进行撤销的机会。
Git stash (储藏)
使用 git stash
,你可以本地保存更改。当你想要切换到另一个分支或 commit
,但又不想 commit
当前的更改时,这会很有用。只需输入 git stash
,它就会保存你的本地更改。你可以多次执行 git stash
,它会将每个更改单独存储在一个堆栈结构中。你可以通过调用 git stash list
来列出你储藏的更改,并通过 git stash pop
来重新获取它们。
有用的工具
你可以使用成千上万的 GIT 工具。然而,尽管许多程序员更喜欢完全从命令行操作,但在某些情况下,图形化概览非常有帮助。我将提及我最喜欢的两个工具,它们也相当常用。
Gitk
Gitk 是一个很好的工具,可以可视化你的历史(git log
)以及每个 commit
中的更改。
Git GUI
Git GUI 是一个很棒的工具,可以轻松地暂存和提交你的更改。