Git Review Memo

本文为Pro Git阅读笔记,整理了日常操作中涉及到的git操作。

Basic

  1. git xxx -h 可以查看该命令参数的简要介绍
    • git help xxx可以查看详细文档
  2. 命令中--用来区分参数和文件路径(e.g. git checkout -- <file>)

撤销更改

  • git commit --amend [--no-edit] 将内容增加进上次的更改[保持提交信息不变]

  • git restore --staged <file> git reset HEAD <file> 撤销文件暂存
  • git restore <file> git checkout -- <file> 撤销文件的编辑(不可恢复)

  • git revert 创建一个新的commit来撤销指定的commit

  • 撤销合并见下文

分支操作

git内部是一个单向列表,每个节点的指针都指向前一个节点。 每个 branch 都是一个指针, HEAD 也是一个指针只想当前 checkout 出来的位置 这非常重要! 理解这一点就能理解下面的一切了

  • use git switch to switch

  • git branch [--merged | --no-merged] [<name>] 显示已经合并(或没有合并)进当前分支(或name分支)的分支
  • git branch <name> [<hash>] 从当前状态[使用指定hash]创建分支
  • git branch -d <name> 删除指定分支(-D强制删除)

  • git checkout -b <name> [<hash>] 创建并切换分支

关于合并分支与分叉

  1. 当两个分支都进行了各自的更改并commit后才会有所谓分叉现象,会使用 3-way merge策略 进行合并
  2. 如果只是一个分支有更改而另一个分支没有,则合并使用简单的 fast-forward 策略合并即可

fast-forward 当试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么Git只会简单的将指针向前推进(指针右移)

遇到冲突的情况

  1. ======符号的上半部分为HEAD所指的文件状态,也就是当前检出的分支的文件状态,下半部分为传入的更改
  2. 在处理好冲突后,使用git add将其标记为处理完成
  3. git mergetool可以启动图形化界面帮助处理更改
  4. 全部处理完成后使用git commit来进行提交

Merge, rebase and squaze

merge

merge会将待合并分支的信息全部带入到合并结果中。

问题:如果一个节点有两个前向节点怎么存储?

remote-branch

  1. git fetch 可以将服务器的数据拉取到本地来,但是并不修改本地的分支
  2. git branch -vv 可以查看分支追踪情况
  3. git ls-remote 显示远程情况

  4. git checkout -b <name> <remote/name>可以从一个远程分支创建本地分支来开始工作,这会自动创建所谓追踪分支
  5. git push --set-upstream-to <local_bantch_name>[:<remote_bantch_name>]可以将本地存在而远程不存在的分支推送到远程去,同样自动追踪
    • 这等价于下面两步操作
    • step1 git push <origin> <local_bantch_name>[:<remote_bantch_name>] 将本地的某个分支推送到远程
    • step2 git branch [-u | --set-upstream-to] <origin/xxx>将本地的追踪分支设定为远程的xxx
  6. git push origin --delete xxx可以删除远程分支xxx
    • git push origin :<NAME> 效果同样

关于追踪分支

  1. git checkout --track origin/serverfix 也可以从远程创建追踪分支
  2. git checkout serverfix当本地不存在远程存在时,这样子会自动按照上一条执行
  3. git checkout -b <name> <remote/name> 可以创建和远程名称不同的本地分支

Rebase

Rebase 是把相对于共同祖先节点的操作整理出来依次应用到目标分支,以此来达到消除分叉的目的。

Rebase后只是checkout到目标分支进行fast-forward merge

  1. git rebase <target_branch>把当前分支应用到 target_branch 上

  2. git rebase --onto master server client 将在client分支里但不在server分支里的修改后合并到master

  3. git rebase <basebranch> <topicbranch> 把topic整合到base上

技巧 如果本地和远程都有修改可以直接rebase本地到remotes/origin/HEAD

选择或指定commit

  1. git show <hash> 用来查看指定提交

  2. HEAD@{n} HEAD第n次移动前的位置(当前为0)
    • git reflog可以查看历史记录
    • 同理main@{3}可以查看main3次移动前的位置
    • 引用日志只保存在本地
  3. HEAD^[n] 代表HEAD的第n个父提交(不写n代表1) 用于选择在合并分叉的时候造成的多个父亲

  4. HEAD~[n] 节点[的父亲]*n 用来选择祖父节点

  5. git log master..topic topic分支中有但是master分支中没有的提交
    • git diff master..topic 查看两个分支的差别
  6. git log refA refB ^refC refA和refB中有但是refC中没有的提交
  7. git log [--left-right] master...experiment 被两者之一包含但是不被同时包含的提交[显示究竟被哪个包含]

交互式操作

  1. git add -i 进入交互式暂存模式,可以把文件的一部分更改add进去

类似的还有下面几个(-p --patch):

  1. git add -p
  2. git stash -p
  3. git restore -S -p 交互式取消stage
  4. git restore [-s] -p 交互式discard(取消更改)

修改提交历史

  1. git rebase -i HEAD~3 交互式的rebase即可完成修改 这个操作可以对历史进行各种修改(合并、删除等等)

关于reset和checkout

文档链接

基本概念

  1. 文档中所说的Index是指暂存区(看图理解)
    • Git将上一次检出到工作目录中的所有文件填充到索引区
    • 之后你会修改并使用git add将其中一些文件替换为新版本
    • 接着通过 git commit 将它们转换为树来用作新的提交
  2. Working Tree就是指工作目录

reset和checkout的区别

git checkout是在移动HEAD自己,而reset是移动HEAD所指向分支的指向

两者的使用

  1. commit级别
    • --soft 只改变HEAD,不改变IndexWorking Tree。本质上是撤销git commit。这样进行commit相当于在进行git commit -amend
    • --mixed 改变HEADIndex,不改变Working Tree。相当于在上面的基础上再进行git restore -S
    • --hard 全都改变。相当于在上谜案的基础上再进行git restore -s(危险)
  2. 操作单个文件
    • git reset [commit] <paths> 使某个文件在Index中的状态变到指定版本
    • git checkout [commit] <paths>使某个文件在Working Tree中的状态变到指定版本(危险)

撤销合并

  1. git reset --hard HEAD~ 但是这会修改历史,如果已经发布更改最好不这样做

  2. git revert -m 1 HEAD -m指出回到哪个父节点,对于合并来说1就是刚刚合并来的那个节点

不甚严谨的几点理解

git主要包括节点和指针,数据和文件目录会以节点的形式存储起来,而branch, HEAD等均为指向节点的指针。

  • commitadd操作是在创建节点,其他操作几乎都是在移动指针
  • git push是在移动仓库的指针remotes/origin/HEAD等等
  • git pull是在依据设定的"追踪分支"尝试merge远程更改并移动HEAD指针及分支指针
  • git fetch的时候会把远程仓库的数据都拿下来但是并不更改本地的指针指向,手动进行merge remotes/origin/HEAD操作后就相当于执行了git pull
  • git merge rebase等是在比对文件并移动HEAD和HEAD所指向的branch的指针
  • reset是在试图直接操作指针和Index区域,
  • 在各种指针移动的时候对应的节点并不会被删除,只是因为链接为单向的,可能因为指针的移动变得无法找到,但是只要能找到那个节点hash就可以恢复(e.g. 使用reflog)。换句话说,理论上只要git已经为文件创建了blob文件,那么内容就不会丢失只是可能不容易找到。

参考资料:

全文内容为Pro Git阅读笔记


Git Review Memo
https://blog.yrpang.com/posts/60954/
作者
yrPang
发布于
2021年3月21日
许可协议