Git Review Memo
本文为Pro Git阅读笔记,整理了日常操作中涉及到的git操作。
Basic
git xxx -h
可以查看该命令参数的简要介绍git help xxx
可以查看详细文档
- 命令中
--
用来区分参数和文件路径(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 switchgit branch [--merged | --no-merged] [<name>]
显示已经合并(或没有合并)进当前分支(或name分支)的分支git branch <name> [<hash>]
从当前状态[使用指定hash]创建分支git branch -d <name>
删除指定分支(-D强制删除)git checkout -b <name> [<hash>]
创建并切换分支
关于合并分支与分叉
- 当两个分支都进行了各自的更改并commit后才会有所谓分叉现象,会使用 3-way merge策略 进行合并
- 如果只是一个分支有更改而另一个分支没有,则合并使用简单的 fast-forward 策略合并即可
fast-forward 当试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么Git只会简单的将指针向前推进(指针右移)
遇到冲突的情况
======
符号的上半部分为HEAD
所指的文件状态,也就是当前检出的分支的文件状态,下半部分为传入的更改- 在处理好冲突后,使用
git add
将其标记为处理完成 git mergetool
可以启动图形化界面帮助处理更改- 全部处理完成后使用
git commit
来进行提交
Merge, rebase and squaze
merge
merge会将待合并分支的信息全部带入到合并结果中。
问题:如果一个节点有两个前向节点怎么存储?
remote-branch
git fetch
可以将服务器的数据拉取到本地来,但是并不修改本地的分支git branch -vv
可以查看分支追踪情况git ls-remote
显示远程情况git checkout -b <name> <remote/name>
可以从一个远程分支创建本地分支来开始工作,这会自动创建所谓追踪分支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
git push origin --delete xxx
可以删除远程分支xxxgit push origin :<NAME>
效果同样
关于追踪分支
git checkout --track origin/serverfix
也可以从远程创建追踪分支git checkout serverfix
当本地不存在远程存在时,这样子会自动按照上一条执行git checkout -b <name> <remote/name>
可以创建和远程名称不同的本地分支
Rebase
Rebase 是把相对于共同祖先节点的操作整理出来依次应用到目标分支,以此来达到消除分叉的目的。
Rebase后只是checkout到目标分支进行fast-forward merge
git rebase <target_branch>
把当前分支应用到 target_branch 上git rebase --onto master server client
将在client
分支里但不在server
分支里的修改后合并到master
上git rebase <basebranch> <topicbranch>
把topic整合到base上
技巧 如果本地和远程都有修改可以直接rebase
本地到remotes/origin/HEAD
选择或指定commit
git show <hash>
用来查看指定提交HEAD@{n}
HEAD第n次移动前的位置(当前为0)git reflog
可以查看历史记录- 同理
main@{3}
可以查看main3次移动前的位置 - 引用日志只保存在本地
HEAD^[n]
代表HEAD的第n个父提交(不写n代表1) 用于选择在合并分叉的时候造成的多个父亲HEAD~[n]
节点[的父亲]*n 用来选择祖父节点git log master..topic
topic分支中有但是master分支中没有的提交git diff master..topic
查看两个分支的差别
git log refA refB ^refC
refA和refB中有但是refC中没有的提交git log [--left-right] master...experiment
被两者之一包含但是不被同时包含的提交[显示究竟被哪个包含]
交互式操作
git add -i
进入交互式暂存模式,可以把文件的一部分更改add进去
类似的还有下面几个(-p --patch):
git add -p
git stash -p
git restore -S -p
交互式取消stagegit restore [-s] -p
交互式discard(取消更改)
修改提交历史
git rebase -i HEAD~3
交互式的rebase即可完成修改 这个操作可以对历史进行各种修改(合并、删除等等)
关于reset和checkout
基本概念
- 文档中所说的
Index
是指暂存区(看图理解)- Git将上一次检出到工作目录中的所有文件填充到索引区
- 之后你会修改并使用
git add
将其中一些文件替换为新版本 - 接着通过 git commit 将它们转换为树来用作新的提交
Working Tree
就是指工作目录
reset和checkout的区别
git checkout
是在移动HEAD自己,而reset
是移动HEAD所指向分支的指向
两者的使用
- commit级别
--soft
只改变HEAD
,不改变Index
和Working Tree
。本质上是撤销git commit
。这样进行commit
相当于在进行git commit -amend
--mixed
改变HEAD
和Index
,不改变Working Tree
。相当于在上面的基础上再进行git restore -S
--hard
全都改变。相当于在上谜案的基础上再进行git restore -s
(危险)
- 操作单个文件
git reset [commit] <paths>
使某个文件在Index
中的状态变到指定版本git checkout [commit] <paths>
使某个文件在Working Tree
中的状态变到指定版本(危险)
撤销合并
git reset --hard HEAD~
但是这会修改历史,如果已经发布更改最好不这样做git revert -m 1 HEAD
-m指出回到哪个父节点,对于合并来说1就是刚刚合并来的那个节点
不甚严谨的几点理解
git主要包括节点和指针,数据和文件目录会以节点的形式存储起来,而branch
, HEAD
等均为指向节点的指针。
commit
和add
操作是在创建节点,其他操作几乎都是在移动指针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阅读笔记