2016.12.4
虽然14年开发第一个项目时就已经接触了git,当时xcode已经开始支持git,在github上面建立的仓库,然后使用xcode去合并代码,不过后来都在独立开发项目,对git的使用算不上熟练,上周五加入了响巢看看大家庭,以后工作中会经常使用git,所以今天就来熟练一下git。主要途径是搜索引擎搜索到的一个不错的入门教程。
安装Git
xcode自带git,也可以通过homebrew,或者去github上面搜索一个安装脚本运行一下,或者直接下载git安装
创建版本库
版本库又名仓库,英文名repository,我们比着上面推荐给大家的入门教程来做一遍,首先创建路径,然后初始化版本库,这时候我们会在目标路径下生成一个".git"文件
注意:Git 能跟踪文本文件的改动,而音视频,图片等二进制文件的改变Git只能跟踪文件大小的变动,无法跟踪具体的变化细节,Word格式是二进制格式,版本控制系统是没法跟踪Word文件的改动的,所以我们要以纯文本方式编写文件
添加文件到版本库
现在我们添加一个txt文件进入我们的git仓库,主要用到两个操作,add与commit。ok,我们来具体操作一下。
说明:-m 这个参数是本次提交的详细说明,可以不加,但是最好带着。
修改提交
这个阶段主要用到的操作有status,是查看git当前的状态,diff,告知git详细的改变,提交与之前一样,add与commit,来,我们比着教程做一下。
版本回退
前面已经修改提交,但是如果你后悔了,怎么回退之前的版本呢,如果回退后又反悔了呢,哈哈,好纠结。这个部分主要用的的操作有log,log用来打印我们各个版本的一个列表,可以通过传递--pretty=oneline参数使显示格式更加简约,在每次commit之前都有一串字符串,这个我们叫做commitid,在git里面有一个被称作HEAD的指针,HEAD指针指向的commit就是我们当前操作的commit,我们可以通过操作HEAD的指向来对我们当前版本进行切换,从而实现回退,主要的操作是reset --hard HEAD^ 或者是 <commitid>,commitid不需要全部写完,这里只是用来筛选,写五个左右就足够了,HEAD^代表HEAD指向的前一个,HEAD^^代表指向的前第二个,HEAD~100,代表指向的前第100个。还有一个叫做reflog的操作,打印的是HEAD位置发生改变的操作的列表。下面我们比着教程来做一下。
工作区与暂存区
工作区(Working Directory)就是你在电脑里能看到的目录,这里msgit文件夹就是一个工作区。
版本库(Repository)工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
修改撤销
主要用到的命令是 git checkout -- file,这个命令就是让这个文件回到最近一次git commit或git add时的状态,git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令。命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区,当我们用HEAD时,表示最新的版本。
删除文件
删除文件主要用到的命令是git rm,之后要记得git commit。如果说我们在工作区错误的删除了某一个文件,我们可以通过git checkout -- <filename> 来把误删的文件恢复到最新版本。下面我们来实际操作一下。
远程仓库
接下来我们要在GitHub上实现我们的远程仓库
下面这一段是引用
第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:
点“Add Key”,你就应该看到已经添加的Key:
为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
最后友情提示,在GitHub上免费托管的Git仓库,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放进去。
如果你不想让别人看到Git库,有两个办法,一个是交点保护费,让GitHub把公开的仓库变成私有的,这样别人就看不见了(不可读更不可写)。另一个办法是自己动手,搭一个Git服务器,因为是你自己的Git服务器,所以别人也是看不见的。这个方法我们后面会讲到的,相当简单,公司内部开发必备。
小明去查看了本地目录并没有.ssh目录,然后在终端输入$ ssh-keygen -t rsa -C "我的邮箱"确实就出现了.ssh目录,依据上面的步骤,在GitHub上面添加好了sshkey,需要说明的是.ssh是隐藏目录,我们需要在终端输入defaults write com.apple.finder AppleShowAllFiles YES就可以查看隐藏文件了
添加远程库(引用)
现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。
首先,登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库:
在Repository name填入learngit,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库:
目前,在GitHub上的这个learngit仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
现在,我们根据GitHub的提示,在本地的learngit仓库下运行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git
请千万注意,把上面的michaelliao替换成你自己的GitHub账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表中。
添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:
$ git push -u origin master
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (19/19), 13.73 KiB, done.
Total 23 (delta 6), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样:
从现在起,只要本地作了提交,就可以通过命令:
$ git push origin master
把本地master分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!
SSH警告
当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告:
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。
Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.
这个警告只会出现一次,后面的操作就不会有任何警告了。
如果你实在担心有人冒充GitHub服务器,输入yes前可以对照GitHub的RSA Key的指纹信息是否与SSH连接给出的一致。
接下来我们自己做一遍
从远程库克隆
这个应该是多人协作时一定会用到的
我们新建一个名字叫做hahaha的远程仓库。接下来我们使用git clone。
一个git仓库,有https地址和ssh地址两种,根据自己的需要去选择就可以,一般情况下,两者都可以,在这里查看
下面是我运行的结果
分支
分支常用的命令。查看分支:git branch 创建分支:git branch <name> 切换分支:git checkout <name> 创建+切换分支:git checkout -b <name> 合并某分支到当前分支:git merge <name> 删除分支:git branch -d <name>
分支策略(引用)
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
Bug分支
通常我们在工作中接到了修复某个bug的任务,这时候我们就需要来创建一个bug分支来修复我们的bug,但是如果当前我们手中的工作尚未完成怎么办呢,git提供了一个操作可以保存当前的工作现场 stash,等以后方便恢复现场继续工作
我们可以通过git stash list命令查看当前保存的工作现场。
使用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除,另一种方式是用git stash pop,恢复的同时把stash内容也删了
多人协作(引用)
当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。
要查看远程库的信息,用git remote:
$ git remote
origin
或者,用git remote -v显示更详细的信息:
$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)
上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
推送分支(引用)
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
$ git push origin master
如果要推送其他分支,比如dev,就改成:
$ git push origin dev
但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
抓取分支(引用)
多人协作时,大家都会往master和dev分支上推送各自的修改。
现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:
$ git clone git@github.com:michaelliao/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 46, done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 46 (delta 16), reused 45 (delta 15)
Receiving objects: 100% (46/46), 15.69 KiB | 6 KiB/s, done.
Resolving deltas: 100% (16/16), done.
当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:
$ git branch
* master
现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:
$ git checkout -b dev origin/dev
现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:
$ git commit -m "add /usr/bin/env"
[dev 291bea8] add /usr/bin/env
1 file changed, 1 insertion(+)
$ git push origin dev
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 349 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
fc38031..291bea8 dev -> dev
你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:$ git add hello.py $ git commit -m "add coding: utf-8"[dev bd6ae48] add coding: utf-8 1 file changed, 1 insertion(+)$ git push origin devTo git@github.com:michaelliao/learngit.git ! [rejected] dev -> dev (non-fast-forward)error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. Merge the remote changes (e.g. 'git pull')hint: before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details.推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:$ git pullremote: Counting objects: 5, done.remote: Compressing objects: 100% (2/2), done.remote: Total 3 (delta 0), reused 3 (delta 0)Unpacking objects: 100% (3/3), done.From github.com:michaelliao/learngit fc38031..291bea8 dev -> origin/devThere is no tracking information for the current branch.Please specify which branch you want to merge with.See git-pull(1) for details git pullIf you wish to set tracking information for this branch you can do so with: git branch --set-upstream dev origin/git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:
$ git branch --set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.
再pull:
$ git pull
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.
这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:
$ git commit -m "merge & fix hello.py"
[dev adca45d] merge & fix hello.py
$ git push origin dev
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 747 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
291bea8..adca45d dev -> dev
因此,多人协作的工作模式通常是这样:
首先,可以试图用git push origin branch-name推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!
如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
小结
查看远程库信息,使用git remote -v;
本地新建的分支如果不推送到远程,对其他人就是不可见的;
从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;
在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;
建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name;
从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。