logo科技微讯

Git 学习笔记

作者:科技微讯
日期:2023-05-27
📝 笔记

一些概念:

  • commit:提交
  • repository,repo:仓库
  • working directory:工作区
  • checkout:检出
  • staging area、staging index、index:暂存区、索引区
  • working area、working tree:工作区
  • SHA:每个 commit 的编码
  • branch:分支

首先要知道查询文档的方法:在 bash 输入 git help [keyword],例如 git help add,就可以看到这个命令的使用说明。另外,查询配置信息需要这样:git config --list --show-origin

推荐两篇学习文章:

以下是我的学习笔记。

HEAD、branch、tag 都是 reference,HEAD 指向 branch 或 tag 或 sha,如果指向 tag 和 sha,那就是 detached head,.git/HEAD 这个文件记录了当前的 head 是哪个。从 .git/refs/heads/ 这个文件夹来看,每个 branch 都有一个 HEAD,你 checkout 到哪个 branch,那这个 branch 的 HEAD 就生效。如果是 detached head,那所有 branch 的 head 都不生效。

这篇文章对理解 HEAD、branch、tag 很有帮助:Git Internals

可以通过 echo [SHA] > .git/HEAD 修改 HEAD 指向哪里,效果和 git reset 类似,所以 git reset 主要改变 HEAD 这个文件。

repo 里面的任一文件有 4 种状态:untracked、unmodified、modified、staged。除了 git add,所有 git 命令对 untracked 文件无效。

git 就是对比,在 git 的世界中,万物皆对比。用对比的思维理解 git,而不要用文件的流动性(变化性)去理解。例如 working dir 和 staged area 对比,如果有差异,git status 就会用红色表示出来,git diff 看的是红色的细节。staged area 和 commit area 对比,如果有差异,git status 就会用绿色表示,绿色表示 staged area 和 working dir  一样,但和 commit area 不一样,git diff --staged 看的是绿色的细节。

git reset 也是对比的过程,默认的 --mixed 重置 commit area 和 staged area,这时候 git status,git 会把 working dir 和 staged 进行对比,差异显示为红色。--soft 只重置 commit area,所以 staged area 和 commit area 有差异,用绿色表示。--hard 会同时恢复 3 个 area。详情可看 git reset -h

搭配 github 中使用 git:

  • git remote:查看本地 repo 在 github 的所有远程 repo,即这个本地 repo 被上传到哪些 github repo;
  • git remote show origin:可以看到 origin 这个 repo 的详细信息;
  • git remote rename [旧名称] [新名称]:重命名,例如本来是 origin 的,改为 newOrigin;
  • git remote add [名称] [URL]:创建远程 repo 的方法,通常叫 origin,url 就是远程 repo 的 url;
  • git remote -v:查看远程 repo 的 url 是什么,包括 fetch 也就是下载的 url,push 也就是上传的 url,-v--verbose 的缩写;
  • git push origin main:把本地的 main 分支,上传至远程的 origin,这里用 origin 指代 url,origin 可省略,因为这是默认值;
  • git push origin [tagName]:默认情况下,git push 不会把 tag 上传到 github,需要主动 push,如果要 push 所有 tag ,可以用 git push origin --tagsorigin 可省略,因为这是默认值;
  • git pull origin main:从远程 origin 这里,把 main 分支最新的内容下载到本地,更新本地的分支,origin 可省略,因为这是默认值;
  • github 的 fork:相当于在 github clone 别人的 repo,如果 fork 之后从自己的账户 clone 到本地,那这个 remote 就是自己的 remote,而不是原始 remote,而且你通常对原始的 remote 无写入权;
  • upstream/main:如果你 fork 了一个项目,然后从你的 repo 把这个项目 clone 到本地,那这个本地项目的 remote origin 就是你自己的 repo,如果你想从原始 repo 更新你的本地项目,需要手动 git remote add [名称] [url],其中名称习惯叫 upstream 而不是 origin
  • 注意 main、origin/main 两个分支的区别,当有 remote 参与时,本地 commit 可能会出现 2 个 branch,main、origin/main 不一致时就会出现,他们的关系可能有四种:ahead、behind、up-to-date、out of sync;
  • git rebase:变基,比较少用;
  • github 的 pull request:相当于远程 merge,以 branch 的方式进行 request,所以最好的做法是,这个 branch 的名称可以反映这个 request 的 topic。当然,在提交 request 的时候,你可以起标题,说明等。本地 merge,如果是快进合并,不会产生新的 commit,但是在 github 接受 pull request 产生的 merge,肯定会产生新的 commit;

开源项目大概是这样进行的,以 deno 为例:ry 发布 deno,其他人可以 fork 它的项目,fork 了之后再 clone 到自己的电脑,发现 bug 或者加入新功能等,需要更改代码,那就建一个新 branch,改代码再通过这个 branch push 到自己的 repo,然后在自己的 repo 那里发起 pull request。可以向原作者 pull request,在创建 pull request 时,github 会提醒你的代码是否和最新的原作代码有冲突,如果有,你可以 add 原始的 remote,也就是 upstream/main,把代码 fetch 下来 diff,根据需要把 main、upstream/main merge 起来,再 push 更新你的 main、origin/main,然后重新提交 pull request

branch 在实际应用中往往可以分为两类:long running branch 、topic branch。pull request 通常是 topic branch。

一些 git 命令:

  • git init:初始化 git 仓库,自动生成 .git 文件夹;
  • git clone:下载仓库;
  • git status:状态;
  • git log:默认显示 SHA、作者、日期、消息,只能看到 branch 的 commit,不在 branch 的 commit 看不到,例如 detached head;
  • git log --oneline:只显示 sha;
  • git log -2、git log --since=2,weeks:显示哪些 log;
  • git log --stat:显示每条 commit 那些文件发生了改变;
  • git log [branch 名称]:查看某个 branch 的 log;
  • git log -p:显示这些改变都是什么,也可以写作 --patch;
  • git log [SHA]:可以从该 SHA 开始,倒翻去看;
  • git log --follow [文件名称]:看这个文件的历史变化,包括重命名;
  • git show:显示最新 commit 的详细变化。它和 git diff 的重要区别是,git show 只会显示该 commit 相对其父 commit 的变化,例如两个分支 merge 之后,有点难区分某个 commit 的父 commit 是什么,这时用 git show [SHA] 就可以默认比较该 commit 相对其父 commit 的变化;
  • git show [SHA]:显示该 SHA 的详细变化,可以和 -p--stat-w(空格) 等一起使用;
  • git add:把变化从工作目录转移到暂存区,后面可以跟具体文件名称,也可以加 .
  • git add --patch:如果一个文件有多个改变,你希望把这些改变分拆开多个 commit 提交,可以用这个命令;
  • git rm --cached [文件]:可以把文件移出 staging area,可接受多个文件,也就是变成 untracked,文件继续保留在硬盘;
  • git rm [文件]:把文件从 working dir 删除,同时从 staging area 删除,也就是从硬盘上删除,rm 不会把文件从 staging area 中删除;
  • git mv [名称 1] [名称 2]:在 repo 中对被 tracked 的文件用 mv 重命名要小心,因为 git 会把重命名理解为 3 个步骤,删了原来那个,新建文件,这个文件没有被 tracked。所以可以用 git rm 这个命令重命名,一次性完成三个步骤,相关笔记:Git 如何重命名文件和文件夹
  • git diff:和 git log -p 一样。后面可以加两个 SHA,这样就是比较两个 commit 的变化,如果不加任何 SHA,就是比较 staging area 和 working dir 两者之间的区别。如果要比较 staging area 和 commit 之间的变化,需要 --staged
  • git diff --staged:参考上一条;
  • git commit:提交;
  • git commit -a:跳过 add 到 staged,直接从 working dir 到 commit;
  • git commit -m:提交说明不要超过 60 个字符;
  • git tag -a v1.0:表示创建一个带注释的 tag,如果没有 -a 就不带注释,git show v1.0 可以查看这个 tag 的内容。tag 不会移动。不带注释的 tag 类似 branch,带注释的 tag 类似 commit,因为带注释的 tag 会生成一个新的 sha 值,只不过 log 不会显示这个 sha 值。可以在 .git/refs/tags/ 查看;
  • git tag:列出所有标签
  • git tag -d v1.0:删除标签
  • git tag -a v1.0 [SHA]:如果后面加 SHA,表示给指定 commit 加标签,如果没加 SHA 就表示给最新的 commit 加标签;
  • git branch:显示所有分支,--merge 可以显示那些已经 merge 的分支  --no-merge 反之;
  • git branch -v :可以查看所有 branch 最新一个 commit,-v--verbose 的缩写;
  • git branch [名称]:创建分支。注意分支的可读性,是指这个分支顺藤摸瓜可以 log 到哪些 commit,不是所有 commit 都可以通过这个分支 log 到,有些 commit 可能所有分支都 log 不到;
  • git checkout [分支名称]:切换分支;
  • git checkout [SHA]:切换 sha;
  • git checkout [tag]:切换 tag;
  • git checkout [文件名称]:撤销该文件 staged 之后的修改,相当于用 staged 的状态覆盖未 staged 的状态
  • git reset HEAD [文件名]:用该文件在 commit 的状态覆盖 staged 的状态,覆盖后相当于该文件在 staged 中和其在 commit 中无变化。又因为 reset 默认不覆盖 working dir,所以该文件在 working dir 中无影响,所以表现出该文件未 staged;
  • git branch [名称] [SHA]:表示创建分支,并指向 sha 这个 commit;
  • git branch -d [名称]:删除分支,注意删除分支是指删除分支这个标志,这个分支的 commit 至少短时间内并不会被删除。如果分支删除之前 merge 入了另一个分支,那这个被删除的分支的所有 commit 都可以通过另一个分支的 log 查看,并且不会被删除。如果没有 merge 就删除分支,那这个分支的所有 commit 不能通过 log 查看,但在一定时间之后(默认 30 还是 90 天)会被删除,删除前只能通过 git reflog 查看。当然就可以 checkout SHA 回到这个 commit;
  • git merge [分支名称]:把分支合并过来,合并分为快进合并、普通合并。checkout 在 a 分支,再 merge,那 merge 之后的新 commit 所在的分支也叫 a。快进合并不会产生新的 commit,只不过 HEAD 的位置变了,具体来说就是往前移动了,普通合并很定会产生新的 commit;
  • git mergetool:要先执行 git merge ,如果有冲突才可以用 mergetool,要先设置:git config --global merge.tool vimdiff3
  • git commit --amend:更新最近的一个 commit;
  • git revert [SHA]:还原 commit,会创建一个新 commit;
  • git reflog:可以查看被 reset 后、30 天内的 commit;

其他:

  • HEAD 当前 commit,HEAD^ 上一个 commit,也可以用 HEAD~ HEAD~ 表示;
  • HEAD^^、HEAD~2 表示上上个 commit;
  • HEAD^^^、HEAD~3 表示上上上个 commit;
  • ^、~ 的区别是,合并分支后的 commit 有两个父 commit,HEAD^ 表示当前所在分支的父 commit,HEAD^1 表示被合并过来的分支的父 commit,~ 没有这个功能。

关于 git checkoutgit resetgit restoregit revert 的区别,可以看 stackoverflow 的这个问答,它们都有后悔药的作用。如果要恢复一个文件,建议用 git restore,它是在 git 2.23 新加的功能,功能上是 git checkout 的子集,更安全。

donation赞赏
thumbsup0
thumbsdown0
暂无评论