Better

Ethan的博客,欢迎访问交流

你可能不知道的 Git 操作

介绍一些相对进阶的操作和命令的细节。

checkout 身兼多职

checkout 身兼多职

  • 分支相关操作
  • 恢复文件到之前的某个状态

使用 -- 来告诉 Git 你是想恢复文件而不是切换分支

恢复文件肯定有 source(从哪里恢复)和 target(恢复到哪里去),checkout 的 target 就是指你的工作目录,source 可从暂存目录往工作目录里恢复文件,也可以从仓库里的各个 commit 记录里往工作目录恢复文件。比如从某个 commit 中恢复

git checkout 9fc9896 test.txt

可以使用各种快捷方式来引用各个 commit,比如 HEAD,分支名称代表所在分支的最新 commit

总结 checkout 常用操作

  • 分支相关操作:git checkout 分支名/commit hash 切换到相应的分支或 commit,加上 -b 参数则会创建分支并切换过去
  • 恢复文件相关操作:git checkout [分支名/commit hash/HEAD快捷方式] -- 文件名 恢复指定分支的最新commit或指定 commit 或快捷方式指向的 commit 的文件到工作目录,若省略中间的参数,则
    • 暂存区有内容且暂存区内容与工作目录不同,则恢复暂存区的状态到工作目录
    • 暂存区无内容,则恢复 HEAD(最新的 commit)的状态到工作目录

真的了解 diff 吗

diff 命令用于比较任意两个状态的差别,语法为:git diff source target,比较的结果其实是以 target 为基准的,也就是说 target 相比于 source 有了哪些变化

如果只给出一个参数,则这个参数为 source,target 默认为当前工作目录。

如果两个参数都省略,那么默认 source 为暂存目录,默认 target 为工作目录。

如果我想比较暂存目录和各个 commit 怎么整呢,使用如下命令

git diff --cached 分支名/commit hash/HEAD快捷方式

reset

版本控制最大的好处就是可以方便的找到以前的版本并恢复。

恢复也有 source 和 target 的概念,这里的 source 肯定就是各个 commit(包含分支名和快捷方式),而 target 根据不同的参数可能是暂存目录或工作目录或两者同时都是 target。默认的 source 就是当前所在分支的最新 commit。

git reset 支持的参数

  • --mixed:默认参数,把HEAD、暂存区修改为你指定的 commit 的时候的文件状态,工作区不变(commit 之间差异 + 暂存区新增内容 + 工作区新增内容保存在工作区)
  • --soft:只是把 HEAD 指向的 commit 恢复到你指定的 commit,暂存区和工作区不变(commit 之间差异作为提交内容保存在暂存区,暂存区有新增内容则保持原样,工作区有新增内容同样保持原样)
  • --hard:把HEAD、暂存区和工作区修改为你指定的 commit 的时候的文件状态

cherry-pick

cherry-pick 其实在工作中还挺常用的,一种常见的场景就是,比如我在 A 分支做了几次 commit 以后,发现其实我并不应该在 A 分支上工作,应该在 B 分支上工作,这时就需要将这些 commit 从 A 分支复制到 B 分支去了

# 目前在 B 分支,需要从 A 分支复制
git cherry-pick commitId1 commitId2 ……
# 此时 B 分支具备指定 commit 的内容,但此时 A 分支还是有对应 commit 的内容,如果需要删除,则切换分支 A,使用 reset 即可

merge & rebase

merge 语法:git merge 目标分支

理解 Fast-forward:是合并的一种类型,当当前分支(master)是目标分支(branch)的祖先 commit 时会发生这种“Fast-forward”合并,其实可以理解为 HEAD 指针指向的快速移动。不会产生新的 commit 记录。

如果当前分支不是目标分支的祖先 commit 节点,则其实是做的三方合并,除了这两个分支的最新 commit 以外,另外一个是这两个分支的共同祖先 commit 点。这种情况下如果没有冲突的话会自动生成一个 merge 的 commit,如果有冲突则手动解决后还是会有一个 merge 的 commit。

首先需要明确的是 git rebase 有很多牛逼的功能,其“交互模式”可以让你干很多事,通过 -i 实现,比如

  • 调整 commit 顺序
  • 合并 commit
  • 删除 commit
  • 修改 message

Git Merge 和 Git Rebase 目的相同,它们都是把不同分支的提交合并到一起。虽然最终目的是一致的,但是其过程却颇为不同。

很多博客会将 rebase 和 merge 一起讨论,主要原因是 rebase 可以实现 merge 的功能,同时 history 看上去更好看(rebase 不会产生多余的 commit,并且保持直线),比如在 master 分支上执行 git rebase branch2 流程如下

  1. 先找到两个分支的共同祖先 commit 节点
  2. 然后把 master 分支上这个节点的儿子节点全部“应用”到 branch2 分支上

一般我们把别的分支合并到 master 时用 merge,而把 master 合并到别的分支时会用到 rebase

HEAD^ vs HEAD~

HEAD 指向当前分支的最近一个 commit,当我们需要通过 checkout 或 reset 找到之前某个 commit 的状态时,通常需要找到对应的 commit id,但 id 通常是 hash 值,不便于记忆,那没有快捷的方式进行索引呢,答案就是 HEAD 配合 ^~ 使用了。

首先要理解什么叫做父提交,通常一个 commit 至少有一个父提交,也就是你的上一个 commit,你需要知道的是,在 merge 的情况下,你会有多个父提交,比如 git merge br1 br2 br3,注意这里的顺序很重要,此时你会有三个父提交,依次是 br1、b2、br3 分支上最新的 commit。

理解了父提交的概念,再来看两者的含义

  • ^ 代表父提交,当一个提交有多个父提交时,可以在后面跟上一个数字,表示第几个父提交,^ 相当于 ^1
  • ~<n> 相当于连续的 n 个 ^

credentials

默认 git 进行远程访问时是需要输入用户名和密码的,还要记住密码,需要用到凭证系统,git 支持 4 中凭证模式

  • cache 模式会将凭证存放在内存中一段时间。 密码永远不会被存储在磁盘中,并且在15分钟后从内存中清除。
  • store 模式会将凭证用明文的形式存放在磁盘中,并且永不过期。 这意味着除非你修改了你在 Git 服务器上的密码,否则你永远不需要再次输入你的凭证信息。 这种方式的缺点是你的密码是用明文的方式存放在你的 home 目录下。
  • 如果你使用的是 Mac,Git 还有一种 “osxkeychain” 模式,它会将凭证缓存到你系统用户的钥匙串中。 这种方式将凭证存放在磁盘中,并且永不过期,但是是被加密的,这种加密方式与存放 HTTPS 凭证以及 Safari 的自动填写是相同的。
  • 如果你使用的是 Windows,你可以安装一个叫做 “Git Credential Manager for Windows” 的辅助工具。 这和上面说的 “osxkeychain” 十分类似,但是是使用 Windows Credential Store 来控制敏感信息。

可以通过如下命令查看当前存储模式

git config --list | grep credential

通过如下命令启用存储功能

$ git config --global credential.helper store

设置成功后,凭证会自动保存到默认文件 ~/.git-credentials 中。

参考



留言