本文主要分为两部分,前一部分是本人学习和工作中使用 Git 的总结经验,后半部分是总结的 Git & GitHub 的学习指南。如果想直接体系学习,可以直接按照指南路线学习。如果你有经验,可以看看第一部分,看看我工作和平时怎么使用 Git 的,看完后如果你有更好的解决方案,或者建议,欢迎评论区交流学习。
Git 是一个开源的基于快照流
的分布式版本控制
系统,旨在快速高效地处理从小型项目到大型项目的所有项目。
Git 有三种状态:
已修改
:modified,表示修改了文件,但还没保存到数据库中。
已暂存
:staged,表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
已提交
:committed,表示数据已经安全地保存在本地数据库中。
三种状态分别对应 Git 项目的三个阶段:
工作区:对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区:是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。
Git 目录:是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
基本的 Git 工作流程如下:
注:图片来源于1.3 起步 - Git 是什么?
更好的理解 Git 的三个区,可以参考文章:Git 工具 - 重置揭密 一文前半部分。
首先,要学会自救,忘记命令的用法作用,这很正常,掌握以下命令,我们就不用去刻意记忆 git 命令用法,忘记了,查一下就行。
git help
:获取命令的作用和使用完整说明,如:git help init
。
git
:同 git help
。如:git init --help
。
man git-
:Linux 下可用,同 git help
,如 man git-init
。
如果你已经熟悉了 git 命令的作用,只是忘记了它可以使用哪些参数,那么可以使用以下命令,快速查询某个命令能使用的参数及其作用。
git
:列出指定 git 命令的可用参数,如:git init -h
。
掌握自助查询后,我们就可以开始创建 git 仓库或者拉取 git 仓库。
git init
:创建一个空的 git 仓库或者重新初始化一个 git 仓库。一般使用在自己本地新建一个项目,使用 git 进行管理的时候
。
如:git init
,会在当前目录创建一个 .git
文件夹,这个里面就存储 git 管理当前项目的所有文件。想了解 Git 的原理可以参看Git 内部原理。
git clone
:克隆一个仓库到本地当前目录。一般,工作中我们刚进公司,都需要先 clone 一堆仓库到本地,再进行需求开发。或者你发现一个好的开源项目,也可以 clone 到本地,更方便地阅读源码
。如:git clone [email protected]:git/git.git
。
我们已经创建好一个 git 仓库,现在我们修改了一些文件,接下来我们需要将修改的文件通过 git 管理起来。
git add
:将文件加到暂存区(索引)。比如:git add *
将所有文件添加到暂存区。
git commit
:将暂存区修改提交到版本库。如:git commit -m "fix bug001"
。如果你本次提交和上次提交的信息一致的话, 可以使用git commit --amend
直接将本次修改内容,合并到上一次提交中。避免产生冗余的提交信息。
说明:一般不同公司或者开源项目对于提交格式和信息会有约束,方便他人了解你本次修改的原因。
在 Windows 下开发的话,我推荐 TortoiseGit 工具,TortoiseGit 是Git版本控制系统的免费开源客户端,可以方便的进行 Git 操作。
根据自己习惯和开发环境选择最适合自己的工具即可,目的都是为了提升开发效率。
TortoiseGit 使用文档
现在我们已经实现了某个功能特性,需要将本地的代码推送到自己的远程仓库,这时我们就需要使用 git push
git push
:使用本地的git项目更新远程仓库,也就是将本地修改推送到远程仓库。如:git push --all
:将本地所有修改推送到远程仓库。
如果需要强制推送到远程仓库,或者直接使用本地仓库覆盖远程仓库,可以用:git push -f
。注意这是一个危险操作,操作前,请确认你正在做什么。
分支是 Git 中的一个重要特性,它非常好地解决了开发过程中开发迭代中,软件功能稳定性问题。通常,我们有3种分支。
稳定分支:功能稳定,一般用于构建版本,发布上网。
开发分支:功能相对不稳定,一般用于开发新特性,等新特性稳定后,会阶段性的将新特性回合到稳定分支。
bug修复分支:解决稳定分支发现的bug,需要同时合入稳定分支和开发分支。
分支管理是一门艺术,分支越多,维护成本会越高,这里就不展开了。
下面主要介绍下分支的基本操作:查看、创建、切换、删除。
git branch
:查看分支。
[root@TanjyAliyun demo]# git branch
dev
* master
*
标识当前 HEAD
指针指向的分支,也就是当前所在的本地分支。在 Git 中,HEAD
是一个指针,指向当前所在的本地分支,可以将 HEAD
想象为当前分支的别名。
git branch
:基于 HEAD
创建
新分支,但不会自动切换到新分支。如:git branch dev
:创建 dev 分支。
git checkout -b
:基于 HEAD
创建并切换到
新分支。如:git checkout -b dev
:创建 dev 分支,并切换到 dev 分支。
git checkout -b
:基于当前分支的某一次 commit-id 创建并切换到
新分支。
一般使用场景:
[root@TanjyAliyun demo]# git branch
* dev
master
[root@TanjyAliyun demo]# git log
commit 2ac24ccead054b679a56613dfb72a4b40102e6ce
Author: jock <6666@hw.com>
Date: Wed Aug 3 21:53:33 2022 +0800
add b.txt
commit e48eb6abdb2e0bc525036f3e528b674d32809e3c
Author: jock <6666@hw.com>
Date: Wed Aug 3 21:51:51 2022 +0800
add a.txt
commit f1d00a5251278236f322f0a58c712c59cb3a4cda
Author: jock <6666@hw.com>
Date: Wed Jul 13 21:41:12 2022 +0800
add test.txt
[root@TanjyAliyun demo]# git checkout -b bug-fix-0804 -m f1d00a52512
Switched to a new branch 'bug-fix-0804'
[root@TanjyAliyun demo]# git branch
* bug-fix-0804
dev
master
[root@TanjyAliyun demo]# git log
commit f1d00a5251278236f322f0a58c712c59cb3a4cda
Author: jock <6666@hw.com>
Date: Wed Jul 13 21:41:12 2022 +0800
add test.txt
[root@TanjyAliyun demo]# git branch
* bug-fix-0804
dev
master
[root@TanjyAliyun demo]# git checkout dev
Switched to branch 'dev'
[root@TanjyAliyun demo]# git branch -d bug-fix-0804
Deleted branch bug-fix-0804 (was f1d00a5).
[root@TanjyAliyun demo]# git branch
* dev
master
git checkout xxx
:切换到 xxx 分支。
git branch -d xxx
:删除 xxx 分支。
接下来,我们可能先clone了项目,但是本地一直没有做修改,同事合入了很多代码到项目中,这时我们想知道同事改了啥。就可以用到以下命令:
git fetch
:从服务器拉取本地没有的数据,但它不会修改本地工作目录中的内容,它只抓取服务器数据,需要你自己合并。
git merge
:将另一个分支的修改合并到当前分支。如:当前 HEADE 是 master 分支,使用 git merge dev
命令表示,将 dev 分支合并到 master 分支。
git pull
:等于 git fetch + git merge
,拉取并合并远程分支到当前分支。
接下来,介绍一下,git merge
合并的三种处理方式:
fast-forward
:快进式合并,不会生成额外的合并 commit 记录。快进行合并的前提是本地当前分支没有修改,远程分支有合入。Git 很懒,默认先采用快进式合并,如果没法进行快进式合并, Git 就会使用非快进式方式合并。如:git merge --ff dev
no-fast-forward
:非快进式合并,无论能否进行快进式合并,Git 都会生产额外的合并commit记录。如:git merge --no-ff dev
。通常我们本地开发有提交记录,远程仓库有同事合入,这时拉取合并远程分支时,就会使用非快进式合并,生成合并 commit 记录。fast-forward-only
:只进行快进式合并,无法进行快进式合并时,放弃合并。如:git merge --ff-only dev
。本地和远程都有提交记录时,还想使用fast-forward-only
模式合并,那么就需要用到 变基(git rebase
)命令了。
git rebase
:变基。改变当前分支修改的老基准,将其他分支的最新提交作为新基准,当前分支修改移动到其他分支最新提交之后。
比如:
当前分支(master):基于 commit A 做了修改,提交了 commit B。
其他分支(dev):基于 commit A 做了修改,提交了 commit C。
此时,想要将 dev 分支的修改通过快进式合并到 master 分支,可以如下操作:
git rebase master
命令,将dev分支的修改基准由commit A 变更到 master 的最新提交 commit C。git merge --ff-only dev
,只进行快进式合并 dev 分支到 master 分支。使用git rebase
需要遵守一个准则:**如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。**
git rebase
可以让我们的提交历史更加干净,但也会破坏提交历史。使用 git merge 还是 git rebase
好?其实是两种观点的体现。
这里不过多展开了。基于当前我工作公司的Git管理现状,采用 rebase 更加合适。所以git pulll -r --ff-only
是我常用的命令之一。
更多的关于 rebase 的比对,大家可以参考这篇文章:Git 分支 - 变基。
有时,我们不想直接合并某个分支,只需要某个分支上的某次修改,这时我们就可以使用 git cherry-pick
来很好完整这个工作。
git cherry-pick
:将指定的修改合并到当前分支。
我工作中,经常遇到一个bug 的修复需要合入多个分支,这时我会在一个分支修改,其他分支采用 cherry-pick
的方式完成。
git cherry pick
也很强大,可以通过 git cherry-pick --help
命令查看更多官方命令说明。
如果我们想回退到指定阶段的版本,那么我们可以使用 git reset
命令
git reset
:重置当前指针(HEAD)到指定状态。可以帮助我们快速的回到历史某个版本。
git reset
支持三种模式:
git reset --soft
:只移动 HEAD 指针,只影响 Git 目录,索引区和工作区不受影响。git reset --mixed
:移动 HEAD 指针,同时用 Git 目录更新索引区内容。Git 默认行为。git reset --hard
:移动指针,同时用 Git 目录更新索引区和工作目录。危险操作,会真正的销毁数据。工作中,我经常使用 git reset --mixed
或者git reset --soft
来修改提交记录。开发过程中,小步快跑,多次提交
,但是正式上库(推送到远程仓库)时,需要把之前的小步提交合并成一个提交,以保证git 记录更加简洁,这时就可以先 reset ,然后把所有修改文件重新提交为一次 commit。
工作中,有时发现开发的一个特性,方案有问题,或者实现不合理,本地修改完全不可用,且没有上库,这时我就会用 git reset --hard
删除本地所有修改。
更多有关 git reset
的信息,可以参考Git 工具 - 重置揭密。
工作中有时会遇到这种场景:我们刚刚合入到远程仓库的修改有问题,导致了一个 bug,需要紧急回退修改,保证原来功能正常。这时git revert
就派上用场了。
git revert
:回退指定 commit 修改,同时会生产一个回退 commit 记录。
有时工作目录有文件没有提交到版本库,这时也可以使用 git stash
来暂存工作目录的修改。等拉取远程分之后,再使用git stash pom
来恢复暂存的修改。
git stash
:暂存工作目录未提交的修改。
开发小步快跑时,正式上库前想将多个 commit 记录合并为一个 commit 记录,除了使用 git reset
外,还有一个办法是使用 git rebase -i
。如:git rebase -i HEAD~3
将最近的三次提交合并为一次 commit。
工作中,因为是使用 Windows 开发,所以基本我使用 TortoiseGit 工具 来完成这个 Git 操作。
更多的用法请使用 git rebase --help
查看。
本地 Git 项目可以关联 N 个远程或者本地其他 Git 项目,关联后,我们就可以将远程或者本地的其他 Git 项目修改 pick 或者 merge 到本地当前 Git 项目,也可以将本地 Git 项目修改推送或者 pick 到关联的 Git 项目。
工作中,通常我会将本地 Git 项目关联两个远程仓库,top 库(大家都合入,用于构建版本上网的仓库),origin(个人二级仓库,用于申请合并到顶级仓库)。添加远程仓库可以用以下命令:
git remote add
:如git remote add top [email protected]:TortoiseGit/TortoiseGit.git
或 git remote add origin [email protected]:Jock2018/TortoiseGit.git
。
前段时间,我需要关联一个本地的仓库做 merge 操作,可以使用类似如下命令:
git remote add local_remote /opt/mycode/TortoiseGit/.git
。
更多操作参考:git remote --help
如取消关联 local_remote:
git remote remove local_remote
工作中难免避免会有冲突,比如代码回合或者代码拉取时,这时就需要处理冲突。
冲突的处理需要经验积累,经过两年的工作,和几次代码回合的处理经验,我如今也能熟练的应对冲突。有以下心得:
git 配置分三个层级,本地–> 全局 --> 系统 ,三个层次中每层的配置都会覆盖上一层次的配置,遵循就近原则,所以 .git/config
中的值会覆盖/etc/gitconfig
中所对应的值。
这里我还是以工作中 Windows 下开发为例,介绍一些常用的配置:
git config --global user.name "Jock"
git config --global user.email [email protected]
core.autocrlf
git config --global core.autocrlf true
:假如你正在 Windows 上写程序,而你的同伴用的是其他系统(或相反),Git 可以在你提交时自动地把回车和换行转换成换行,而在检出代码时把换行转换成回车和换行。可以用 core.autocrlf
来打开此项功能。 如果是在 Windows 系统上,把它设置成 true
,这样在检出代码时,换行会被转换成回车和换行。git config --global core.autocrlf input
:core.autocrlf
设置成 input 来告诉 Git 在提交时把回车和换行转换成换行,检出时不转换。适用于产品运行在 Linux,但是开发在本地的场景。在我工作的公司,这个设置是必须的,否则会导致本地构建的版本无法成功安装部署。git config --global core.autocrlf false
:如果你是 Windows 程序员,且正在开发仅运行在 Windows 上的项目,可以设置 false
取消此功能,把回车保留在版本库中。一般这种场景比较少,现在程序很多都跑在 Linux 的生产环境中。pull 设置
git config --global pull.rebase true
:采用 rebase 方式合并远程方式,而不是采用 merge 方式。这样可以避免顶级库合并到本地的 commit 记录。
git config --global pull.ff only
:只采用快进式拉取合并远程库,否则不拉取。需要配合 git rebase
使用。
commit 设置:
git config --global commit.template C:\Users\tanjy\commit-message.txt
:指定提交信息模板。我一般使用 TortoiseGit 工具,它能方便记住历史提交信息,已经非常方便了。
以上配置完成后,全局 git 配置文件内容大致如下:
[user]
name = Jock
email = [email protected]
signingkey = ""
[core]
quotepath = false
autocrlf = input
[fetch]
prune = false
[pull]
rebase = true
ff = only
[commit]
template = C:\\Users\\jock\\commit-message.txt
更多的配置说明请参考官方文档,组合出适合你当前公司,最高效的配置就行。
自定义 Git - 配置 Git
git 配置官方说明
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为
.gitignore
的文件,列出要忽略的文件的模式。
文件 .gitignore
的格式规范如下:
#
开头的行都会被 Git 忽略。/
)开头防止递归。/
)结尾指定目录。!
)取反。GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore
文件列表, gitignore GitHub 地址。
工作中,一般一个代码仓库,在根目录配置一个 .gitignore
即可。
更多的请参考:忽略文件
以上就是个人作为后端研发经常使用的一些 Git 操作和配置,可以发现,这里主要都是针对客户端的,至于服务端的配置和使用,大家可以参考下面的学习指南进行系统学习。Git 还有很多高级操作,比如钩子的配置等,建议大家有时间,可以快速过一下Git Pro这本书,了解 Git 都能干哪些事,方便需要时,快速查找。
目标
基本概念
Git 基本概念
Git 安装
Git 常见配置
修改配置
查看配置
Git 基本操作(开发流程)
分支操作
推荐资料
目标
知识点
什么是 GitHub
如何访问 GitHub
基本概念
仓库
分支
READEME
STAR
Follow
账号类型(个人/组织)
必备操作
搜索仓库/代码
创建仓库
Fork 仓库
Watch 仓库
上传本地代码
修改个人信息
GitHub Flow 协作流程
了解 GitHub issues
共享代码流程
推荐资料
目标
知识点
高级操作
预防冲突
解决冲突
配置 SSH
子模块
Git 钩子
忽略提交(git ignore)
cherry-pick
分布式工作流程
Git worktree
Git Flow
Git 内部原理
Monorepo
了解其他版本控制系统,和 Git 对比
推荐资料
目标
开发
协作
项目管理
推荐资料
以上Git & GitHub 学习指南参照 程序员鱼皮的总结。
所有参考资料均在文中给出了相应链接。