终端运行:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
出现错误:
Press RETURN to continue or any other key to abort
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/. /usr/local/etc /usr/local/share/man/man3 /usr/local/share/man/man5 /usr/local/share/man/man7 /usr/local/share/info /usr/local/share/doc
Password:
==> /usr/bin/sudo /usr/sbin/chown andy /usr/local/. /usr/local/etc /usr/local/share/man/man3 /usr/local/share/man/man5 /usr/local/share/man/man7 /usr/local/share/info /usr/local/share/doc
==> /usr/bin/sudo /bin/mkdir /Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Library/Caches/Homebrew
==> /usr/bin/sudo /usr/sbin/chown andy /Library/Caches/Homebrew
==> Downloading and installing Homebrew...
remote: Counting objects: 3770, done.
remote: Compressing objects: 100% (3609/3609), done.
remote: Total 3770 (delta 43), reused 539 (delta 26), pack-reused 0
Receiving objects: 100% (3770/3770), 3.21 MiB | 568.00 KiB/s, done.
Resolving deltas: 100% (43/43), done.
From https://github.com/Homebrew/homebrew
* [new branch] master -> origin/master
error: unable to unlink old 'Library/Aliases/0mq' (Permission denied)
error: unable to unlink old 'Library/Aliases/4store' (Permission denied)
error: unable to unlink old 'Library/Aliases/Secret Rabbit Code' (Permission denied)
error: unable to create symlink Library/Aliases/actor-framework (Permission denied)
error: unable to create symlink Library/Aliases/adwaita-icon-theme (Permission denied)
error: unable to unlink old 'Library/Aliases/ag' (Permission denied)
error: unable to unlink old 'Library/Aliases/alut' (Permission denied)
error: unable to unlink old 'Library/Aliases/android' (Permission denied)
error: unable to unlink old 'Library/Aliases/apache-activemq' (Permission denied)
error: unable to unlink old 'Library/Aliases/apache-fop' (Permission denied)
error: unable to create symlink Library/Aliases/atlassian-cli (Permission denied)
Last login: Sat Oct 10 11:09:55 on ttys003
zhong:~ andy$ cd /usr/local/
zhong:local andy$ sudo mv -v Library Library.old
Password:
Library -> Library.old
zhong:local andy$
成功Press RETURN to continue or any other key to abort
==> /usr/bin/sudo /bin/mkdir /Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Library/Caches/Homebrew
==> /usr/bin/sudo /usr/sbin/chown andy /Library/Caches/Homebrew
==> Downloading and installing Homebrew...
remote: Counting objects: 3770, done.
remote: Compressing objects: 100% (3609/3609), done.
remote: Total 3770 (delta 43), reused 539 (delta 26), pack-reused 0
Receiving objects: 100% (3770/3770), 3.21 MiB | 478.00 KiB/s, done.
Resolving deltas: 100% (43/43), done.
From https://github.com/Homebrew/homebrew
* [new branch] master -> origin/master
HEAD is now at 6014053 disktype: fix bottle misplacement
==> Installation successful!
==> Next steps
Run `brew help` to get started
zhong:GIT andy$ mkdir Excel2Json
zhong:GIT andy$ cd Excel2Json/
zhong:Excel2Json andy$ pwdpwd:查看当前路径
/Volumes/Macintosh HD/Work/GIT/Excel2Json
zhong:Excel2Json andy$ git init
Initialized empty Git repository in /Volumes/Macintosh HD/Work/GIT/Excel2Json/.git/
zhong:Excel2Json andy$
zhong:Excel2Json andy$ ls -ahls -ah:可查看隐藏的文件
. .. .git
第一步,用命令git add
告诉Git,把文件添加到仓库:
$ git add readme.txt
执行上面的命令,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。
第二步,用命令git commit
告诉Git,把文件提交到仓库:
$ git commit -m "wrote a readme file"
[master (root-commit) cb926e7] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
-
要随时掌握工作区的状态,使用
git status
命令。 -
如果
git status
告诉你有文件被修改过,用git diff
可以查看修改内容。
-
HEAD
指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
。 -
穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本如果嫌输出信息太多,看得眼花缭乱的,可以试试加上
--pretty=oneline
参数:$ git log --pretty=oneline 3628164fb26d48395383f8f31179f24e0882e1e0 append GPL ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file
Git必须知道当前版本是哪个版本,在Git中,用
HEAD
表示当前版本,也就是最新的提交3628164...882e1e0
(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。现在,我们要把当前版本“append GPL”回退到上一个版本“add distributed”,就可以使用
git reset
命令:$ git reset --hard HEAD^ HEAD is now at ea34578 add distributed
commit id
是3628164...
,于是就可以指定回到未来的某个版本:$ git reset --hard 3628164 HEAD is now at 3628164 append GPL
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
-
要重返未来,用
4.工作区和暂存区git reflog
查看命令历史,以便确定要回到未来的哪个版本。
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。
工作区(Working Directory)
就是你在电脑里能看到的目录,比如我的Excel2Json文件夹就是一个工作区:
版本库(Repository)
工作区有一个隐藏目录
.git
,这个不算工作区,而是Git的版本库。Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支
master
,以及指向master
的一个指针叫HEAD
。第一步是用
git add
把文件添加进去,实际上就是把文件修改添加到暂存区;第二步是用
git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。git checkout -- file
可以丢弃工作区的修改:撤销修改
$ git checkout -- readme.txt
命令
git checkout -- readme.txt
意思就是,把readme.txt
文件在工作区的修改全部撤销,这里有两种情况:一种是
readme.txt
自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;一种是
readme.txt
已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。总之,就是让这个文件回到最近一次
git commit
或git add
时的状态。修改小结
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git checkout -- file
。场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令
git reset HEAD file
,就回到了场景1,第二步按场景1操作。场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
命令
git rm
用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。5.添加远程库
已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。
自行注册GitHub账号。由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
第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
文件的内容:
zhong:~ andy$ ssh-keygen -t rsa -C "zjia5@qq.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/andy/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/andy/.ssh/id_rsa.
Your public key has been saved in /Users/andy/.ssh/id_rsa.pub.
The key fingerprint is:
eb:73:17:e8:2f:a5:b7:4a:4c:e8:c0:22:4f:92:f1:66 zjia5@qq.com
The key's randomart image is:
+--[ RSA 2048]----+
| |
| |
| . |
| + . . |
| + E o S .. |
| B . o +. o |
| . o.oo . |
| ...= o |
| .o.*o. |
+-----------------+
zhong:~ andy$ ls -a
..configDesktop
...gitconfigDocuments
.CFUserTextEncoding.gitignore_globalDownloads
.DS_Store.hgignore_globalLibrary
.Trash.idlercMovies
.android.localMusic
.bash_history.sshPictures
.bash_profile.subversionPublic
.bash_profile.pysave.viminfodebug.keystore
.bash_profile.save.yunpanelipse_workspace
.bash_profile.swpApplicationsfarm_kona.keystore
.cacheCocos Studio未命名文件夹
zhong:~ andy$ cd .ssh/
zhong:.ssh andy$ ls
id_rsaid_rsa.pubknown_hosts
zhong:.ssh andy$ vi id_rsa.pub
zhong:.ssh andy$
把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
git remote add origin https://github.com/Mr-Zhong/Excel2Json.git
远程库的名字就是
origin
,这是Git默认的叫法,也可以改成别的,但是origin
这个名字一看就知道是远程库。把本地仓库的内容推送到GitHub仓库。 git push -u origin master
zhong:.ssh andy$ cd /Volumes/Macintosh\ HD/Work/GIT/Excel2Json
zhong:Excel2Json andy$ git remote add origin https://github.com/Mr-Zhong/Excel2Json.git
zhong:Excel2Json andy$ git push -u origin master
Username for 'https://github.com': zjia5@qq.com
Password for 'https://zjia5@qq.com@github.com':
Counting objects: 21, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (21/21), 3.00 MiB | 491.00 KiB/s, done.
Total 21 (delta 0), reused 0 (delta 0)
To https://github.com/Mr-Zhong/Excel2Json.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
zhong:Excel2Json andy$
从现在起,只要本地作了提交,就可以通过命令:
$ git push origin master
把本地
6。克隆远程仓库master
分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!$ git clone git@github.com:michaelliao/gitskills.git
要克隆一个仓库,首先必须知道仓库的地址,然后使用
git clone
命令克隆。Git支持多种协议,包括
https
,但通过ssh
支持的原生git
协议速度最快。GitHub给出的地址不止一个,还可以用
https://github.com/michaelliao/gitskills.git
这样的地址。实际上,Git支持多种协议,默认的git://
使用ssh,但也可以使用https
等其他协议。使用
7.分支https
除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh
协议而只能用https
。分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。
但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
首先,我们创建
dev
分支,然后切换到dev
分支:$ git checkout -b dev Switched to a new branch 'dev'
git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:$ git branch dev $ git checkout dev Switched to branch 'dev'
然后,用
git branch
命令查看当前分支:$ git branch * dev master
git branch
命令会列出所有分支,当前分支前面会标一个*
号。然后,我们就可以在
dev
分支上正常提交,比如对readme.txt做个修改,加上一行:Creating a new branch is quick.
然后提交:
$ git add readme.txt $ git commit -m "branch test" [dev fec145a] branch test 1 file changed, 1 insertion(+)
现在,
dev
分支的工作完成,我们就可以切换回master
分支:$ git checkout master Switched to branch 'master'
切换回
master
分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变:现在,我们把
dev
分支的工作成果合并到master
分支上:$ git merge dev Updating d17efd8..fec145a Fast-forward readme.txt | 1 + 1 file changed, 1 insertion(+)
git merge
命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev
分支的最新提交是完全一样的。注意到上面的
Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。当然,也不是每次合并都能
Fast-forward
,我们后面会将其他方式的合并。合并完成后,就可以放心地删除
dev
分支了:$ git branch -d dev Deleted branch dev (was fec145a).
删除后,查看
branch
,就只剩下master
分支了:$ git branch * master
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在
master
分支上工作效果是一样的,但过程更安全。小结
Git鼓励大量使用分支:
查看分支:
git branch
创建分支:
git branch <name>
切换分支:
git checkout <name>
创建+切换分支:
git checkout -b <name>
合并某分支到当前分支:
git merge <name>
删除分支:
git branch -d <name>
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用
git log --graph
命令可以看到分支合并图。8.创建标签
在Git中打标签非常简单,首先,切换到需要打标签的分支上:
$ git branch * dev master $ git checkout master Switched to branch 'master'
然后,敲命令
git tag <name>
就可以打一个新标签:$ git tag v1.0
可以用命令
git tag
查看所有标签:$ git tag v1.0
默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
方法是找到历史提交的commit id,然后打上就可以了:
$ git log --pretty=oneline --abbrev-commit 6a5819e merged bug fix 101 cc17032 fix bug 101 7825a50 merge with no-ff 6224937 add merge 59bc1cb conflict fixed 400b400 & simple 75a857c AND simple fec145a branch test d17efd8 remove test.txt ...
比方说要对
add merge
这次提交打标签,它对应的commit id是6224937
,敲入命令:$ git tag v0.9 6224937
再用命令
git tag
查看标签:$ git tag v0.9 v1.0
注意,标签不是按时间顺序列出,而是按字母排序的。可以用
git show <tagname>
查看标签信息:$ git show v0.9 commit 622493706ab447b6bb37e4e2a2f276a20fed2ab4 Author: Michael Liao <askxuefeng@gmail.com> Date: Thu Aug 22 11:22:08 2013 +0800 add merge ...
可以看到,
v0.9
确实打在add merge
这次提交上。还可以创建带有说明的标签,用
-a
指定标签名,-m
指定说明文字:$ git tag -a v0.1 -m "version 0.1 released" 3628164
用命令
git show <tagname>
可以看到说明文字:$ git show v0.1 tag v0.1 Tagger: Michael Liao <askxuefeng@gmail.com> Date: Mon Aug 26 07:28:11 2013 +0800 version 0.1 released commit 3628164fb26d48395383f8f31179f24e0882e1e0 Author: Michael Liao <askxuefeng@gmail.com> Date: Tue Aug 20 15:11:49 2013 +0800 append GPL
还可以通过
-s
用私钥签名一个标签:$ git tag -s v0.2 -m "signed version 0.2 released" fec145a
签名采用PGP签名,因此,必须首先安装gpg(GnuPG),如果没有找到gpg,或者没有gpg密钥对,就会报错:
gpg: signing failed: secret key not available error: gpg failed to sign the data error: unable to sign the tag
如果报错,请参考GnuPG帮助文档配置Key。
用命令
git show <tagname>
可以看到PGP签名信息:$ git show v0.2 tag v0.2 Tagger: Michael Liao <askxuefeng@gmail.com> Date: Mon Aug 26 07:28:33 2013 +0800 signed version 0.2 released -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin) iQEcBAABAgAGBQJSGpMhAAoJEPUxHyDAhBpT4QQIAKeHfR3bo... -----END PGP SIGNATURE----- commit fec145accd63cdc9ed95a2f557ea0658a2a6537f Author: Michael Liao <askxuefeng@gmail.com> Date: Thu Aug 22 10:37:30 2013 +0800 branch test
用PGP签名的标签是不可伪造的,因为可以验证PGP签名。验证签名的方法比较复杂,这里就不介绍了。
-
命令
git tag <name>
用于新建一个标签,默认为HEAD
,也可以指定一个commit id; -
git tag -a <tagname> -m "blablabla..."
可以指定标签信息; -
git tag -s <tagname> -m "blablabla..."
可以用PGP签名标签; -
命令
git tag
可以查看所有标签。
操作标签
如果标签打错了,也可以删除:
$ git tag -d v0.1 Deleted tag 'v0.1' (was e078af9)
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令
git push origin <tagname>
:$ git push origin v1.0 Total 0 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git * [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags Counting objects: 1, done. Writing objects: 100% (1/1), 554 bytes, done. Total 1 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git * [new tag] v0.2 -> v0.2 * [new tag] v0.9 -> v0.9
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
$ git tag -d v0.9 Deleted tag 'v0.9' (was 6224937)
然后,从远程删除。删除命令也是push,但是格式如下:
$ git push origin :refs/tags/v0.9 To git@github.com:michaelliao/learngit.git - [deleted] v0.9
要看看是否真的从远程库删除了标签,可以登陆GitHub查看。
9.搭建Git服务器
GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。
搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的
apt
命令就可以完成安装。假设你已经有
sudo
权限的用户账号,下面,正式开始安装。第一步,安装
git
:$ sudo apt-get install git
第二步,创建一个
git
用户,用来运行git
服务:$ sudo adduser git
第三步,创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的
id_rsa.pub
文件,把所有公钥导入到/home/git/.ssh/authorized_keys
文件里,一行一个。第四步,初始化Git仓库:
先选定一个目录作为Git仓库,假定是
/srv/sample.git
,在/srv
目录下输入命令:$ sudo git init --bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以
.git
结尾。然后,把owner改为git
:$ sudo chown -R git:git sample.git
第五步,禁用shell登录:
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑
/etc/passwd
文件完成。找到类似下面的一行:git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,
git
用户可以正常通过ssh使用git,但无法登录shell,因为我们为git
用户指定的git-shell
每次一登录就自动退出。第六步,克隆远程仓库:
现在,可以通过
git clone
命令克隆远程仓库了,在各自的电脑上运行:$ git clone git@server:/srv/sample.git Cloning into 'sample'... warning: You appear to have cloned an empty repository.
剩下的推送就简单了。
管理公钥
如果团队很小,把每个人的公钥收集起来放到服务器的
/home/git/.ssh/authorized_keys
文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。
管理权限
有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。
这里我们也不介绍Gitolite了,不要把有限的生命浪费到权限斗争中。
小结
-