Skip to content

git 学习

https://learngitbranching.js.org/

基础篇

git commit #  提交

git branch <your-branch-name> # 创建分支

git checkout -b <your-branch-name> # 创建分支并切换到分支

git merge bugFix # 将 bugFix 分支合并进入到当前分支

git rebase master # 将当前分支 rebase 到指定的分支

高级篇

HEAD 是一个对当前检出记录的符号引用, 下面指令可以修改 HEAD 的指向

编辑到哪里 HEAD 就指向哪里, 一般情况下我们使用分支的最后节点,但是有时需要指向历史中的某个节点,并针对那一点进行操作。所以就需要移动 HEAD 到其它节点,然后再进行处理

节点的定位

  • 节点符号(节点的 hash 值,或是 hash 值前 n 个字符)
  • 分支名(表示分支的最后那个节点位置, 其实分支名可以认为是节点的别名)
  • HEAD 表示当前编辑的位置
  • ^ 向上移动一位, ^^ 向上移动两位
  • ~n 向上移动 n 位

当我们要操作某个节点时,则移动 HEAD 的位置到这个节点,然后再进行操作

# 将HEAD 直接移支某个指定的位置 
git checkout <节点> # 将 HEAD 指向节点
git checkout C1;  # HEAD 指向 C1 (c1 为符号)
git checkout master; # HEAD 指向 master
git commit; # HEAD 前进一步, 这也是移动HEAD 的一种方式 
git checkout C2 # HEAD 指向 C2

# 使用的相对节点定位 ^ 表示上一级节点
git checkout master^  # 表示从 master 开始向上移动一级
git checkout master^

# 使用 ~ 指定数值,表示, 表示称动到指定数字的上级节点
git checkout master~4 # 表示 HEAD 从master 向上移动四级
git checkout HEAD~4 # 表示 HEAD 从当前位置向上移动四级

# 当遇到合并分支时 git checkout HEAD^1 / HEAD^2 选择分支
# 可以使用链式移动 git checkout HEAD~^1~4 # 选择分析 1,然后再向上4级, 链式好像只能使用 HEAD 

将分支移到某个节点

放弃分支原来的节点,将分支从新节点开始

# 将分支移到某个位置,**应该就是**放弃**现在的分支,让分支从哪个节点重新开始**
git branch -f master HEAD~3
git branch -f master C8
git branch -f master bugFix
git branch -f master <节点>

撤销

分为两种 git reset 与 git revert

一般本地使用 reset , 要影响到远程只能使用 revert ,具体看例子中的备注

git reset HEAD~1 # 表示将当前节点向上移一级,原来的节点丢失
git reset master # 表示将当前操作移到某个节点,当前操作至该节点的操作丢失
git reset <节点> # 表示丢弃从当位置到<节点>的操作
# 原节点所做的变更还在,但是处于未加入暂存区状态。

git revert HEAD # 表示生成一个新的节点,该节点与上一个节点的操作是相反的,这样达到清除的目的。因为 reset 只在本地操作。但是如果已经推送到远端的话,不能让别人撤消,只要生成一个相反的操作就达到撤销的效果
git reset <节点> # 例子里没有说,有空搜索一下

移动提交记录

整理操作表示将一些节点复制到当前的节点中

Git Cherry-pick

将某一些节点复制到当前节点

git cherry-pick <节点1> <节点2> <节点3> # 将 节点1、节点2、节点3 依次复制到当前节点
# 比如下面的截图, 原 master 在 c5, 运行 git cherry-pick C2 C4 后复制 c2、c4 得到 c2'、 c4' 

1543974474007

交互式 rebase

交互式 rebase 指的是使用带参数 --interactive 的 rebase 命令, 简写为 -i , 当进行整理时,记了节点名 (如果有节点名,直接使用 Git Cherry-pick )。 会显示 GUI 辅助进行整理操作

git rebase -i <节点> # 表示从 节点 开始进行整理。
git rebase -i HEAD~4

杂项

修改提交 1:合并时放弃一些不要的节点

如下图所示, 处理bugFix 过程中加入一了一些调试信息,但是这些信息不想加入到 master (如果使用 git merge 会将调试信息的节点也合并过来) 中,那么可以使用如下命令进行操作

git checkout master # 将当前操作指向 master
git cherry-pick c4 # 将 c4 节点复制到 master

1543975128818

修改提交 2: 修改以前节点

如下图所示, 在 newImage 中需要修改内容,然后合并到 master 中, 与下一个需求很像

git rebase -i c1 # 将 c3 与 c2 复制过来 得到c3', c2',然后修改 c2'
git commit --amend # 重新提交 c2' 得到 c2'' 
git rebase -i master # 将 c2'' 与 c3' 复制到新的分支中, 得到c2''',c3''
git checkout master
git rebase c3''

1543975775727

修改提交 3: 修改以前节点

修改 newImage ,然后合并到 master,与上一个需求很像

git checkout newImage # 移到 newImage 
git commit--anmend # 修改并重新提交 , 得到 c2'
git checkout master # 称到 master 
git cherry-pick c2' c3 # 将 c2' c3 复制到新节点

1543977233929

git tag

为节点取一个永久的名称

git tag <标签> <节点>  # 为 节点 为取一个标签名, 如果不设置节点名,则为 HEAD
git tag v1 C1 # 比如 git checkout C1, git tag v1 效果相同

git describe

查找离你最近的标签

git describe 的语法是:

git describe

可以是任何能被 Git 识别成提交记录的引用,如果你没有指定的话,Git 会以你目前所检出的位置(HEAD)。

它输出的结果是这样的:

<tag>_<numCommits>_g<hash>

tag 表示的是离 ref 最近的标签, numCommits 是表示这个 ref 与 tag 相差有多少个提交记录, hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。

当 ref 提交记录上有某个标签时,则只输出标签名称

# 下图中操作
git describe master # 输出如下:
v1_2_gC2
git describe side # 输出如下
v2_1_gC4

1543977929225

远程仓库

远程仓库的地址 <remote name>/<branch name> , 远程名称默认为 origin

git checkout o/master 
git commit # 注意这里 HEAD 与 o/master 进行了分离。因为 o/master 在远程, 如下图所示

1543982291613

git fetch

git fetch 做了些什么

git fetch 完成了仅有的但是很重要的两步:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 o/master)

git fetch 实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。

如果你还记得上一节课程中我们说过的,远程分支反映了远程仓库在你最后一次与它通信时的状态,git fetch 就是你与远程仓库通信的方式了!希望我说的够明白了,你已经了解 git fetch 与远程分支之间的关系了吧。

git fetch 通常通过互联网(使用 http://git:// 协议) 与远程仓库通信。

git fetch 不会做的事

git fetch 并不会改变你本地仓库的状态。它不会更新你的 master 分支,也不会修改你磁盘上的文件。

理解这一点很重要,因为许多开发人员误以为执行了 git fetch 以后,他们本地仓库就与远程仓库同步了。它可能已经将进行这一操作所需的所有数据都下载了下来,但是并没有修改你本地的文件。我们在后面的课程中将会讲解能完成该操作的命令 :D

所以, 你可以将 git fetch 的理解为单纯的下载操作

示意图

1543982448628

git fetch 高级参数

git fetch

git fetch :

git fetch origin foo

Git 会到远程仓库的 foo 分支上,然后获取所有本地不存在的提交,放到本地的 o/foo (这里是一个本地的 o/foo, 而不是本地的 foo 上 )上。

如果不指定 source 参数时 表示下载同名远程分支

git fetch origin :foo # 表示从远程下载 foo 分支,并在本地创建foo 分支

git pull

git pull 被认为是 git fetch + git merge 的简写

当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。也就是说就是你可以执行以下命令:

  • git cherry-pick o/master
  • git rebase o/master
  • git merge o/master
  • 等等

实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是我们要讲的 git pull

就是将远程分支 fetch 下来,然后再将改变进行 merge , 并不会将改变提交到远程

1543983077000

git push # 等于 git fetch  + git merge
git pull --rebase # 等于  git fetch + git rebase

把这个命令翻译过来就是:

切到本地仓库中的“master”分支,获取所有的提交,再到远程仓库“origin”中找到“master”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。

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

需要注意的是,因为我们通过指定参数告诉了 Git 所有它需要的信息, 所以它就忽略了我们所检出的分支的属性!

** 个人理解 **: 因为指定了参数 , 与当前所有分支无关。

git push origin :

git push origin foo^:master # 将本地 foo 的上一级推送到远程 origin/master

git pull 高级参数

git pull

git pull :

与 git fetch 相同,只是会再做一步 merge

git push

git pull 刚好相反。 git push 负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。一旦 git push 完成, 你的朋友们就可以从这个远程仓库下载你分享的成果了!

git push 高级参数

git push

git push origin master

如果不指定 source 参数时 表示删除远程分支

git push origin :foo # 表示删除远程的 foo 分支

综合操作

当远程已经有其他人进行了变更,则无法进行正常的 push 。这时可以先 git pull 然后 git rebase 或是 git merge 然后再进行 git push

关于 rebase 与 merge

关于 merge 与 rebase 的讨论。以下是关于 rebase 的优缺点:

优点:

  • Rebase 使你的提交树变得很干净, 所有的提交都在一条线上

缺点:

  • Rebase 修改了提交树的历史

比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。

一些开发人员喜欢保留提交历史,因此更偏爱 merge。而其他人(比如我自己)可能更喜欢干净的提交树,于是偏爱 rebase。仁者见仁,智者见智。 :D

远程跟踪分支

本地分支名与远程分支名默认是相同的。因为它们被设置为跟踪分支

# 比如日志中会显示
local branch "master" set to track remote branch "o/master"
  • git checkout -b totallyNotMaster o/master 创建本地分支totallyNotMaster 并跟踪远程分支 master
  • git branch -u o/master foo 将远程分支 master 与 本地分机 foo 进行绑定 ,如果不支持 foo ,则绑定当前本地分支