Git 实战手册

1、前言

先来讲说 Git 最本质的工做流程吧,从远程仓库拉取代码到本地版本库,签出分支而后在本地工做区进行开发,经过暂存区将代码提交到版本库,最后再推送到远程仓库。这样的流程不难理解,可是在实际团队协做中,每每会涉及到本身的分支、其余成员的分支、公共分支等分支的合并、解决冲突、推送、回退之类的操做。为了梳理清楚 Git 的各类操做,本文将以 场景 + 解决方案 的形式组装成操做手册,便于本身往后查阅,以及团队新成员能快速上手。git

2、必备知识点

Git 的核心概念分为四个区块,分别是远程仓库、本地版本库、暂存区以及工做区,下面这张图能清晰的描述出这四个区块之间的关系。shell

Git 核心区块

全局说明:分支后的星号*表明的是当前分支。安全

3、拉取篇

场景 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 // 拉取到本地版本库 + 工做区变基到最新版本库日志

4、分支篇

场景 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 操做若产生了冲突,处理流程以下

  1. 解决冲突,或者想中断这次操做 git cherry-pick --abort(中断则不须要后两步操做)
  2. 添加到暂存区 git add .
  3. 继续遴选 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 操做若产生了冲突,处理流程以下

  1. 解决冲突,或者想中断这次操做 git rebase --abort(中断则不须要后两步操做)
  2. 添加到暂存区 git add .
  3. 继续变基 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. p,pick 选中
  2. r,reword 选中,而且修改提交信息
  3. e,edit 选中,rebase 时会暂停,容许你修改这个 commit
  4. s,squash 选中,会将当前 commit 与上一个 commit 合并
  5. f,fixup ,与 squash 相同,但不会保存当前 commit 的提交信息
  6. x,exec 执行其余 shell 命令

5、提交篇

场景 1. 最近的一次提交记录信息错误怎么修改?

解决方法:

git commit --amend -m '新的提交信息'
复制代码

场景 2. 最近的一次提交,发现遗漏了部分改动?

**解决方法:**出于提交信息的完整性考虑,在不新增提交记录的状况下将遗漏的改动合并到本次提交中。

// 将遗漏的文件改动添加到暂存区
git add <filename>

// 将暂存区中的全部文件合并到最近一次提交中,
// 若是不带 --no-edit 参数,则在合并以后会进入提交信息修改面板
git commit --amend --no-edit
复制代码

6、推送篇

场景 1. 怎么推送到远程指定分支?

解决方法:

// 推送到远程指定分支,并建立关联
git push -u origin <branchname>

// 若本地分支已经与远程分支关联,则可省略远程分支
git push
复制代码

场景 2. 怎么强制推送到远程分支?

解决方法:

git push -f // --force 的缩写
复制代码

若在强制推送的过程当中,已经有其余人推送到该远程分支,则会使他人的提交记录丢失,为了更安全的推送,可用以下命令。在他人推送了新的提交的状况下,强制推送会被拒绝

git push --force-with-lease
复制代码

扩展:须要强制推送的场景可能以下

  • 分支 git rebase 变基操做后
  • 将错误代码推送到了远程,想要丢弃此提交记录(建议用下文介绍的 git revert 操做)

强制推送到远程分支会覆盖远程,若不熟悉此命令,请慎用!

7、撤销篇

场景 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
复制代码

扩展:revertreset 的区别

revert 是新增一次提交记录,其修改内容正好抵消指定 的改动,而 reset 的撤销效果是重置了版本库。假设本地版本库和远程仓库一致,reset 撤销了一个提交,此时本地版本库落后远程仓库一个版本,git push 推送到远程会失败,而 revert 新增了一个提交,本地版本库领先远程仓库一个记录,此时 git push 能够正常推送到远程。

8、日志篇

场景 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
复制代码

9、贮藏篇

场景 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
复制代码
相关文章
相关标签/搜索