你说的对,但是《Git》是由 Linus Torvalds 自主研发的一款去中心化的版本控制软件……
你说的对,但是《Git》是由 Linus Torvalds 自主研发的一款去中心化的版本控制软件。软件运行在一个被称作「repository」的世界,在这里,被用户
git add的工作区文件将被记录进暂存区,git commit生成版本历史节点。你将扮演一位名为 contributor 的神秘角色,在多人的协作中邂逅性格各异、能力独特的同伴们,在各自的设备上轮流rm -rf项目文件夹,然后重新git clone——同时,逐步发掘「版本控制」的真相。
git 和 GitHub 并不只为程序员而生,也不只为团队而生。
作为一种版本控制软件——
- 能编码为计算机数据的内容;
- 除了最终结果以外也关注中间版本,可能会在不同版本之间返工的项目
——理论上都可以用 git 来管理。
(当然实际上,还是程序员的纯文本“小”文件更适合 git,团队协作时更需要 git。)
单人版本记录
- 在 git 服务提供商(下文也简称为远端)的网页上创建仓库(repository,一种特殊的文件夹)。然后用
git clone URL将远端仓库克隆到本地- 另一种方法是在本地文件夹
git init- 优点是更容易适配一些模板或者框架的创建流程,比如 next.js, python poetry;
- 缺点是和远端仓库的对接需要更进阶的 git 知识
- 另一种方法是在本地文件夹
git pull将远端的更新拉取到本地。git pull=git fetch+git merge
- 在仓库内增加、修改、删除文件
git status可以看到工作区内文件们的总体改动情况git diff FILE可以检查某个文件内容的具体变化- 一些图形界面软件可以可视化修改状态(广告位招商)
git add FILE将工作区各文件交给 git 追踪,生成 blob,加入 git 的暂存区 (index)。git commit根据暂存区的各文件构建一个历史节点,在弹出的编辑器里写 commit message,简明概括这一版本的改动,方便将来回顾。git push将本地的更新推送到远端。- GOTO 第 2 步……
多人协作工作流
所谓 git 工作流,指的是 git 硬性规定的接口之外,团队为了方便管理,自行额外约定的各项 git 功能的使用方式。
这个文档 https://www.atlassian.com/git/tutorials/comparing-workflows 比较了几种常见的工作流 (centralized, feature branching, Gitflow, forking),看了一下——
对于绝大多数课题组的规模,feature branch 工作流基本就够用了,而且它的官僚主义成本也可以承受。
Feature Branch 工作流
-
课题的实际推动者(不一定是老板)维护远端的
main分支,让最新一个 commit 保持为整个项目的交付状态。 -
参与者
git clone URL到本地以后,用以下命令从main分支的最新 commit 开始工作:git checkout main git fetch origin # 确认当前没有未提交的本地修改后: git reset --hard origin/main -
参与者有新的工作要做时,用
git checkout -b NEW_FEATURE创建一个新的分支,分支的名字NEW_FEATURE简要概括工作主题。 -
在分支内工作,和“单人版本记录”的第 3—5 步相同。
-
git push -u origin NEW_FEATURE把本地分支推送到远端,第一次之后可以省略git push后面的部分。 -
分支的工作完成后,通知课题实控人合并分支。
-
课题实控人在
main分支上执行git merge NEW_FEATURE。 -
参与者跳 GOTO 第 2 步(初次参与)或第 3 步(已经参与过)……
Forking 工作流
在 GitHub 上,每个参与者都有自己的账号,但只有实控人才掌握项目官方 repository。这时上面的 feature branch 工作流中的——
- 第 2 步之前,参与者要在自己的账号下 fork 项目实控人的 repository;
- 第 5 步的远端,指的是参与者自己的 forked repository
- 第 6 步的通知合并,变成在 GitHub 网站上发起 pull request,从参与者的
NEW_FEATURE分支指向实控人的main分支
异常处理
“还是换回第 1 版吧”
每一个 commit 都是版本历史中的一个时间节点,也就是一个版本。在 git 系统内部,每一个 commit 有一个不重复的哈希值来索引。
git log 可以看到每个 commit 的哈希值和提交信息。
回到之前某个 commit 时,可能有两种想法:
- 只是看看,运行一下项目,不作进一步开发:
git checkout COMMIT_HASH - 以这个 commit 为起点开始一段新思路的开发,也就是创建一个新的分支:
git checkout -b NEW_BRANCH COMMIT_HASH
与别人的分支合并时出现矛盾
有时在我们从 main 创建分支之后,有其他人在我们之后又从 main 新开了一个分支。
如果对方比我们先合并进 main 的话,我们后来进行合并时,有些文件在两个分支中都被修改过,git 无法自动决定保留哪一版,就会出现合并冲突 (merge conflict)。
因此就需要加快工作进度,把合并冲突的球踢给对方。
不完全是开玩笑,预防合并冲突的最佳实践就是让每个分支的寿命尽可能短,快进快出,前一个人的分支合并之后下一个人再开始自己的分支。
假如冲突冲突已经发生,git 会按行标记两个分支不同的内容:
<<<<<<< HEAD
自己的版本
=======
对方的版本
>>>>>>> other-branch
解决方法:
- 打开冲突的文件
FILE.txt - 删除冲突的标记和不想要的那一版本的内容,保存
git add FILE.txtgit commit构建一个冲突解决的历史节点
git rebase 预防合并冲突
为了预防合并冲突,在 main 分支有任何新的 commit 之后,都最好用 git rebase 提前反映到我们当前的 NEW_FEATURE 分支。
举个例子,当我们在 E 分出去之后,main 上又新增了 F, G 两个 commits
A---B---C NEW_FEATURE
/
D---E---F---G main
git stash暂时保存NEW_FEATURE分支上,我们工作到一半还没有 commit 的内容git checkout main转到 main 分支git pull获取远端的新 commitsgit checkout NEW_FEATURE回到我们的分支git rebase main
A'--B'--C' NEW_FEATURE
/
D---E---F---G main
这样操作之后,我们的 A’ 的上游从 E 变成了 G。
有时候这种操作已经来不及了,git rebase 本身就会发生合并冲突,那就还是要回到上一节的方法去解决。
本文收录于以下合集: