前言
- 起因:Git 的上手有点难度
- 目的:介绍 Git 基本概念和操作,帮你更快上手 Git
Git 优点
- 本地即可实现文件的版本管理,无需服务器
- 极轻量的分支操作,方便多分支并行开发
- 强大的功能:
rebase
,stash
,bisect
等等
基本概念
注:为避免不正确翻译反而给你带来困惑,除非毫无争议的概念,此处尽量采用英文。
提交 (commit)
一个提交是一次变更记录,由 commit
子命令生成。
多次提交环环相扣,形成树状提交历史:
commit
一个 commit 包含:commit id 、作者名字和邮箱、时间戳和 commit message (描述信息)
commit id
定义:一串40 个 [a-f]
字符( SHA1 摘要值)
其他简便的表示方法:
- 若不冲突,也可以开头的几个字符表示
- 分支名称
HEAD
当前分支的最后一次提交- 特殊语法,
master^
表示 master 的前一次 revision
更多请参考 man gitrevisions
HEAD
一种游标或者指针,同一时刻只能指向某个 commit ,此即 HEAD
。
仓库 (repository)
仓库用于存放所有版本管理相关的数据,包括提交、分支等。
- 本地仓库 (local)
即
.git/
目录,除非你很确定你在做什么,否则别修改其中的文件。 -
远端仓库 (remote) ,即在服务器的仓库
- 一个本地仓库可对应多个远端
- 本地通过 push/fetch/pull 子命令与远端交互
分支 (branch)
- 可以离线创建和管理,无需服务器交互
- remote-tracking branch ,名字格式是
<remote>/<branch>
此种分支你无法提交更改 - tracking branch: 用于追踪远端分支的分支 通过 fetch/pull/push 子命令来与远端进行同步 参考 Git: What is a tracking branch? - Stack Overflow
存储区域
文件除了已经存入仓库之外,还有两个地方:
- working tree
- staging area
Git 比 SVN 多了 staging area 这一个概念,请务必要理解它。
详细内容,请参考 git-add 。
working tree
- 可以理解为在文件系统中看到的目录和文件的状态、内容;
- 一个仓库默认有一个 working tree ,但也可以有多个
staging area (index)
通常翻译为“暂存区”。
index 是 working tree 的一个快照( snapshot ),下次提交的就是这个快照。
文件状态
- untracked 未纳入 git 中管理的文件
-
tracked 已纳入 git 中管理的文件
可以细分为三种文件:
- unmodified 未变动的文件
- modified 已经更改,但是未放入 staging area 的内容
- staged 已经更改,且已放入 staging area 的内容
文件状态转换图
文件状态转换图:
hunk: 差异块
-
hunk 概念:由
@@@...@@@
包围的一块连续的差异index fabadb8,cc95eb0..4866510 --- a/describe.c +++ b/describe.c @@@ -98,20 -98,12 +98,20 @@@ - static void describe(char *arg) ++static void describe(char *arg, int last_one) { + unsigned char sha1[20]; + struct commit *cmit; struct commit_list *list;
- hunk 比文件粒度更细,用于操作文件的一部分 “古老”的 diff/patch 工具就已有 hunk 概念
ignore 文件
- 用于忽略编译结果文件等文件
- 文件格式:文本文件,一行表示一个忽略规则
- 支持通配符,比如所有
.o
结尾的文件:*.o
- 可忽略目录,比如
obj/
不同作用范围的 ignore 文件
$HOME/.config/git/ignore
当前用户默认,对用户的所有工程都起作用$GIT_DIR/.gitignore
工程内,此文件自身也纳入 Git 管理$GIT_DIR/info/exclude
工程内,同时不纳入 Git 管理(不推荐)
注:工程内的 ignore 文件优先级高于 home 目录的。
基本操作
分支相关
git checkout foo
切换到某个分支git checkout -b bar
从HEAD
创建新分支,并切换到它git status
查看当前工作区的状态git add foo.clj
把文件当前内容放入 staging area
git commit -m "hello world"
提交git commit --amend
更改HEAD
commitgit diff
查看变更 默认对比 working area vs. index 的差异; 加--cached
对比 index 与上一次 commit 的差异。
提交日志查看
git log
查看当前分支的提交日志(历史)git log --oneline --decorate --graph --all
显示所有分支的提交历史 /img/slides/2021-10-07-git-log-all.png
分支合并操作
命令: git merge
,按是否 fast-forward 可以分为两种情况。
fast-forward merge
执行 git merge --ff-only topic
不带任何选项时, Git 会优先使用 ff 方式,建议明确加选项。
Before vs. After topic A---B---C topic | master / |/ D---E master D---E---A---B---C
non-fast-forward merge
执行 git merge --no-ff topic
,会有一个真正的 merge commit 。
示例1:
Before vs. After A---B---C topic A---B----C topic / / \ D---E master D---E------------F master
示例2:
Before vs. After A---B---C topic A-----B-----C topic / / \ D---E---F---G---H master D---E---F---G---H---I master
与远端相关的操作
git clone https://github.com/whatacold/texttoolkit.git
从远端克隆到本地git remote add origin https://github.com/whatacold/texttoolkit.git
添加一个远端到本地仓库git remote
查看远端,加-v
同时查看地址
git push origin
把当前分支同步到远端git fetch origin
从远端同步最新版本,但不改变 tracking branchgit pull origin
拉取最新代码并合并,相当于这两步:git fetch
+git merge
协作模式
详细请参考: Git - Distributed Workflows - git-scm.com
Centralized Workflow
一般企业内部的项目采用的是此种模式
Integration-Manager Workflow
GitHub/GitLab 的协作,采用的就是这种模式
COMMENT 黑科技
git rebase 更改分支的代码基( revision 会变 )
git cherry-pick: 摘取其他分支的一个、或者多个 commit
git blame: bug 该“责怪”谁
某行新加的代码由谁引入的
某行代码被谁删除引发 bug 的
git bisect: 二分查找法定位问题在何时引入
Tips
- 常用 git status 查看工作区状态,比如 commit 之前确认改动是否符合预期
- 提交到远端的 commit ,再更改可能对别人造成影响,因此不要着急 push
- 多人协作的分支,已经 pushed 的 commit ,不要再 rebase 更改
参考资料
- Git Cheatsheet - about.gitlab.com
- 使用手册
git help <subcommand>
/man git-<subcommand>
一般在手册的结尾部分,会有一些示例演示常见用法。 - Pro Git 2nd Edition
- Video: Three ways to discard/revet commits with Magit