开发 - Git基础

Git的名词解释和常用命令

准备秘钥

## 生成公钥
ssh-keygen

## 查看公钥
cd ~/.ssh/
cat id_rsa.pub

Git 名词解释

区域划分

git 内部分为:远程仓库(Remote)、本地仓库(Repository)、暂存区(Index)、工作区(workspace)。

下图描述了四者之间的关系:

  • 工作区:文件目录。

  • 暂存区:

    放在 .git/index 目录下,当执行git add <file>命令时,数据就进入了暂存区。

    暂存区的意义有三个:

    1. 简化提交

      将多次的文件改动合并成一次提交,避免了频繁 commit 操作

    2. 文件快照

      在执行git add <file>时,实际上是在暂存区创建了工作区的文件快照,与工作区是互相隔离的,想要回退到某个快照时,可从暂存区中恢复文件到工作区。

    3. “休眠”改动

      详见下述 Git 命令中的 stash 命令

  • 本地仓库:当执行git commit命令时,暂存区所有的数据就都被提交到了本地仓库并清空。

  • 远程仓库:当执行git push命令时,数据就被提交到了远程仓库。

打开 .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 有两个功能:

  1. 撤销工作区的修改

    使用git checkout -- <file>命令,可以将当前文件在工作区的修改进行撤销

  2. 用于从历史 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 pullgit fetchgit 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,因为有暂存区的存在,所以对于修改的撤销可以分为三种情况:

  1. 工作区有修改,没有提交到暂存区:

    ## 这里的双横杠一定要加,不然会被视作分支
    git checkout -- readme.md
    
  2. 工作区的修改提交到了暂存区:

    这里需要分为两步:首先将暂存区的修改撤销,然后将工作区的修改撤销

    ## 从暂存区撤销
    git reset readme.md
    ## 从工作区撤销
    git checkout -- readme.md
    
  3. 工作区的修改提交到了暂存区之后,又进行了修改:

    这种情况需要在第二部的步骤前再加一步 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

一图总结

作者

BiteByte

发布于

2020-12-10

更新于

2024-01-11

许可协议