跟着游戏学一点Git用法

之前看到一个公众号文章分享了Github上的一个资源玩游戏一样学 Git 分支,这个 GitHub 热门别再错过了,才发现自己对于Git的理解只停留在:其是一个仓库,然后pull and push,over;因此正好借助着这个闯关学习Git资源,就试着玩玩,从头了解下如何用Git进行版本控制

Git基本命令(本地仓库操作)

git commit

git commit不会每次都复制整个目录,而是在条件允许下,将当前版本和上一个版本做比较,取其中有差异的部分打包到一个记录中,因此提交记录非常轻量,并有一定的父子层级关系

git branch/checkout

git branch 创建分支,相当于基于这个提交以及其所有的父提交进行后续的开发;而一般我们是在主分支(master)上进行的,创建分支后,我们需要从主分支切换到分支上,那么需要用git checkout命令

git branch bugFix
git checkout bugFix

如果想创建分支和切换分支一起进行,则git checkout -b bugFix,这样相当于在当前分支下创建一个bugFix分支并切换

git merge/rebase

git merge用于合并两个分支,git merge bugFix相当于将分支bugFix合并到主分支上,git checkout bugFix; git merge master则是将主分支master合并到分支FugFix上

除了git merge来合并分支外,还有一种用git rebase

Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去(相当于创建了一个副本,原来的还是保留着)。使得合并后看起来更加线性

git rebase bugFix相当于将master主分支(当前分支)rebase到bugFix分支上


在提交树上移动有下面几种方法:

  • HEAD 总是指向当前分支上最近一个提交记录,cat .git/HEAD查看HEAD指向;HEAD一般都是指向分支名,如bugFix,但也可通过git checkout name来指向某次提交记录,这里name是提交记录的哈希值,可通过git log来查看提交记录的哈希值
  • 提交哈希值不太方便,那么可以使用相对引用来移动,如^向上移动1个提交记录、~3则向上移动3个提交记录,一般加在引用名称的后面git checkout master^,也可以将HEAD作为相对引用的参照git checkout HEAD^
  • 如果想移动很多步就用git checkout HEAD~3,将分支强制移动到某个位置git branch -f master HEAD~3或者git branch -f master name(将master主分支移到name位置)
  • 如返回合并提交的上一个父节点:git checkout master^,Git 默认选择合并提交的“第一个”父提交,但可以通过git checkout master^2来返回合并提交的“第二个”父提交,如果想快速移动可以用链式操作:git checkout HEAD~^2~2,比如像在上某一个父提交创建一个分支bugWork,则git branch bugWork HEAD~^2~

对已有的提交记录进行撤销变更:

  • git reset通过回退来实现变更,如git reset HEAD~,这样就返回上一个提交记录,而原来那个就不存在了;这种方法一般在自己本地使用
  • 如果想撤销变更推送到远程仓库分享给别人,我们需要用git revert HEAD,这样会生成一个新提交(引用了更改信息,用于撤销),所以这个主要用于远程分支

如果想单独提取某个或者某几个提交记录到某个分支下(或者对其做一些改动),可以用下述方法:

  • cherry-pick可以将一些提交记录复制到当前所在位置(HEAD)下面,如:git cherry-pick name,name指你的提交记录或者其哈希值;或者是多个提交:git cherry-pick name1 name2 name3
  • 如果不清楚提交记录的哈希值,也可以通过交互式的rebase,可以让你从一系列的提交记录中找到想要的记录,如git rebase -i HEAD~4,表示对之前的4个提交进行修改(调换位置、删除以及合并等)

git commit --amend可以用来修改上一次提交,合并你的修改和上一次提交,而不是提交一个新的记录(记住amend不是修改最近一次commit, 而是整个替换掉他. 对于Git来说是一个新的commit),还可以用来编辑上一次的提交(只要将想要修改的提交挪到最前端的位置)

Git的tag可以用于永久的标记某个特定的提交,然后像分支一样引用;比如给某个提交标记为版本1:git tag v1 namegit describe配合tag标签使用,可以用来描述离你最近的锚点:git describe <ref>,ref是提交记录的引用或者HEAD位置,那么输出就是离ref最近标签的描述(__g,其中tag标签,numCommits表示ref和tag相差多少个提交记录,hash则是ref的哈希值,当ref上刚好有标签时,则输出标签名称)

Github(远程仓库操作)

git clone

git clone复制远程仓库到本地后,本地仓库会多一个分支(origin/master),即为远程分支

远程分支有一个特别的属性,在你检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。

git fetch

git fetch可以用远程仓库获取数据,如果将远程仓库的数据下载到本地仓库后,本地的远程分支会被更新,即:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 origin/master),因此git fetch是本地与远程仓库的通信方式
  • git fetch并不会改变本地仓库的状态,不会更新master分支,即单纯的下载操作
  • git fetch不加任何参数的话,则会下载所有提交记录到各个本地远程分支上

fetch参数:git fetch <remote> <place>,如git fetch origin foo,则表示到远程仓库的foo分支上,获取所有本地仓库不存在的提交,并放到本地origin/foo上;至于为什么是到本地origin/foo分支而不是本地foo分支,是因为正如上面所说,fetch不改变本地仓库的状态

当然也是可以直接更新本地分支的(这个方法理论上是可行的,但是一般不会做这样的操作。。。),如git fetch origin foo:bar,表示将远程仓库的foo分支的提交放到本地origin/bar分支上,如果bar不存在则会自动新建个bar分支

git pull

对于从远程仓库抓到数据到本地仓库然后将远程分支合并到本地分支这步骤(相当于git fetchgit merge),可以用一个命令来实现:git pull

pull参数:由于pull是fetch和merge的缩写,所以fetch支持的参数都可适用于pull,比如:git pull origin foo相当于git fetch origin foo; git merge o/foo,merge相当于将更新的origin/foo合并到检出位置(一般可能就是master);类似的还有git pull origin master:foo这种形式

git push

上传远程仓库的命令则是git push,即将你的变更上传到指定的远程仓库中,并且合并提交记录;同时远程分支和本地仓库的master分支也随之同步

push参数:git push <remote> <place>,如git push origin master,则表示:

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

如果想将foo分支推送到远程仓库的bar分支中,如:git push origin foo:bar;如果bar分支不存在,则Git会在远程仓库中创建一个bar分支

如果用git push origin :foo这种方式,则会删除远程仓库中的foo分支;类比git fetch origin :bar则会在本地创建一个bar分支

push会强制合并远程仓库的最新状态,所以当你本地落后于远程仓库的提交的话,就会失败;解决办法是通过rebase来调整当前工具是基于最新的远程分支,如:git fetch更新本地仓库的远程分支,然后用git rebase或者git merge将合并,再git push;但最为方便的是则是git pull --rebase或者git pullgit push,这个就是我们最常用最熟悉的操作了~


为什么操作远程分析的时候不太常用merge:Rebase 使你的提交树变得很干净, 所有的提交都在一条线上;但Rebase 修改了提交树的历史(而merge则对于历史可溯源)

如果想让任意分支来跟踪origin/master,那么方法如下:

  • 通过远程分支检出一个新的分支:git checkout -b foo origin/master,foo则是一个新分支来跟踪远程分支origin/master;值得注意的是,接着git pull后本地仓库的master分支是不会被更新的;如果用git push则是将totallyNotMaster分支推送到远程仓库的master分支上
  • 通过设置远程分支:git branch -u origin/master,则是将当前分支来跟踪origin/master,也可以指定某个分支git branch -u origin/master foo

蛮好玩的一个交互式学习 Git 分支的“游戏”

git_learning

本文出自于http://www.bioinfo-scrounger.com转载请注明出处