Git基础教程
为什么要学习Git
在软件工程中,需要对整个团队的代码和文档(项目成果)进行统一的管理,这就需要软件配置管理了。软件配置管理(SCM)是指通过执行版本控制、变更控制的规程,以及使用合适的配置管理软件,来保证所有配置项的完整性和可跟踪性。配置管理是对工作成果的一种有效保护。而Git就是一种软件配置管理软件。
相比于别的软件配置管理软件(如VSS、CVS、Subversion等),Git作为开源的分布式版本控制系统,可以更加有效、高速地处理从很小到很大的项目版本控制。所以,我们需要学习Git。
一些基础概念
版本控制
版本:说到版本,大家都不太陌生。我们常说的有软件版本和文件版本。对于软件版本,我们常用的软件都有其版本,比如JDK有1.8、11、17等版本,Mysql有5.7、8.0等版本;而文件版本则是用于保存重要的历史记录,且可以用来恢复数据而产生的,比如你对某个文件建立了一个副本,然后去修改原文件,这种情况下,两个文件的文件版本就是不同的。
由此可见,对文件版本的控制是重要的,而手动进行每一个文件的版本控制是费时费力的,所以我们需要版本控制软件来帮助我们自动对版本进行控制。
版本控制软件的基础功能有:
- 保存和管理软件
- 提供客户端工具进行访问
- 提供不同版本文件的比对功能
集中式版本控制
要对版本进行控制,最容易想到的办法就是有一个集中管理软件版本的中央服务器,然后每个开发者都从这个服务器获取最新代码并对其进行更新。
这么做虽然可以实现多人协作开发中对版本的控制,但是存在着一些问题:
- 文件冲突问题:如果几个开发者都先下载了同一版本的文件,并分别对其进行修改,那么最终,服务器最新版本的文件只能是最后一名上传者所修改的文件,而之前的上传者上传的文件都会被覆盖掉。对于不同的集中式版本控制软件,解决方法不完全相同,比如对文件进行加锁等,但这样显然会降低开发效率。不过可以使用版本控制软件的不同版本文件比对功能,来比对修改的内容,并对每个开发者的修改进行合并,但这难免遇到冲突问题。
- 单点故障问题:如果中央服务器寄了,或者网络不通了,这样对文件的下载和上传就无法进行!开发流程就会被卡住,这很影响开发效率,并且此时还有文件丢失无法找回的风险!
为了解决单点冲突问题,可以使用分布式版本控制。
分布式版本控制
如果每个开发者在本地都将中央服务器的资源下载了一份,这样,当中央服务器寄了,或者网络不通了,开发者照样可以通过本地的资源进行文件的下载和上传。当中央服务器和网络恢复后,再将对本地资源库的修改上传到中央服务器即可。
安装Git
想要安装Git,可以访问Git官方网站:Git - 安装 Git (git-scm.com)
这个网站中也对Git有充分的介绍,可以作为本教程的进阶使用。
同时,我们也可以安装Github Desktop:GitHub Desktop | Simple collaboration from your desktop
关于Github Desktop的使用,在这里不多赘述,作为一个Git的图形化界面,Github Desktop的使用是较为简单的,很快就能上手。
Git基础操作
我们可以用Git来创建仓库。当我们创建好一个仓库,会发现指定的文件夹(工作区)中有一个.git文件夹(可能会被隐藏),这里的.git文件夹就相当于当前的本地仓库,而我们在工作区中进行文件操作,会通过与.git文件夹进行比对,从而实现不同版本文件的比对功能。
假如我们在工作区文件夹中创建一个a.txt文件,我们的本地仓库其实是没有这个文件的,想要让本地仓库有这个文件,就需要提交(commit)来把文件上传到本地仓库。
本地仓库中会记录不同版本的文件,每个版本的文件都会有对应的版本号,当我们commit之后,我们看到的文件就是本地仓库中最新版本的文件。要区分不同文件版本,就需要版本号,版本号是由40个16进制的数字(0到f)组成的,因为产生于提交时,所以又叫提交码。版本号不仅可以指明文件的版本,还可以用来查询提交信息、定位文件在仓库中的位置。
假设现在有多个开发者来共同开发一个软件,但是他们开发的是不同的模块。由于开发过程中需要多次提交,而每个人的提交顺序又是不固定的,所以这时我们的提交就没有什么规律可言,当我们想要定位到某一次提交的具体内容,就很难找到了,另外,多个开发者可能会修改同一份文件,这就会导致文件的冲突,从而带来风险。同时,频繁的提交还会导致仓库越来越大,这样就会降低不同版本文件比对的效率。
要解决以上的三个问题,我们可以借助Git的分支(branch)功能。每个分支都是当前版本库的副本,每个开发者都在这个副本上进行操作,最后再把不同的副本进行合并(merge)即可。当我们在user分支创建、修改、删除文件并commit后,order分支的仓库是看不到的,想要让order的库中也有操作过的文件,就需要进行合并。
当我们对同一份文件进行合并,其内容可能是有冲突的,这种时候,Git会给出区别,并让你自己选择最终内容从而解决冲突,然后就可以继续合并了。
可以通过.git文件夹中的HEAD,查看保存当前最新版本号的文件的位置,同时该文件名即当前的分支名。
在提交时,需要开发者给出提交的描述信息,而在合并时,会Git自动给出描述信息,如果我们想自己添加描述信息,则可以使用标签(tag)。
上边的操作是针对本地仓库的,而在实际开发中,我们会使用远程仓库,毕竟Git是一个分布式版本控制软件,是需要有一个中央服务器来保存资源的。可是我们自己搭建远程仓库,成本还是较大的,所以我们可以选择第三方的代码托管平台,比如Github、Gitee等,它们可以为我们提供远程仓库服务。当我们创建好远程仓库之后,我们可以使用clone操作来获取远程仓库的内容。当我们commit后,我们修改后的文件就会到达本地仓库,但还没有到达远程仓库,想要上传到远程仓库需要进行push操作,来把本地库的内容推到远程库。
README文件可以作为仓库资源信息的介绍,因此在我们对项目进行修改的过程中,README文件也要进行对应的修改。
可以使用**.gitignore文件**来让Git忽略掉指定文件的修改。
Git常用指令
Git有很多常用的指令,下图是一张对Git常用指令的概括图。
Git配置
查看Git版本
1 |
|
配置Git的用户名称和邮箱
1 |
|
我们还可以通过修改.git文件夹里的config文件来完成修改配置。并且可以在命令中添加 --global
来进行全局操作,然后会发现我们的用户目录下会有一个.gitconfig文件,可以通过修改这个文件来完成全局配置。在Github Desktop中,也可以在图形化界面设置当前操作的用户名称和用户邮箱。
仓库指令
初始化本地仓库
1 |
|
执行完这个指令后,文件夹中就会有.git文件夹。
用Github Desktop和用命令直接创建的仓库有一些区别,比如用Github Desktop创建的仓库中,初始会有.gitattributes文件。并且用命令行创建仓库在初始化完成而没有其他操作的时候,HEAD文件会表明当前在master分支,且我们去HEAD文件中给出的路径去寻找版本号,会发现没有此文件,这是因为我们还没有进行过提交。而用Github Desktop创建的仓库中,一开始便会有一次提交(提交了.gitattributes文件),所以一开始就有版本号,并且我们可以自己在客户端中选择分支名(master、main或者其他)。
克隆远程仓库
1 |
|
当我们克隆远程仓库之后,我们本地会有远程仓库的所有信息,包括之前的提交记录等。
文件操作
添加文件到暂存区
1 |
|
-u
或--update
:仅添加已跟踪(已纳入版本控制)的文件的修改(不包含新增文件)-A
或--all
:添加所有修改(包括未跟踪的新文件)-i
或--interactive
:进入交互式暂存模式(细分修改块)
从版本控制中移除文件
1 |
|
如果文件已被修改但未提交,直接使用 git rm
会失败(需先提交或强制删除)。若文件已手动删除,直接运行 git add <file>
或 git rm <file>
效果相同。
提交到本地仓库
1 |
|
-a
或--all
:自动暂存所有已跟踪文件的修改(跳过git add
)。--amend
:修正上一次提交(修改提交信息或追加文件)。--no-edit
:与--amend
配合使用,保留原提交信息。
恢复误删除的文件
1 |
|
当我们不小心删除了某个文件,我们可以从最新提交中恢复它。但如果最新提交中没有这个文件,我们就无法恢复它。但是我们过去提交过这个文件,所以我们可以通过reset来把版本库重置到有这个文件的那一次提交。
1 |
|
--soft
:HEAD指针回退到目标提交,并且暂存区和工作区保留修改,修改处于已暂存状态。--mixed
:HEAD指针回退到目标提交,暂存区被重置,工作区保留修改,也就是说需要重新add,这也是默认的reset模式。--hard
:HEAD指针回退到目标提交,但是暂存区和工作区完全重置,会丢弃所有修改,需要慎用!
如果我们不想失去之前的提交记录,那么就不应该用reset,而是用revert。
1 |
|
这么做会回退到目标版本号之前的一个版本,并且往提交历史中新增一条revert的记录,而不是直接丢掉之前的记录。
查看状态
1 |
|
- Changes to be committed:已暂存但未提交的文件(绿色)。
- Changes not staged for commit:已修改但未暂存的文件(红色)。
- Untracked files:未被 Git 跟踪的新文件(红色)。
查看文件差异
场景 | 命令 | 说明 |
---|---|---|
工作区 vs 暂存区 | git diff |
默认行为,查看未暂存的修改(工作区与暂存区的差异) |
暂存区 vs 最新提交 | git diff --staged (或 --cached ) |
查看已暂存但未提交的修改 |
比较两次提交之间的差异 | git diff <commit1> <commit2> |
对比两个提交的差异 |
比较不同分支的差异 | git diff <branch1>..<branch2> |
显示两个分支最新提交的差异 |
查看某次提交的修改内容 | git diff <commit>^! |
^! 表示对比该提交与其父提交的差异 |
选项 | 说明 |
---|---|
--color-words |
高亮显示单词级差异(而非行级) |
--stat |
仅显示修改统计(文件列表和增删行数) |
-w |
忽略空格变化(适合格式化调整的场景) |
-- <file> |
仅比较指定文件(如 git diff HEAD -- README.md ) |
分支操作
创建分支
1 |
|
你可以通过这个命令来创建一个分支,但是在创建分支前,需要提交一次,也就是说,你不可以刚init完,什么都没提交,就创建分支(分支是基于提交的)。
查看分支
1 |
|
-a
:查看包括远程分支在内的所有分支-v
:查看各个分支的最后一次提交信息
切换分支
1 |
|
也可以使用switch,Git 2.23+的版本可用:
1 |
|
删除分支
1 |
|
重命名分支
1 |
|
合并分支
1 |
|
当合并分支发生冲突时,分支会进入MERGING,我们需要手动解决冲突,然后进行提交,这样就可以完成merge。
标签操作
直接操作版本号会有一些不方便,为了解决这个问题,我们可以使用标签tag
查看标签
1 |
|
设置标签
1 |
|
用Github Desktop也可以设置标签
日志操作
场景 | 命令 | 说明 |
---|---|---|
查看完整提交日志 | git log |
默认按时间倒序显示所有提交 |
简洁单行显示 | git log --oneline |
每个提交显示为一行 |
图形化分支日志 | git log --graph --oneline |
用 ASCII 图形展示分支合并日志 |
按时间过滤提交 | git log --since="yyyy-mm-dd" --until="yyyy-mm-dd" |
显示指定时间范围内的提交 |
按作者过滤提交 | git log --author="作者名" |
显示特定作者的提交 |
按关键词搜索提交信息 | git log --grep="关键词" |
查找提交信息中包含关键词的提交 |
查看某文件的修改日志 | git log -- <file> |
显示影响指定文件的所有提交 |
用自定义输出格式查看 | git log --pretty=format:"格式" |
按照输入的格式查看提交日志 |
查看某一版本的提交历史 | git log <版本号> |
查看指定版本号及其之前的提交 |
远程仓库指令
当我们查看config文件,可以看到remote的信息,remote右边的就是远程仓库的名称,它可以在操作中代替下方的url。
添加远程仓库
1 |
|
删除远程仓库
1 |
|
重命名远程仓库
1 |
|
把本地仓库的内容推送到远程仓库
1 |
|
从远程仓库拉取最新版本
1 |
|
查看Git对象信息
1 |
|
选项 | 说明 |
---|---|
-t |
查看对象的类型(如 blob 、tree 、commit 、tag ) |
-s |
查看对象的大小(字节数) |
-p |
以友好格式打印对象内容(最常用) |
-e |
仅检查对象是否存在(静默模式,无输出) |
可以通过此命令来查看Git对象信息,比如提交、树、标签、文件内容等。