Git基础教程
本文最后更新于:2023年7月14日 上午
git基础命令介绍
创建一个版本库
版本库也就是仓库,英文名repository,我们经常使用网页端git直接new一个版本库,这里我们介绍一下如何使用命令创建一个仓库。
- 首先创建一个文件夹作为仓库文件夹,当然这里也可以使用存有内容的文件夹并非必须是一个空的文件夹,只做样例展示。
- 使用
git init
初始化仓库
1 |
|
如图,此时一个空的版本库就做好了,这个时候我们也可以看到文件夹内自动创了.git
目录。
ok,我们现在来上传一个文件试试,readme.txt
存有新添加内容。
- 首先将文件存储到git暂存区,就是告诉git我要把这个文件添加到仓库。
git add
可以添加多个文件或目录,git add .
会将本目录下所有文件存储至暂存区。此时git已经记录了一条操作记录即创建readme.txt
文件,我们可以使用git status
查看文件操作记录。
1 |
|
- 文件进入暂存区后,操作已被记录,但是还没有合并到仓库上,这个时候使用
git commit
就将操作合并到仓库上,-m
表示添加注释,一般会写明本次提交都做了啥。
1 |
|
当然git commit
可以使用空的注释,这需要额外的参数:
1 |
|
从上述两个步骤中,我们可以大致看出git基本流程:git add
命令实际上就是一个保存记录的过程,git版本控制也是由git add
实现的,当我们保存了很多条操作记录时,就可以使用git commit
保存操作记录。
查看操作记录
现在我们对readme.txt
进行修改保存,再次查看git状态。
1 |
|
可以看到git已经检测到文件修改了,但是还没有实际进行保存。此时我们可以使用git diff readme.txt
查看修改了什么。
1 |
|
现在我们可以看到我们对文件进行的所有修改,我们可以使用git add
和git commit
进行提交。
个人的思考
从上述两个基本操作,我们可以大概的将git看作是一个历史记录保存器,我们对文件的每一个字符的修改都会保存为一个字符历史记录,但是这样保存的话,字符历史记录就会太多太复杂了,因此git让我们使用git add
显示的指定保存时间点(即历史记录分界线),git后台会将所有上次保存点至当前保存点的所有字符历史记录合并为某一行的操作,这就出现了+
和-
,这样字符历史记录就被合并为某个版本信息了,当然对同一个文件的多次git add
最终会被合并为一次修改,如果没有进行git commit
,某一次的git add
是无法恢复的;我们使用git commit
将保存的历史记录写入仓库,整个版本库操作就完成了。
撤销修改
当我们发现最近编写的内容出现问题时,我们想要删除工作区修改,如何进行操作呢?
在没有使用git时,我们常用的办法就是一行行手工判断是否是修改的,并重新改回去,现在我们有了git那就方便多了,git checkout -- <filename>
可以将指定的文件恢复至上一次保存的状态,无论是添加到暂存区的状态还是提交到仓库的状态都可以进行恢复,当然以最新的一次状态为准,总之,就是让这个文件回到最近一次git commit
或git add
时的状态。从此处我们可以看出无论是git add
还是git commit
最好确保代码完善后再进行add或commit操作,否则版本回退可能会无法直接运行。
那如果我们想要撤销暂存区里的内容呢?
那就应该使用git restore --staged <filename>
或git reset HEAD
把文件从暂存区移动到工作区,保留文件最后一次修改的内容。
版本回退
如果我们想进一步回退本地仓库的版本呢?我们首先对文件进行再一次的修改并提交至版本库
1 |
|
此时,如果我们想回退到之前的版本怎么办呢?git提供了git log
查看所有保存历史记录。
1 |
|
可以看到git清楚的记录了每一次commit修改的内容和注释,HEAD
表示我们当前处在的位置。
ok,坐好了,我们要准备回退了
1 |
|
以下三种方式回退的示例:
1 |
|
此时版本记录恢复到当前版本的上一版,回退版本修改记录存储在暂存区中,但是原文件内容不发生变化。
回到回退前的版本,我们使用git relog
查看所有回退命令,可以找到我们回退前的版本id。
1 |
|
1 |
|
此时版本记录恢复到当前版本的上一版,回退版本修改内容不会存储在暂存区中,原文件内容不发生变化。
重新回退至回退前的版本
1 |
|
此时版本记录恢复到当前版本的上一版,回退版本修改内容不会存储在暂存区中,原文件内容发生变化,针对新版本的所有修改都会丢失。
文件删除
当我们不需要某个文件时,我们会使用rm
进行删除,但实际上我们有两种选择,一是确实要从版本库中删除某一文件,那就是使用git rm
进行删除,并且git commit
。
1 |
|
我们可以看到rm
命令虽然在本地进行了删除,但是并未存入暂存区;git rm
删除本地文件并存入暂存区准备commit
,git rm
与rm
+git add
等价。
添加远程仓库
接下来,我们在github上创建一个远程仓库。
这样远程就有一个仓库了但是并未与本地仓库相互关联,我们可以使用git登陆自己的账号,使用``将本地仓库与远程仓库相互关联起来。
1 |
|
添加之后远程库的名字就是origin
,这是git的默认叫法,也可以改成别的。下一步,就可以把本地库的所有内容推送到远程库上。
1 |
|
把本地库的内容推送到远程,用git push
命令,实际上是把当前分支master
推送到远程。
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在开始,只要本地作了提交,就可以通过命令:
1 |
|
把本地master
分支的最新修改推送至GitHub。
删除远程仓库
如果添加的时候远程仓库写错了,或者想删除远程仓库,可以先使用git remote -v
查看远程仓库信息,再使用git remote rm origin
删除远程仓库,值得注意的是,这里的删除是将本地仓库与远程仓库的关联删除,并不是真正的将远程仓库直接删除,如果想要直接删除远程仓库,需要到github网页进行删除。
创建并合并分支
在git中,每次提交都会默认提交到master
分支中,master
就是主分支,而HEAD
就是指向的当前分支,只有当前分支是主分支时,HEAD
才指向master
。
个人思考:分支可以理解为更加高层面的历史记录分界线,区别于提交操作,分支具有多个平行提交继承于同一个提交的特性,个人看来这是分支具有的最大特色。
首先,我们先创建dev
分支,然后切换到dev
分支:
1 |
|
-b 表示创建并切换,等价于
1 |
|
使用git branch
可查看当前分支。
接下来,我们在dev
分支下添加一些提交,并合并到master
分支中。
1 |
|
默认情况下git会进行快进模式的合并,即直接把master
的指针指向dev
,合并就完成了,此时就可以放心的删除dev
分支了;
1 |
|
但是当master
和dev
分支出现冲突时,快进模式无法解决冲突的,比如master
和dev
有不同的提交记录,此时我们就需要手动解决冲突合并。
我们创建dev
分支,并提交一次修改记录;
1 |
|
之后我们切换回master
分支,再次提交一次不同修改内容的记录。
1 |
|
此时我们将分支合并
1 |
|
会发现合并分支报错,要求我们解决冲突问题,此时我们打开冲突文件,可以看到git使用<<<<<<<
、=======
、>>>>>>>
标识了两次提交记录。
此时我们可以选择保存某一个分支,或者手动输入保存的内容从而解决冲突。
使用git log --graph
能够查看分支合并图
1 |
|
现场保存
现在设想一下如下场景:
我正在编写一个新的功能,此时主分支程序出现了bug,需要紧急修复,而我的新功能还未完成,如果此时使用git add
和git commit
保存的话,分支上就会出现一些功能不完善的提交,这对整个分支来说可能会造成混乱,因此最好能够暂时保存当前工作现场,而不是提交不完善的代码,幸好,git提供给我们stash
功能,通过此功能我们可以将当前的工作现场储藏起来,等以后恢复现场继续工作。
保存工作区现场
1 |
|
此时我们已经成功保存工作区现场,使用git stash list
可以查看所有保存现场。
1 |
|
这是我们对主分支进行bug修复
1 |
|
然后回到我们的开发分支,恢复现场
1 |
|
之后需要使用git stash drop
进行删除保存现场,也可以使用git stash pop
恢复并删除现场,但是此时的dev
分支并未应用bugFix
的提交,我们可以使用git cherry-pick <commit>
将某一特定提交复制到当前分支,之后便会重新进行合并操作。
1 |
|
注意:现场储藏功能似乎仅支持储藏工作区文件,暂存区内容会被清空。
多人协作
查看远程库信息
1 |
|
推送分支
指定推送的分支
1 |
|
拉取分支
在本地创建和远程对应的分支
1 |
|
建立本地分支与远程分支的关联
1 |
|
标签管理
标签实际上就是某个commit提交记录,因为提交记录的id过长,所以采用标签方便记录。
git tag
默认是打在最新提交的commit上的,如果想要打在其他commit,只需要添加commitid即可
1 |
|
标签删除
1 |
|
提交标签至远程仓库
1 |
|
Git高级操作
Git处理换行符问题
当我们在Windows和Linux系统中同时进行git开发时,可能会出现大量文件修改但是,实际上没有内容更改的情况,原因就是Windows和Linux的换行符不统一问题。
Unix系统里,每行结尾只有“<换行>”,即”n”;Windows系统里面,每行结尾是“<换行><回车>”
解决方法
方法一:
在项目的 .gitattributes
文件中添加以下内容:
1 |
|
方法二:
1 |
|
强制全局转换换行符,签出时将换行符转换成CRLF,签入时转换回 LF。
创建新的空白分支
在使用git时,有时候我们可能需要创建一个新的分支,用于存放本项目的其他文件,这个时候我们就需要一个提交完全空白的分支,但是git只提供了在当前分支基础上创建新分支的命令,需要达到我们的目的需要一些手法操作。
使用
git checkout
的--orphan
参数创建新分支1
git checkout --orphan uHook
该命令会创建一个名为uHook的分支,并且该分支下有前一个分支下的所有文件,但是当前分支不会指向任何以前的提交。
我们不想提交任何内容,所以我们需要把当前内容全部删除,用git命令
1
git rm -rf .
使用
git commit
提交新分支1
git commit -am "new branch for uHook"
如果没有任何文件提交的话,分支是看不到的,可以创建一个新文件后再次提交则新创建的branch就会显示出来。
使用branch来查看分支是否创建成功。
1
git branch -a
Github Actions
Github Actions简介
Github Actions是持续集成和持续交付(CI/CD)的自动化平台。简而言之,Github Actions可以自动化编译、运行、测试提交的代码,同时也可以自动化的部署。
Github Actions基本概念
- workflow
workflow是一组预先配置完成的自动化进程,主要包括自动运行的jobs。Workflows默认存储在项目库根路径下的.github/workflows
目录中,每一个 workflow对应一个具体的.yml 文件(或者 .yaml)。
- Runner
Runner是工作流运服务器的代称。Github官方提供Ubuntu、Windows、macOS三种系统运行工作流。当然Github也提供自搭建服务器的方式运行工作流。
- jobs:
本工作流中的所有任务,一个job是工作流中运行在同一环境(Runner)下的一组步骤,同一job中的步骤是按顺序执行的,也就是说步骤之间可以存在依赖关系。
但是job之间默认不存在先后关系,job之间是并行运算的,因此运行环境也不同步。如果如要设置job之间的依赖关系的话,也可以使用jobs.<job_id>.needs
进行配置。
- Actions
Actions是Github Actions平台设置的一些常用基本操作,例如拉去和推送等操纵。Actions也是可以自定义和发布的。
YAML基本语法
由于Github Actions是使用YAML
配置的,因此创建Github Actions前先介绍一下YAML
基本语法。
YAML
的配置文件后缀为.yml
,如main.yml
基本语法
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tap,只允许1空格
- 缩进的空格数不重要,只要相同的层级的元素左对齐即可
#
表示注释- 以 - 开头的行表示构成一个数组
Github Actions的使用
在项目库根路径下的.github/workflows
目录中创建.yml
文件,该文件就是一个工作流的配置文件:
name
可选。Workflow的名字,默认为工作流的名字,在Github Actions页面显示该name。
1 |
|
run-name
可选。某一Workflow运行时显示的名称, 在某一Github Actions标签下,每次运行时会显示。
1 |
|
on
工作流触发时机,可以选择在代码库进行操作时触发,比如pull
、push
、pull_request
等等,详情请见官方文档,这里展示workflow_dispatch
的用法,workflow_dispatch
可以手动触发workflows
并进行相应的配置,可以使用inputs表明可选选项
1 |
|
jobs
jobs表示要执行的一项或多项任务,一个job就是一个任务,每个job之间默认是并行的,如果在job间存在前后顺序的话需要设置jobs.<job_id>.needs
。
1 |
|
jobs.<job_id>.runs-on
配置jobs运行虚拟机环境,可选择Ubuntu、Windows、macOS三种系统。
- ubuntu-latest,ubuntu-20.04
- windows-latest,windows-2019
- macos-latest,macos-11,macos-10.15
官方提供的配置感觉还不错,详情见官方文档
1
2
3
4
5
6
7jobs:
check-bats-version:
runs-on: ubuntu-latest
...
hello-world:
runs-on: windows-latest
..jobs.<job_id>.env
使用env可以给该任务或者步骤部署环境
1 |
|
jobs.<job_id>.if
控制job是否启用,
if
可以添加表达式以便自主控制是否执行本项任务,表达式可以使用Github上下文jobs.<job_id>.steps
某一job的所有步骤,同一job中的steps都是在同一环境下的执行的,每一个step是一条独立的命令或执行脚本,顺序执行具有依赖关系。
jobs.<job_id>.steps.uses:
使用一些官方或者第三方的actions来执行,例如最经常使用的actions/checkout@v2
,他们克隆我们repo的代码,之后工作流就可以使用repo的文件了。
1 |
|
Github Actions的功能实在过于丰富,目前仅仅便于理解,详细使用教程请看官方文档。
测试样例
1 |
|
参考链接
Github高级用法
Github引用外部仓库
例如在unraider
仓库中引用patchelf
可以使用以下命令,该命令会克隆外部仓库同时自动生成 .gitmodules
文件,git commit
即可获得引用外部仓库形式
1 |
|
注意:Git clone 的时候需要加上--recursive
,否则克隆下来的 TARGET_FOLDER 是空文件夹:
1 |
|
如果没加--recursive
,克隆后只需要初始化子模块即可:
1 |
|