三道 google 风格 git 面试题及其解答

5,790 阅读6分钟

第一题: 把配置文件推送到了远程仓库,怎样删除远程仓库的该配置文件,本地还要用到这个文件。

这种操作失误,比较常见。一般这样解决:

git rm --cached filename
echo filename >> .gitignore

先解释第二步,本地需要,远程仓库不需要,肯定是要把那个文件写入 .gitignore 文件里面。 否则以后还要删除。

第一步则是把该文件从 git 的暂存区域中删除。暂存区域,就是 index 区域。

见一下亲人:

111

git 三个区,git rm filename, 会把文件从工作区 Working Directory 和暂存区域 Staging Area 中删除。本地还要用,就不能这么搞。

git rm --cached filename, 则把文件从暂存区域 Staging Area 删除,保留工作区的,我们一般编辑见到的。

这种情况就是已经 commit 了,生成快照,文件进了版本库 Commit History,然后 push, 远程库与本地库同步一下。

这个时候,直接 push 到远程,无效。因为没有新的快照,也就是没有新的 commit id. 本地与远程的历史 log 是一致的。 修改文件,add 再 commit, push 提交过去,就会生效。

第二题: git 如何解决代码冲突

解决冲突三连

git stash 
git pull 
git stash pop 

操作就是把自己修改的代码隐藏,然后把远程仓库的代码拉下来,然后把自己隐藏的修改的代码释放出来,让 git 自动合并。接着找 <<<<<<<, 哪里冲突哪里改。

如果要代码库的文件完全覆盖本地版本,

git reset --hard commit id
git pull

前面两招挺管用的,场景就是合作的远程仓库上,别人做了一些改动,我没有 commit , 然后把别人的 commit 拉下来。

刚进入公司的时候,没办法,我也经常这么做

加强版就来了

场景就是合作的远程仓库上,别人做了一些改动,我在本地也做了一些 commit , 然后把别人的 commit 拉下来,再把我的更改添加上去。接着找 <<<<<<<, 哪里冲突哪里改。

这个时候,先检查一下我的本地仓库与合作的远程仓库的最近的一个共同 commit id.

git reset commit id
git stash 
git pull 
git stash pop 

git reset commit id 的作用是取消暂存文件。将 HEAD 的指针指向 commit id,修改了暂存区域 Staging Area 和版本库 Commit History,工作区沙盒 Working Directory 保持原样。

亲人来了,看图加深一下理解:

屏幕快照 2019-06-28 下午4.14.00.png

  • git reset commit id就是 git reset -mixed commit id,移动 HEAD,更新索引,即更新 staging area。移动 HEAD 分支的指向,使索引看起来像 HEAD。效果上看,就是取消了 commit id 以后的,add 和 commit .

  • git reset --soft commit id,就是移动 HEAD。移动 HEAD 分支的指向,本质上是撤销了上一次 git commit 命令。

当你在运行 git commit 时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。

当你将它 reset 回 HEAD~(HEAD 的父结点)时,其实就是把该分支移动回原来的位置,而不会改变索引和工作目录。

  • git reset --hard commit id, 移动 HEAD,更新索引,更新工作目录。三件事情,全做了。前两件事情,已经说了。更新工作目录,让工作目录看起来像索引。从效果上看,就是撤销一切修改,本地文件状态同 commit id 的那时候。

第三题: 什么时候合并分支用 git rebase, 不用 git merge ?

git rebasegit merge 都可以用于合并分支,从 feature 分支上,取得新的提交 commits , 然后运用到 master 分支上(当然运用到其他分支上也行)。

但是路子不同

merge 是合并,rebase 是变基

变基怎么变?

aaa

看图可知: git rebase 有一个移动 base , 改变合并基准的操作

直观的理解: git rebase 做的事情,就是先移指针,再移结点。

  • 先移指针:master 分支之前分出的 feature 分支的 commit id, 是 feature 分支的基准 base.

在 feature 分支上 git rebase master,就把 feature 分支的基准 base 移动到 master 分支最新的 commit id 上。

  • 再移结点: 把 feature 分支上新增的提交 commit id ,放到新的 base 结点后面。准确一些,就是把 feature 分支上新做的修改操作,重新应用到 master 分支的 HEAD 结点上。

举个例子:

合并分支前:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

根结点是 A, 最初是 A , 在 A 状态,分出去了分支 branch

git merge 是这样合并的:

A <- B <- C
^         ^
 \         \
  D <- E <- F

提交到 C 的 master 分支和提交到 E 的 branch 分支,直接合并,一般是合并到 master, 有冲突解决冲突。

看图可知: 采用 git merge ,不改变原来的 commit id, 会产生新的提交 commit id

项目协作成员比较多,一般需要使用 git rebase, 当然也可以这样git merge, git merge --squash feeature

如果使用 git merge ,很可能这样,

555

这样看,就很不舒服。需要采用 git rebase 修改历史:

git rebase 是这样合并的:

A <- B <- C <- `D` <- `E`

把 feature 分支( 例子中是 branch 分支)的提交 commits ,移动到 master 分支的顶端。

使用 rebase, 看起来更加 nice, 更加直观,历史就是一条直线嘛,没什么枝枝岔岔的。

看图可知: 使用 rebase 后,把 feature 分支上 commit 拿来后,commit id 改掉了。并且没有创建合并的公共节点 commit id

6666

有的团队采用 git rebase 工作流,有的采用 git merge 工作流,git rebase 工作流要求对 git 的理解深一些,商用多一些, feature 做好了,用 git rebase 合并。

这样加 feature 怎么加的,看 log 比较明朗。否则看 log, 上一条 feature A, 下一条 feature B , 不是很职业程序员。

git rebase 工作流,一般这么实操, 在 feature 分支上 git rebase master, 切到 master 分支上后, git merge feature, 这时候 merge 也没做什么事情,就是把 master 分支的 HEAD 结点,移动到 feature 的 HEAD 结点, 两个分支这时候状态同步了。

git merge 工作流民用挺合适的,他的设计非常符合直觉,玩 github 开源,比较合适。因为多人写作,git flow 工作流,不是很好统一

如果你采用 git rebase 工作流,你团队其他人不知道,你这么搞,commit id 修改了,与他们本地库的对不上,你们对你的意见可能会比较大

毕竟 git 采用分支,就是不冒犯别人代码的意思