先来讲说 Git 最本质的工做流程吧,从远程仓库拉取代码到本地版本库,签出分支而后在本地工做区进行开发,经过暂存区将代码提交到版本库,最后再推送到远程仓库。这样的流程不难理解,可是在实际团队协做中,每每会涉及到本身的分支、其余成员的分支、公共分支等分支的合并、解决冲突、推送、回退之类的操做。为了梳理清楚 Git 的各类操做,本文将以 场景 + 解决方案 的形式组装成操做手册,便于本身往后查阅,以及团队新成员能快速上手。git
Git 的核心概念分为四个区块,分别是远程仓库、本地版本库、暂存区以及工做区,下面这张图能清晰的描述出这四个区块之间的关系。shell
全局说明:分支后的星号*表明的是当前分支。安全
场景 1. 假设现有两个远程分支 master、branchA,并已经拉取到本地版本库,此时一同事删除了远程仓库中的分支 branchA,可是你电脑上仍然能看到 origin/branchA ,怎样才能更新仓库呢?bash
解决方法: 获取远程仓库的新分支以及删除远程仓库已删除的分支(本地版本库中的分支)markdown
git fetch -p // --prune(修剪)的缩写
复制代码
场景 2. 本地新增提交记录后,准备推到远程前会先从远程 git pull
拉取代码进行更新,假设此时其余同事已经推送新的代码,为何这时候拉取会自动产生一个(看似)没有意义的提交记录,怎样让提交记录更直观明了?app
原始提交记录fetch
C---D origin/branchA
/
A---B---E branchA*
复制代码
git pull 以后的提交记录多了一个 Merge branch "XXX" into XXX
,也就是下例的 F 点spa
C---D origin/branchA
/ \
A---B---E---F branchA*
复制代码
解决方法:指针
git pull --rebase
复制代码
origin/branchA
|
A---B---C---D---E branchA*
复制代码
git pull = git fetch + git merge // 拉取到本地版本库 + 合并到本地工做区
git pull --rebase = git fetch + git rebase // 拉取到本地版本库 + 工做区变基到最新版本库日志
场景 1. 怎么从指定分支建立分支?
解决方法:
// 从目标分支建立本地分支,若是没有指定目标分支,默认为当前分支
git branch <name> [<targetBranch>]
// 建立并切换到该分支
git checkout -b <branchname> [<tragetBranch>]
复制代码
场景 2. 怎么将本地分支关联到指定的远程分支?
解决方法:
git branch --set-upstream-to=<origin/branchname>
// 或者
git branch -u <origin/branchname>
复制代码
场景 3. 怎么取消本地分支和远程分支的关联?
解决方法:
git branch --unset-upstream [<origin/branchname>]
复制代码
场景 4. 怎么查看分支以及其关联的远程分支的详细信息?
解决方法:
git branch -vv
复制代码
场景 5. 怎么删除本地分支?
解决方法:
git branch -D <branchname>
复制代码
场景 6. 怎么删除远程分支?
解决方法:
git push -d origin <branchname> // 或者 git push origin :branchname 复制代码
场景 7. 怎么移动/重命名分支?
解决方法:
// 将目标分支 移动/重命名 到新分支
git branch -m <branchname> [<targetBranch>]
复制代码
场景 8. 怎么合并分支?
解决方法:merge
合并
git merge <branchname>
复制代码
状况1:原始提交记录
C---D feature
/
A---B master*
复制代码
状况1:合并以后的提交记录
A---B---C---D master*、feature
复制代码
状况2:原始提交记录
C---D feature
/
A---B---E master*
复制代码
状况2:合并以后的提交记录
C---D feature
/ \
A---B---E---F master*
复制代码
在多人协做开发场景下,merge 操做容易产生分支间的闭环,使得分支提交信息变得不清晰,不利于分支维护。
场景 9. 怎么合并指定分支的指定提交记录?
解决方法:cherry-pick
遴选
// 合并指定提交记录
git cherry-pick <commit>
// 合并多个提交记录
git cherry-pick <commit1> <commit2> <commit3>
// 若提交记录在同一分支上,则能够采用区间形式(start, end],commit1不包含,commit3包含
// 等价于合并了 commit二、commit3
git cherry-pick <commit1>..<commit3>
// 若省略了区间形式的起点,则起点默认为两个分支的交点
git cherry-pick ..<commit3>
复制代码
原始提交记录
C---D feature
/
A---B---E master*
复制代码
git cherry-pick B..D
遴选以后的提交记录
C---D feature / A---B---E---C'---D' master* 复制代码
cherry-pick 操做会拷贝提交记录到当前分支,使得当前分支的提交信息更为清晰。
扩展:cherry-pick
操做若产生了冲突,处理流程以下
git cherry-pick --abort
(中断则不须要后两步操做)git add .
git cherry-pick --continue
场景 10. 从公共分支签出特性分支进行新功能开发,开发完后准备合并回功公共分支,此时发现公共分支已经有其余人推送新的提交记录,此时怎么以公共分支来更新特性分支?
**解决方法:**经过 rebase
变基,将当前分支的基点移动到目标分支上
// branchname 默认为当前分支,可省略
git rebase <targetBranch> [<branchname>]
// 等价于下面两条命令
git checkout <branchname>
git rebase <targetBranch>
复制代码
原始提交记录
C---D feature
/
A---B---E master*
复制代码
git rebase master
变基以后的提交记录
C'---D' feature* / A---B---E master 复制代码
变基操做能更新当前分支的基点(起点),使当前分支包含公共分支上新的提交记录,这样有个好处就是若是和主分支存在冲突,能够在特性分支上提早解决。
由于重塑了历史提交记录,因此变基后当前分支会和远程分支不一致,须要采用强制推送(参见下文)覆盖远程分支。
扩展:rebase
操做若产生了冲突,处理流程以下
git rebase --abort
(中断则不须要后两步操做)git add .
git rebase --continue
场景 11. 怎么合并多个提交记录?
解决方法:git rebase --interactive
或者缩写形式 git rebase -i
交互式变基
// commit 为须要处理的提交记录区间的父节点
git rebase -i <commit>
复制代码
原始提交记录
A---B---C---D feature*
复制代码
须要合并 C、D 两个提交记录,git rebase -i B
pick a0a6eba feat: 新增 C 功能
pick 95f09e5 feat: 新增 D 功能
复制代码
将 D 提交记录合并进 C 中 ,因此把 D 记录的 pick
改成 squash
,而后保存退出便可
pick a0a6eba feat: 新增 C 功能
squash 95f09e5 feat: 新增 D 功能
复制代码
最终的提交记录,E 点则包含了 C 和 D 的所有改动
A---B---E feature*
复制代码
扩展:交互式变基过程当中会自动进入 vi 编辑模式,参数解释以下
场景 1. 最近的一次提交记录信息错误怎么修改?
解决方法:
git commit --amend -m '新的提交信息' 复制代码
场景 2. 最近的一次提交,发现遗漏了部分改动?
**解决方法:**出于提交信息的完整性考虑,在不新增提交记录的状况下将遗漏的改动合并到本次提交中。
// 将遗漏的文件改动添加到暂存区
git add <filename>
// 将暂存区中的全部文件合并到最近一次提交中,
// 若是不带 --no-edit 参数,则在合并以后会进入提交信息修改面板
git commit --amend --no-edit
复制代码
场景 1. 怎么推送到远程指定分支?
解决方法:
// 推送到远程指定分支,并建立关联
git push -u origin <branchname>
// 若本地分支已经与远程分支关联,则可省略远程分支
git push
复制代码
场景 2. 怎么强制推送到远程分支?
解决方法:
git push -f // --force 的缩写 复制代码
若在强制推送的过程当中,已经有其余人推送到该远程分支,则会使他人的提交记录丢失,为了更安全的推送
,可用以下命令。在他人推送了新的提交的状况下,强制推送会被拒绝
。
git push --force-with-lease
复制代码
扩展:须要强制推送的场景可能以下
git rebase
变基操做后git revert
操做)强制推送到远程分支会覆盖远程,若不熟悉此命令,请慎用!
场景 1. 怎么撤销本地工做区文件的改动?
解决方法:
// 丢弃工做区某个文件的改动
git checkout -- <filePath>
// 丢弃工做区全部文件的改动
git checkout -- .
复制代码
撤销本地工做的改动后,文件会回退到最近一次 commit 或 add 状态。
场景 2. 怎么撤销暂存区的改动?
解决方法:
// 当没有指定 filename 时,表示撤销暂存区中的全部文件
git reset HEAD <filename>
复制代码
撤销暂存区的改动后,文件会回到工做区状态。
场景 3. 怎么撤销本地版本库的改动?
解决方法:
// 回退到指定的提交记录
git reset [<mode>] [<commit>]
复制代码
经过指定 mode 参数,使回退以后的文件处于相应状态:
- --soft:将当前分支重置到指定 ,当前版本与指定版本间的改动文件处于 暂存区 中,待提交状态。
- --hard:重置 暂存区 和 工做区,自 以来暂存区和工做区中的任何修改都被 丢弃。
- --mixed(默认):将当前分支重置到指定 ,当前版本与指定版本间的改动文件处于 工做区 中,'not staged' 状态。
扩展:HEAD 能够理解为一个指针,老是指向当前分支上最近一次的提交记录。HEAD^ 表示上一个提交记录,HEAD^^表示上两个提交记录,HEAD~n 表示上 n 个提交记录。
HEAD^/HEAD~1
|
A---B---C---D---E---F branchA
| |
HEAD^^/HEAD~2 HEAD
复制代码
场景 4. 下班前急匆匆的将代码推到远程仓库,而后愉快的回家了,结果次日发现推远程的代码出错了(手动滑稽 :p ),这时候怎么撤销远程仓库的改动?
解决方法:revert
逆向修改,而后从新提交并推送到远程仓库。
// 撤销修改
git revert <commit>
// 推送到远程仓库,实现远程仓库的撤销
git push
复制代码
扩展:revert
和 reset
的区别
revert
是新增一次提交记录,其修改内容正好抵消指定 的改动,而 reset
的撤销效果是重置了版本库。假设本地版本库和远程仓库一致,reset
撤销了一个提交,此时本地版本库落后远程仓库一个版本,git push
推送到远程会失败,而 revert
新增了一个提交,本地版本库领先远程仓库一个记录,此时 git push
能够正常推送到远程。
场景 1. 本地 commit 提交了可是未推送到远程仓库,此时 git reset --hard
误操做强制回滚,弄丢的提交记录怎么恢复?
**解决方法:**经过 git reflog
查看引用日志,找到误删的提交记录,而后回滚到这条被删除的记录
git reflog // Reference logs(引用日志),能记录 HEAD 和分支引用所指向的历史
复制代码
假设本地提交了三次 commit,而后误操做强制回滚到第一次提交,致使第2、三次的提交所有丢失
// git reflog 引用日志输出格式以下
1c36188 HEAD@{0}: reset: moving to 1c36188
d921970 HEAD@{1}: commit: feature-3
1c002dd HEAD@{2}: commit: feature-2
1c36188 HEAD@{3}: commit (initial): feature-1
复制代码
找到误删的提交记录 feature-2 和 feature-3
git reset --hard d921970
复制代码
场景 1. 当正在编写代码的时候,接到了一个紧急任务,须要切换分支去开发,此时未完成的代码怎么保存?
解决方法:
// 将所有未保存的代码添加到贮藏区,若未填写描述信息,则以上一次 commit 的信息记录
git stash [push [-m <message>]]
复制代码
场景 2. 怎么查看贮藏区中保存的代码?
解决方法:
git stash list
// 若存在贮藏的代码,则输出格式以下
stash@{0}: On feature: 新功能开发未完成,先贮藏一下 // 添加了描述信息
stash@{1}: WIP on master: 8e50dc3a feat:添加新功能xxx // 未填写描述信息
复制代码
场景 3. 怎么取出贮藏的代码?
解决方法:
// 恢复指定下标的贮藏代码,并删除对应的贮藏列表,index 默认为 0
git stash pop [index]
// 等价于下面两条命令
git stash apply [index] // 取出贮藏
git stash drop [index] // 删除贮藏列表
复制代码
场景 4. 怎么清空贮藏列表?
解决方法:
git stash clear
复制代码