初级命令
创建Git仓库,Git 通过 .git 目录来管理版本历史,初始化命令用来初始化一个新的仓库。在add之前,所有文件改动都在工作区(Working Tree),add之后就在暂存区(Staging Area),commit之后就在版本库(Repository),初始化之后只有master一个主干分支
bash
# 初始化命令,让项目文件目录变成一个git版本控制仓库
git init
# 检查当前状态,会输出有哪些文件改动未添加到暂存区
git status
# 是把改动从工作区放到暂存区,用于精确控制一次提交包含哪些内容。
git add xxx
# 提交命令,会把暂存区的内容生成一个不可变的快照,并记录作者、时间和提交信息
git commit
更新
团队协作中每次本地提交之前核心规则,严格遵守的流程:
bash
stash暂存起来 ---> update ---> stash pop -> commit ---> push
git本身没有update,很多IDE集成的update功能其实就是等价于git pull。
bash
# git pull等价于:
git fetch #只更新远程信息
git merge #下载远程修改并尝试合并
提交
如果说我在某个文件里面将a改成了b,然后commit了,后来发现b不如a,我又把它reset掉了。但是我又发现还是b好,这个时候可以不重新commit,即可快速回到上一次已经commit成功的状态。
bash
# 先到reflog找到你的commit hash
git reflog
git reset --hard 你的CommitHash
触类旁通,merge本质上也是一种特殊的commit,当合并回退并不理想时,它也适用这条命令撤销合并回退。
回退
反悔刚才的提交,但代码还想留着改 → 用 git reset --soft
撤销刚才的提交,代码也不要了 → 用git reset --hard
在历史里留下一个'我撤了'的记录 → 才用 git revert
注意:revert不会让代码回到"未提交状态",它会直接生成一个"反向的新提交"。
查询记录
和log很像的一个命令叫status、reflog,git log用于查看提交历史,反映项目的正式变更记录;git reflog记录HEAD的每一次移动,用于恢复误操作(如误删分支、误 reset);git status显示当前工作区和暂存区的状态,指导下一步操作。
三者的核心区别在于:log看历史,reflog看操作轨迹,status看当前状态。
bash
# 查看提交历史,展示已经持久化到仓库中的 commit 记录。
git log
# 提交多次log内容多会分页显示,比较麻烦,这样可以单页显示
git --no-pager log
# --oneline(压缩显示) --graph(画出分支拓扑图) --all(显示所有分支) --decorate(给commit贴标签显示括号内容)
git log --oneline --graph --all --decorate
未保存修改
一般回退时IDE都会出现已经修改的文件标记为uncommittedChanges,意味着工作区还有这些修改没有操作,要么shelves暂存起来,要么全部丢弃再进行其他git操作。
丢弃
bash
# 清空暂存区
git reset
# 清空工作区
git checkout -- .
暂存

stash就是把当前工作区/暂存区的改动临时藏起来,但不生成commit。这让git就不会在有未保存修改时阻止你回退分支节点(如上图阻止你切换分支),在你需要这些暂存起来的修改时,stash pop出来,这样这些改动就不会消失,需要时可以立即重现。
许多IDE用Shelve替代了git原生Stash功能,它并不是git自带的。存和恢复对应的操作就是Shelve和unShelve。
bash
# 存起来
git stash
# 存起来并写备注
git stash push -m "备注"
# 查看stash列表
git stash list
# 恢复并删除stash记录
git stash pop
# 回复但不删除stash记录
git stash apply
# 删除某个stash
git stash drop stash@{1}
# 清空所有stash
git stash clear
恢复但不删除会在list和节点图一直显示一个stash记录:

分支
查询
bash
# 查看当前有哪些分支
git branch
新建与删除
如果当前HEAD在要删除分支,git会直接拦截你不让你删除,这个时候需要切换到其他分支再操作。
bash
# 新增分支
git branch XXX
# 删除分支
git branch -d XXX
git branch -D XXX
小写d是安全删除,Git 会校验:如果该分支独有代码还没合并到当前分支,就拒绝删除,防止代码丢失。
大写D强制删除。
另外如果中途存在merge、rebase这类操作未完成,会强制中断,提示:

需要merge/rebase+--abort彻底终止这类交互式变基中途状态,才可以删除。
恢复已删除分支
删除了分支怎么办?可以用reflog找回。git reflog是引用日志,记录本地仓库所有HEAD移动记录。普通 git log 只能看分支上存在的提交;git reflog 能看到已经被删除、回滚、覆盖、丢弃的提交,是救丢失代码的神器。但它本身不会记录删除分支这一操作,可以找到被删除分支最后一次提交的commit ID,结合branch命令找回。
bash
# 例如9e65c29 是被删除分支最后一次提交的commit hash
git branch feature 9e65c29
合并
合并时往往需要进入vim编辑提交信息,merge、rebase一样。
bash
# 合并分支
git merge XXX
# 合并分支时发生冲突 回退
git merge --abort
# 合并分支时冲突能够解决 继续合并
git merge --continue
merge
merge合并极大概率发生冲突,需要先编辑文件保留想要的代码,必须先add再继续合并否则会报错,再merge --continue(需要进入vim填写提交信息,i开始编辑 esc退出 :wq结束),或者commit也行,二选一。如果有多个commit节点发生冲突需要来回继续确认、解决冲突好几次,无法处理这么多冲突也可以--abort一口气放弃回到合并之前。
目前分支状态:

在develop上合并master时:

在master上合并develop时:

操作不同分支进行合并,操作分支会变到最左侧,被合并分支会有弯头指向最左侧操作分支。有意思的是,只是执行checkout操作,没有提交修改,左侧依然是最近一次提交修改的分支,只要切换了分支并有实际改动,最左侧才会立即变成操作分支。
分支A merge了分支B,只相当于A提了一次修改,B依然保持独立不受A的影响,B的上一次提交并不是这次,这次提交只针对A来说。
rebase
merge是"把两条线接在一起",rebase是"把一条线挪到另一条线的屁股后面"。rebase也叫变基操作。
rebase之前:

rebase之后:

merge 会保留完整的历史拓扑,并生成一个新的 merge commit,适合团队协作和公共分支;rebase 会把当前分支的提交移动到目标分支的最新提交之后,使历史呈线性结构,适合本地整理提交,但由于会改写历史,不适合已推送的公共分支。
撤回合并
Git允许你改自己的过去,但不允许你改别人的过去。已经push的代码可能已被同事拉取,已成为既定历史事实,你的拯救方式只能是基于当前反向修改你之前的代码,成为一次新的commit。
核心原则:
没 push → 用 git reset --hard HEAD~1(当前HEAD的前一个提交,1是相对前1个提交点)
已 push → 用 git revert -m 1 commitHash(m1指的就是操作分支的前一个提交点)。
在 merge 冲突尚未解决时,可以使用 git merge/rebase --abort 直接放弃本次合并。
摘抄
很多时候,某个分支为了某个bug打了补丁,但其他分支也想要这个修复,就需要摘抄过来,颗粒度不同于合并,需求更小,这个时候cherry-pick就上场了。
bash
merge = 把整棵桃树搬过来
rebase = 把桃树嫁接到另一棵树上
cherry-pick = 只摘一颗桃子

Cherry-pick 是"精准投送提交"的工具,适合救火、纠错、局部合流,不适合大规模合并。