详细学习git的相关

122 阅读12分钟

推荐实战网站 Learn git Branching

基础知识相关

  1. 提交 git commit -m '注释'

  2. 创建分支 git branch 分支名
    切换分支 git checkout 分支名
    创建并切换 git checkout -b 分支名

  3. (此时在被合并分支上)合并分支需要将副分支合并到主分支(主分支的提交记录会指向该分支,此时主分支会有两个父节点)
    git merge 副分支
    再切换到副分支,将副分支合并(移动)到主分支
    git checkout 副分支; git merge master

  4. git rebase master (此时在要合并的分支上,合并之后原来的提交记录变透明)。Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。
    再切换到主分支更新 git rebase bugFix(因为主分支在副分支节点之上,主分支的引用向前移动了一下)

  5. 移动head到提交记录节点-->git checkout <提交记录的哈希值> 使用git log查看提交记录 相对引用: 使用 ^ 向上移动 1 个提交记录 git checkout master^ 使用 ~ 向上移动多个提交记录,如 ~3(~上标)

  6. 强制修改分支位置(-f 选项让分支指向另一个提交) git branch -f master HEAD~3将 master 分支强制指向 HEAD 的第 3 级父提交。

  7. 撤销通过把分支记录回退几个提交记录来实现撤销改动,在reset后, C2 所做的变更还在,但是处于未加入暂存区状态。

    1. git reset HEAD~1 (只能在本地撤销)
    2. git revert HEAD (远程,revert 之后就可以把你的更改推送到远程仓库与别人分享,它会创建c2'的提交与c2上一层提交一样)
  8. 整理提交记录

    1. git cherry-pick <提交号>...将一些提交复制到当前所在的位置(HEAD)下面
  9. 交互式 rebase 指的是使用带参数 --interactive 的 rebase 命令, 简写为 -i
    git rebase -i HEAD\~4
    当 rebase UI界面打开时, 你能做3件事:

    • 调整提交记录的顺序(通过鼠标拖放来完成)
    • 删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
    • 合并提交。 遗憾的是由于某种逻辑的原因,我们的课程不支持此功能,因此我不会详细介绍这个操作。简而言之,它允许你吧多个提交巨鹿合并成一个
  10. 本地栈式提交:让 Git 复制解决问题的那一个提交记录就可以了,再合并到master

    • git rebase -i
    • git cherry-pick
  11. 提交的技巧--->rebase(可能会造成冲突) 你之前在 newImage 分支上进行了一次提交,然后又基于它创建了 caption 分支,然后又提交了一次。此时你想对的某个以前的提交记录进行一些小小的调整。我们可以通过下面的方法来克服困难:

    • 先用 git rebase -i 将提交重新排序,然后把我们想要修改的提交记录挪到最前
    • 然后用 git commit --amend 来进行一些小修改,不保留上一次的提交
    • 接着再用 git rebase -i 来将他们调回原来的顺序
    • 最后我们把 master 移到修改的最前端(用你自己喜欢的方法),就大功告成啦!
  12. 提交的技巧2--->git cherry-pick 要在心里牢记 cherry-pick 可以将提交树上任何地方的提交记录取过来追加到 HEAD 上(只要不是 HEAD 上游的提交就没问题)。

    • 先用 git cherry-pick 复制不属于HEAD上游的提交记录
    • 然后用 git commit --amend 来进行一些小修改,不保留上一次的提交
    • 再用git cherry-pick 复制最新的提交记录
  13. tag的作用:它们并不会随着新的提交而移动。你也不能检出到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。 git tag v1 c1 我们将这个标签命名为 v1,并且明确地让它指向提交记录 C1,如果你不指定提交记录,Git 会用 HEAD 所指向的位置。(可以不移动HEAD)

  14. git describe(一个查找产生 Bug 的提交记录的指令)
    git describe 的​​语法是: git describe <ref>
    <ref> 可以是任何能被 Git 识别成提交记录的引用,如果你没有指定的话,Git 会以你目前所检出的位置(HEAD)。 它输出的结果是这样的: <tag>_<numCommits>_g<hash>
    tag 表示的是离 ref 最近的标签, numCommits 是表示这个 ref 与 tag 相差有多少个提交记录, hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。 当 ref 提交记录上有某个标签时,则只输出标签名称

  15. ^ 改变HEAD到它的父提交
    git checkout master^2 HEAD将到它的第二个父提交记录
    git checkout master~^2~ 可支持链式

远程仓库相关

远程分支有一个命名规范 —— 它们的格式是: <remote name>/<branch name>
因此,如果你看到一个名为 o/master 的分支,那么这个分支就叫 master,远程仓库的名称就是 o。 当你用 git clone 某个仓库时,Git 已经帮你把远程仓库的名称设置为 origin 了

  • 从远程仓库获取数据--Git Fetch(用于更新下载) 当我们从远程仓库获取数据时, 远程分支也会更新以反映最新的远程仓库
  1. 从远程仓库下载本地仓库中缺失的提交记录
  2. 更新远程分支指针(如 o/master)
    git fetch 实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。
    git fetch 并不会改变你本地仓库的状态。它不会更新你的 master 分支,也不会修改你磁盘上的文件。
    所以, 你可以将 git fetch 的理解为单纯的下载操作。
  • 下载并合并分支 git pull 就是 git fetch 和 git merge 的缩写!

模拟团队合作

克隆一个远程仓库(用 git clone),再在刚创建的远程仓库中模拟一些修改,然后在你自己的本地分支上做一些提交,再拉取远程仓库的变更。

  1. git clone
  2. git commit (此时在本地仓库上,远程仓库提交记录还没有在本地上得到更新,需要pull合并)
  3. git pull

git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于你正使用的 Git 的版本 远程仓库和会本地仓库的提交记录都会得到更新

假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

这种情况下, git push 就不知道该如何操作了。如果你执行 git push,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,异或由于你的提交已经过时而直接忽略你的提交?

因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。 比如本地仓库c1-->c3,而远程仓库为c1-->c2,因为你最新提交的 C3 基于远程分支中的 C1。而远程仓库中该分支已经更新到 C2 了,所以 Git 拒绝了你的推送请求。

最直接的方法就是通过 rebase 调整你的工作

  1. git fetch 将c1-->c2分支下载,与c1-->c3有共同一个祖先
  2. git rebase o/master 此时在还当前分支,合并后c2,c3共同指向共同的最新提交分支(本地有合并变化,远程仓库直接指向最新提交)

上面简写为git pull --rebase

  1. git push 我们用 git fetch 更新了本地仓库中的远程分支,然后用 rebase 将我们的工作移动到最新的提交记录下,最后再用 git push 推送到远程仓库。

使用merge方法

  1. git fetch 将c1-->c2分支下载,与c1-->c3有共同一个祖先
  2. git merge o/master 此时在还当前分支,(本地和远程都会将c1的孩子提交进行合并成一个共同的提交)

上面简写为git pull

  1. git push 尽管 git merge 不会移动你的工作(它会创建新的合并提交),但是它会告诉 Git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。

git pull --rebase 就是 fetch 和 rebase 的简写! git pull 就是 fetch 和 merge 的简写!

模拟

在大型项目中开发人员通常会在(从 master 上分出来的)特性分支上工作,工作完成后只做一次集成。这跟前面课程的描述很相像(把 side 分支推送到远程仓库),不过本节我们会深入一些.

但是有些开发人员只在 master 上做 push、pull —— 这样的话 master 总是最新的,始终与远程分支 (o/master) 保持一致。

对于接下来这个工作流,我们集成了两个步骤:(git pull --rebase; git push)

  • 将特性分支集成到 master 上
  • 推送并更新远程分支

推送远程主分支

推送主分支(rebase).png

合并远程仓库

rebase与merge操作远程仓库的对比 优点: Rebase 使你的提交树变得很干净, 所有的提交都在一条线上 缺点: Rebase 修改了提交树的历史 比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。

merge推送.png

远程跟踪

pull 操作时, 提交记录会被先下载到 o/master 上,之后再合并到本地的 master 分支。隐含的合并目标由这个关联确定的。

push 操作时, 我们把工作从 master 推到远程仓库中的 master 分支(同时会更新远程分支 o/master) 。这个推送的目的地也是由这种关联确定的!

当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/master)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 master。 克隆完成后,你会得到一个本地分支(如果没有这个本地分支的话,你的目录就是“空白”的),但是可以查看远程仓库中所有的分支(如果你好奇心很强的话)。这样做对于本地仓库和远程仓库来说,都是最佳选择。

git checkout -b totallyNotMaster o/master

  • 就可以创建一个名为 totallyNotMaster 的分支,它跟踪远程分支 o/master。

git branch -u o/master foo

  • 另一种设置远程追踪分支的方法就是使用:git branch -u 命令,执行:git branch -u o/master foo (foo必须已经存在) 这样 foo 就会跟踪 o/master 了。如果当前就在 foo 分支上, 还可以省略 foo: git branch -u o/master

Git Push 的参数

git push <remote> <place> 通过“place”参数来告诉 Git 提交记录来自于 master, 要推送到远程仓库中的 master。它实际就是要同步的两个仓库的位置。 参数“remote”是远程仓库

  • git push origin master 切到本地仓库中的“master”分支,获取所有的提交,再到远程仓库“origin”中找到“master”分支,将远程仓库中没有的提交记录都添加上去

要同时为源和目的地指定 <place> 的话,只需要用冒号 : 将二者连起来就可以了: git push origin <source>:<destination> 如果你要推送到的目的分支不存在,git会新建一个。 source指的是本地提交记录的位置 destination指的是要移动的本地的远程仓库

Git fetch 的参数

git fetch origin foo Git 会到远程仓库的 foo 分支上,然后获取所有本地不存在的提交,放到本地的 o/foo 上。 它不会更新你的本地的非远程分支, 只是下载提交记录(这样, 你就可以对远程分支进行检查或者合并了)。

git fetch origin <source>:<destination> source 现在指的是远程仓库中的位置 而 <destination> 才是要放置提交的本地仓库的位置。它与 git push 刚好相反

例子:git fetch origin foo~~1:bar Git 将 foo~~1 解析成一个 origin 仓库的位置,然后将那些提交记录下载到了本地的 bar 分支(一个本地分支)上,如果你要推送到的目的分支不存在,git会新建一个。 foo~1是远程仓库位置。 bar是本地分支位置。

git push origin :foo 通过给 push 传空值 source,成功删除了远程仓库中的 foo 分支 git fetch origin :foo 如果 fetch 空 到本地,会在本地创建一个新分支bar。

Git pull 参数

因为 git pull 到头来就是 fetch 后跟 merge 的缩写。你可以理解为用同样的参数执行 git fetch,然后再 merge 你所抓取到的提交记录。

  • git pull origin foo 相当于: git fetch origin foo; git merge o/foo

  • git pull origin bar~~1:bugFix 相当于:git fetch origin bar~~1:bugFix; git merge bugFix

git pull 实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)