开发 - Git基础
Git的名词解释和常用命令
准备秘钥
## 生成公钥
ssh-keygen
## 查看公钥
cd ~/.ssh/
cat id_rsa.pub
Git 名词解释
区域划分
git 内部分为:远程仓库(Remote)、本地仓库(Repository)、暂存区(Index)、工作区(workspace)。
下图描述了四者之间的关系:
工作区:文件目录。
暂存区:
放在 .git/index 目录下,当执行
git add <file>
命令时,数据就进入了暂存区。暂存区的意义有三个:
简化提交
将多次的文件改动合并成一次提交,避免了频繁 commit 操作
文件快照
在执行
git add <file>
时,实际上是在暂存区创建了工作区的文件快照,与工作区是互相隔离的,想要回退到某个快照时,可从暂存区中恢复文件到工作区。“休眠”改动
详见下述 Git 命令中的 stash 命令
本地仓库:当执行
git commit
命令时,暂存区所有的数据就都被提交到了本地仓库并清空。远程仓库:当执行
git push
命令时,数据就被提交到了远程仓库。
HEAD
打开 .git 文件夹内的 HEAD 文件可以看到类似ref: refs/heads/master
的字符串,说明 HEAD 指针指向的是分支,再由分支指向 commit 对象,HEAD 指针会随着分支指针的变化而变化。下图中,切换到了 maint 分支后,HEAD 也指向了 maint 分支的最新提交。
不过 HEAD 指针不一定指向分支,他也可以直接指向 commit 对象,当使用git checkout <commit>
命令时,HEAD 指针会处于detached HEAD
(游离)状态,如下图:
游离的 HEAD 常用于定位 bug,可以回到某个提交,做出修改后可以直接使用git switch -c <branch>
新建一个分支,这样一来,原分支没有被影响且不需要做回退的操作,又获得了一个处理好问题的新分支。
在 Git 命令中,HEAD 指针常用来做定位,例如 checkout 、reset 命令都可以使用HEAD~n
作为定位参数,n 从 0 开始,HEAD~ 表示当前 commit,HEAD~1 表示前一个 commit。
Branch
分支是指向 commit 对象的指针
Origin
远程仓库名字 origin
与分支名字 master
一样,在 Git 中并没有任何特别的含义。 master
是当你运行 git init
时默认的起始分支名字,原因仅仅是它的广泛使用;
origin
是当你运行 git clone
时默认的远程仓库名字。
Git 命令
Checkout
checkout 有两个功能:
撤销工作区的修改
使用
git checkout -- <file>
命令,可以将当前文件在工作区的修改进行撤销用于从历史 commit 提交中恢复(所有或单个)文件到工作区和暂存区
恢复所有文件:
git checkout <commit>
如下图,使用
git checkout master~3
将工作区和缓存区恢复到 commit b325c 时的状态。
恢复单个文件:
git checkout <commit> -- <file>
如下图,使用
git checkout HEAD~ -- readme.md
将 readme.md 文件从 commit da985 中恢复至工作区和暂存区。
Rebase
rebase(变基)的用途:改变分支中一个/多个 commit 的基点。
rebase 和 merge 都用于将一个分支合并入另一个分支,但是他们在执行的结果上是有区别的,假设现在有两个分支 feature 和 master,两个分支都有新的提交,如下图所示:
现要将 main 分支的修改合并到 feature 分支上,用 merge 和 rebase 两种方式去合并这两个分支会产生不同的结果。
git merge main
的执行结果:
创建了一个新的合并提交,优点是保留了完整的历史记录,缺点是经常合并分支会令提交历史变得杂乱。
git rebase main
的执行结果:
为 feature 分支的每一个提交都创建了全新的副本(副本与预案提交的 id 不同),并将其移动到 main 分支的顶端,在此过程中没有产生额外的提交,能够保持提交历史的简洁,但是 rebase 命令重写了项目的历史记录,丢失了合并提交的上下文,无法看到真实的更改是何时合并到目标分支上的,所以 rebase 更适合在没有和别人协同工作的情况下使用。
Pull
取回远程主机某个分支的更新,再与本地的指定分支合并
## 拉取并合并 branch2 分支
git pull origin branch2
## 如果当前所在的本地分支和远程分支存在映射关系,则可以直接使用简化的命令
git pull
git pull
是git fetch
和git merge
两个命令的缩写
git pull origin
## 实际上就相当于
git fetch origin branch2
git merge origin/branch2
Reset
reset 用于撤销操作
撤销文件修改:
git reset --
## 将所有文件移出暂存区
git reset
## 将 hello.md 文件移出暂存区
git reset -- hello.md
撤销提交:
git reset
## 撤销最后两个提交
git reset HEAD~2
## 撤销u7hfi2提交之后的所有提交
git reset u7hfi2
修改范围
reset 有三种参数,对应了对不同范围的修改:
--soft
只回退 commit,不会影响工作区域和暂存区。
--mixed
(默认方式)只改变暂存区,不改变工作区。
--hard
改变工作区和暂存区到指定 commit,所以如果在工作区有修改的情况下进行版本的回退操作,最好先备份,因为工作区会被覆盖。
Stash
将工作区内的改动进行暂存,切换到其他分支时,工作区的改动不会被携带过去,使用git stash pop
命令就可以将原先的工作区和暂存区改动恢复到工作区,如果要工作区和暂存区的改动各自恢复,需要加--index
参数。
## 将已跟踪的文件压入栈中,未跟踪的文件不会压入栈中
git stash
## 工作区和暂存区的改动都恢复到工作区
git stash pop
## 工作区和暂存区的改动各自恢复
git stash pop --index
## 查看所有的缓存列表
git stash list
## 重新获取某一次缓存,缓存不会消失,需要手动删除
git stash apply stash@{n}
## 删除某一次的缓存
git stash drop stash@{n}
Git 命令速查
创建分支
创建新的分支,可以选择继承的分支
git switch -c [start point]
## 新建一个名为 hello 的分支
git switch -c hello
## 新建一个继承于 dev 的名为 hello 的分支
git switch -c hello dev
## 新建一个名为 hello 的分支并建立与远程分支的映射关系
git switch -c hello origin/dev
切换分支
git switch
删除分支
删除本地分支
git branch -d
删除远程分支
git push origin -d
合并分支
将目标分支合并到当前分支上
git merge
发布分支
发布本地分支
git push origin
发布本地分支并与远程分支进行关联
git push -u origin
现在创建一个新的 dev 分支,使用git push orign <branch>
来推送和创建远程分支,可以看到通过git branch --vva
命令打印出来的分支信息中,dev 分支和 origin/dev 分支是没有进行关联的:
现在换用git push origin -u <branch>
进行发布,可以看到 dev 已经与 origin/dev 正确关联。
撤销修改
现在假设有个文件叫做 readme.md,因为有暂存区的存在,所以对于修改的撤销可以分为三种情况:
工作区有修改,没有提交到暂存区:
## 这里的双横杠一定要加,不然会被视作分支 git checkout -- readme.md
工作区的修改提交到了暂存区:
这里需要分为两步:首先将暂存区的修改撤销,然后将工作区的修改撤销
## 从暂存区撤销 git reset readme.md ## 从工作区撤销 git checkout -- readme.md
工作区的修改提交到了暂存区之后,又进行了修改:
这种情况需要在第二部的步骤前再加一步 checkout 操作
## 从工作区撤销 git checkout -- readme.md ## 从暂存区撤销 git reset readme.md ## 从工作区撤销 git checkout -- readme.md
版本回退
保留当前工作区与缓存区的回退:
git reset --soft
清空工作区与缓存区的回退:
git reset --hard
除了可以使用 commit ID,还可以HEAD~n
的形式来快速回退版本:
git reset HEAD~
git reset master~2
版本回退后如果需要将当前回退版本更新到远程仓库,则需要下述命令强制更新线上版本:
git push --force
版本前进
现在有个版本 A、B,将版本回退到 A,此时如果再想回到版本 B,还是通过git reset
去实现,但是此时必须获取到 commit ID,然而现在git log
已经打印不出 A 版本之后的 commit ID,所以需要使用git reflog
命令来打印出每一次命令的 commit ID,然后使用 ID 进行回退。
下图是将版本回退到 A:
下图是将版本前进到 B:
拷贝提交
将指定提交应用于其他分支
git cherry-pick
撤销提交
对指定提交内的修改进行回退,与 reset 不同的是,它不是直接回退提交,而是会新建一个 commit,在这个新的 commit 内去对指定版本的修改进行反向修改。
git revert
下图展示了 revert 与 reset 的区别
提交标签
为了方便查找具体的迭代,可以给某个提交打上标签(tag),之后再分支列表处能看到 tag 列表。
git tag
## 打完标签之后需要手动 push 到远程服务器上:
git push origin
## 一次推送多个 tag
git push origin --tags
合并提交
将多个提交合并成一次提交,保持提交历史的清爽
git merge B --squash
绕过提交校验
项目如果配置了 pre-commit-check 且本次提交想绕过提交前的检查,可以使用 --no-verify
来实现
git commit -m "feat: 绕过校验" --no-verify