作者:Rhythm-2019
邮箱:rhythm_2019@163.com
最近更新:2020.01.03

感觉好像两三个月没写博客了,最近一直要去实验室做毕设,之前在Gitee上看见一个项目Learn Git Branching,是一个可视化的Git学习平台。所以元旦找了两天来学习一下。虽然觉得以后工作可能面对的问题会更复杂一些,也没有说教程里面这么直观,但是打好基础嘛,大家也可以去体验一下

基础指令

  • git commit

    对比新旧仓库文件的内容产生一份提交记录,新的提交记录指向旧的提交记录

  • git branch

    在当前最新提交记录中创建一个新的副本,以新的名称命名

  • git checkout

    用于切换分支,git checkout -b可以创建并切换到新的分支

  • git marge <引用>

    当前分支和新分支合并,在当前分支上产生一个提交记录,该提交记录指向两个分支的上一个节点

    合并分支

    由于当前master上包含了所有的提交,再次合并Git什么都不会做

    继续合并

  • git rebase

    让两个分支合体,让其看上去像是线性开发的(实际上是并行的)

    Rebase

注意,git rebase <ref>是将HEAD指向记录及其之前的记录复制到ref指定位置上

操作分支

下面介绍一些分支上的操作

HEAD

我们首先看一下 “HEAD”。 HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。

HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。

HEAD 通常情况下是指向分支名的(如 bugFix)。在你提交时,改变了 bugFix 的状态,这一变化通过 HEAD 变得可见。
分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。在命令执行之前的状态如下所示:

HEAD -> master -> C1

HEAD 指向 master, master 指向 C1

输入git checkout C1

HEAD -> C1

对于不同的提交记录,我们可以通过git log知道他的哈希值,然后使用git checkout指向后进行操作AD

个人对HEAD和分支的理解:我觉得他们都像是一个指针,他们都可以指向提交记录,但是HEAD甚至可以指向分支,我们可以使用git checkout <分支>对HEAD进行分离,分离后分支就不随着HEAD的移动而移动了,包括提交移动

相对引用

我们使用git log会查到每次提交的哈希值,因为哈希值比较长,如果我们向快速让HEAD指向上一次或几次提交,可以直接使用下面两个操作符号

  • ^ 上一个父节点(如果有两个父节点请使用^1、^2来表示)
  • ~ 上几个父节点,如上两个父节点可以使用~2表示

这两个符号支持连锁操作

相对引用

操作分支

我们现在可以使用checkout轻松操作HEAD了,我们同样可以使用git branch -f <分支> <提交记录>,让某个分支回到某个记录上,例如下面的练习,操作右边的HEAD和分支是的他变成左边红色底色的模样

Git branch练习

我的答案是

git branch -f master C6
git checkout HEAD^
git branch -f bugFix HEAD^

撤销分支

如果在本地,我们可以使用git reset <目标提交记录>对当前提交记录的下一个记录及后面的所有记录进行撤回操作,如果你需要对远程仓库进行操作,则需要使用git revert进行撤销,撤销后会产生一个新的提交记录便于你进行提交

大家可以从下面的描述中看出两者区别

reset撤回本地

大家要注意HEAD的位置,reset后的参数是指目标位置

revert撤回远程

总结一下就是reset只对本地进行撤销,revert针对远程仓库进行撤销

移动提交记录

使用git charry-pick <记录1> <记录2>...可以快速将提交记录复制到当前分支位置

当然啦,当你需要复制的记录比较多,你可以使用交互式的rebase完成,指令为git rebase -i <目标引用>,Git会弹出一个选择窗口,他会让你编辑当前HEAD指向及以前的记录供你进行编辑,编辑好后帮你复制到目标引用后面

交互式Rebase

当你的项目出现了BUG你想要把他修复,你在代码里面加了很多输出语句,提交了一次记录,修改好BUG在提交一次。显然你是希望将第二次提交放入master,这时就需要使用git cherry-pickgit rebase -i

移动提交记录使用场景

我们总结一下rebase的用法吧:

  • git rebase <ref> 将当前的HEAD指向的提交记录(包含之前的记录)复制到ref后面
  • git rebase <to> <from>form指向的记录(包括之前的)复制到to后面
  • git rebase -i <ref> 与第一个指令一样,只是提供了UI界面可以删除中间的某些记录

实战练习

对于我们刚刚学习的两个指令

  • git cherry-pick <record...>
  • git rebase -i <record>

在配合上我们之前学习的

  • git checkout <record>
  • git branch -f <分支> <record>

我们可以轻松的完成所有的提交记录变更

练习1

例如下面这个练习,在我们开发中也很常见,就是程序员1完成了图片代码编写并创建newImage分支提交了自己的代码,程序员2在newImage分支基础上创建了新的分支caption并提交了自己新的代码,现在主管觉得程序员1的代码写的有问题,需要进行修改并整合程序员2的代码

实战练习1

所以我的指令是

git rebase -i master # 调整C2 C3的顺序使得C2是最新的
git commit --amend   # 更新C2
git rebase -i master # 将C2 C3调整回来
git branch -f master caption # 让master指向caption的指向

如果程序员1和程序员2开发不同的模块,这样调整没什么问题,但是如果他们对同一个代码进行了修改可能会造成代码冲突

更好的应该是使用cherry-pick,指令如下

git checkout master # 让HEAD指向maser指向的记录
git cherry-pcik newImage # 复制newImage到master
git commit --amend # 更新newImage
git cherry-pick caption # 复制caption

你会发现使用cherry-pick后,分支的指向都会及时更新

练习2

再如需要你合并下面所有分支的内容

rebase练习2

答案是

git rebase master bugFix
git rebase bugFix side
git rebase side another 
git rebase naster another  # 更新master到最新

练习3

产生下面三个分支

练习3

治理只能使用cherry-pick复制了,答案是:

git checkout one
git cherry-pick C4 C3 C2
git checkout two
git cherry-pick C5 C4 C3 C2
git branch -f three C2

里程碑

你会发现分支和HEAD都会随着提交而移动,当项目做到一定程度要发一个大版本的时候吗,你希望用一个标签表示这个大版本的提交记录的话,可以使用git tag <标签> <引用>来标识,该标签永远指向该提交记录,因此被称为里程碑

Git还提供了一个指令git describe <ref>,用于查询距离该提交最近的里程碑和距离他的步长

里程碑

远程仓库

由于我们现在需要进行协作开发,所以代码需要放在仓库进行管理,远程仓库有下面两个特点,我直接引用教程的话啦

  • 首先也是最重要的的点, 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 你仍可以通过远程仓库拿回你丢失的数据。
  • 还有就是, 远程让代码社交化了! 既然你的项目被托管到别的地方了, 你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)

克隆项目

我们可以使用git clone url克隆一个项目

远程分支

与本地分支不同,远程分支一般以<remote_name><branch_name>命名,remote_name一般是origin表示

当你使用git checkout o/master的时候会自动分离出HEAD,对于本地分支则会让HEAD指向master,这是对远程分支的一种保护,即远程分支不能被移动

远程分支反映了远程仓库在你最后一次与它通信时的状态,我们可以从远程仓库中获取数据或提交数据

获取数据

我们可以使用git fetch获取最新的仓库,fetch会帮助我们完成两件事

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

Git Fetch指令

聪明的你可能以及发现了本地分支master没有更新,fetch只会更新远程分支

这其实非常常见,比如我写某部分代码写了比较久,同事在我提交之前提交了更新的代码,那我应该这样做

  • git fetch 更新o/master分支内容
  • git merge o/master 将最新代码合并到当前我本地的最新提交中

大家可以看下面两幅图,图一是还没输入指令之前

合并最新代码1

执行后

合并最新代码2

其实Git提供了pull指令就是合并了这两条指令

提交代码

我们可以使用git push提交你的代码,代码同步完成后,远程分支会自动更新到最新状态

但是一般情况是你上传代码时发现同时比你先提交但你又没及时拉下最新的代码,我们需要先将把最新的代码拉下来,然后调整一下顺序

git fetch
git rebase o/master

提交代码1

如果你只是简答的使用git pull,再push的话效果如下

提交代码2

这样远程仓库的代码记录会比较乱,所以大家还是先使用git fetch来下最新的代码,然后使用rebase调整顺序后再push,其中

git fetch
git rebase o/master

可以简写成git pull --rebase

例如这里需要你合并所有的分支并提交

rebase提交代码

我们只需要使用rebase合并就好

git fetch
git rebase o/master side1
git rebase side1 side2
git rebase side2 side3
git rebase side3 master
git push

有同学可能会问能不能使用merge来调整rebase,其实肯定是可以的,rebase有自己的优缺点

  • 优点:线性整洁
  • 缺点:修改了提交顺序

merge提交push 有里两个参数,第一个是remote的名称,默认origin,第二个是你需要提交的分支名称,你一定见过这个

git push origin master

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

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

Git还提供设置提交映射的方式,girt push orgin <本地>:<远程>,这样我们可以将自己的分支直接提交到master上面

git push设置映射关系

其实git fetch也有类似的功能,git fetch origin <远程>:<本地>可以指定跟新哪个分支

fetch参数

注意:如果你使用git fetch origin master这种写法移动的是远程分支o/master,如果你使用的是 `git fetch origin <远程>:<本地>的话移动的是本地分支

git pull也有类似的功能,大家可以去摸索一下

这两个命令还有两个特殊用法:

git psuh origin :分支 # 删除本地和远程分支
git fetch origin :分支 # 创建本地分支

远程跟踪

其实我也在好奇Git怎么就知道master对应o/master的呢?原来是你执行git clone的时候就已经默认绑定了,也就是本地分支master跟踪远程分支o/master。我们有没有办法创建自己的分支,让自己的分支也跟着o/master呢?

  • git checkout -b <新分支> o/master
  • git branch -u o/master <新分支>

删除文件

平时我大手大脚的不小心把一些不该提交的东西提交了该怎么办

  • 如果你只是使用git add加入了暂存区,你可以使用git rm及时删除文件
  • 如果你已经commit了,你需要撤销这次commit,这个下面讲
  • 如果你已经push了,则需要执行下面命令

    git rm -r --cached a/2.txt //删除a目录下的2.txt文件 删除a目录git rm -r --cached a
    git commit -m "删除a目录下的2.txt文件"

    用-r参数删除目录,git rm --cached a.txt删除的是本地仓库中的文件,且本地工作区的文件会保留且不再与远程仓库发生跟踪关系,如果本地仓库中的文件也要删除则用git rm a.txt

撤销文件操作

这路分几种情况哈

  1. 当你在工作区修改了某个文件,然后突然想撤回到刚拉下来的时候的墨阳

    git checkout -- filename

  2. 如果你修改文件后add到暂存区了需要撤回到最初的样子

    git reset HEAD filename

  3. 现在你已经commit了,但你想回去

    根据上面所学,这里可以直接撤销这次提交,然后再从暂存区中撤回,所以命令是

    git reset --hard filename

  4. 已经push

    git revert HEAD^

  5. 撤回完后悔了,又想回去

    git reflag # 查出刚刚指向指令的coomitId
    git reset -hard <commit_id>

值得一提的是commit -a可以直接将这次提交追加到上一次中

参考


Rhythm_2019
48 声望3 粉丝