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 switchto 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..topictopic分支中有但是master分支中没有的提交git diff master..topic查看两个分支的差别
git log refA refB ^refCrefA和refB中有但是refC中没有的提交git log [--left-right] master...experiment被两者之一包含但是不被同时包含的提交[显示究竟被哪个包含]
交互式操作
git add -i进入交互式暂存模式,可以把文件的一部分更改add进去
类似的还有下面几个(-p --patch):
git add -pgit stash -pgit 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 pullgit merge rebase等是在比对文件并移动HEAD和HEAD所指向的branch的指针reset是在试图直接操作指针和Index区域,- 在各种指针移动的时候对应的节点并不会被删除,只是因为链接为单向的,可能因为指针的移动变得无法找到,但是只要能找到那个节点hash就可以恢复(e.g. 使用
reflog)。换句话说,理论上只要git已经为文件创建了blob文件,那么内容就不会丢失只是可能不容易找到。
参考资料:
全文内容为Pro Git阅读笔记