Git 简介

最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑。不过,慢慢地有人把它移植到了Windows上。现在,Git可以在Linux、Unix、Mac和Windows这几大平台上正常运行了。

要使用Git,第一步当然是安装Git了。根据你当前使用的平台来阅读下面的文字:

在Linux上安装Git

首先,你可以试着输入git,看看系统有没有安装Git:

1
2
3
4
$ git
The program 'git' is currently not installed.
You can install it by typing:
sudo apt-get install git

如果你碰巧用 Debian 或 Ubuntu Linux,通过一条 sudo apt-get install git 就可以直接完成 Git 的安装,非常简单。

老一点的 Debian 或 Ubuntu Linux,要把命令改为 sudo apt-get install git-core,因为以前有个软件也叫 GIT(GNU Interactive Tools),结果 Git 就只能叫 git-core 了。由于 Git 名气实在太大,后来就把 GNU Interactive Tools 改成 gnuit,git-core 正式改为 git。

如果是其他Linux版本,可以直接通过源码安装。先从 Git 官网下载源码,然后解压,依次输入:./config,make,sudo make install 这几个命令安装就好了。

在Mac OS X上安装Git

如果你正在使用Mac做开发,有两种安装Git的方法。

一是安装 homebrew,然后通过 homebrew安装Git,具体方法请参考homebrew 的文档:http://brew.sh/。

第二种方法更简单,也是推荐的方法,就是直接从 AppStore 安装 Xcode,Xcode 集成了 Git,不过默认没有安装,你需要运行 Xcode,选择菜单 “Xcode”->“Preferences”,在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了。

创建版本库

什么是版本库呢?版本库又名仓库,英文名 repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被 Git 管理起来,每个文件的修改、删除,Git 都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

所以,创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录:

1
2
$ mkdir learngit
$ cd learngit

第二步,通过 git init 命令把这个目录变成 Git 可以管理的仓库:

1
2
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/

瞬间 Git 就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个 .git 的目录,这个目录是 Git 来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把 Git 仓库给破坏了。

如果你没有看到 .git 目录,那是因为这个目录默认是隐藏的,用 ls -ah 命令就可以看见。

言归正传,现在我们编写一个 readme.txt 文件,内容如下:

1
2
Git is a version control system.
Git is free software.

一定要放到 learngit 目录下(子目录也行),因为这是一个Git仓库,放到其他地方 Git 再厉害也找不到这个文件。

和把大象放到冰箱需要3步相比,把一个文件放到 Git 仓库只需要两步。

  • 第一步,用命令 git add 告诉Git,把文件添加到仓库:
1
$ git add readme.txt

执行上面的命令,没有任何显示,这就对了,Unix 的哲学是“没有消息就是好消息”,说明添加成功。

  • 第二步,用命令 git commit 告诉 Git,把文件提交到仓库:
1
2
3
4
$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt

版本回退

现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改 readme.txt 文件如下:

1
2
Git is a distributed version control system.
Git is free software distributed under the GPL.

然后尝试提交:

1
2
3
4
$ git add readme.txt
$ git commit -m "append GPL"
[master 1094adb] append GPL
1 file changed, 1 insertion(+), 1 deletion(-)
1
2
3
4
5
6
7
8
9
10
11
版本1:wrote a readme file
Git is a version control system.
Git is free software.

版本2:add distributed
Git is a distributed version control system.
Git is free software.

版本3:append GPL
Git is a distributed version control system.
Git is free software distributed under the GPL.

不断的修改之后,所有有不同的版本。当然了,在实际工作中,我们脑子里怎么可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令查看。git log 命令显示从最近到最远的提交日志,

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上–pretty=oneline参数:

1
$ git log --pretty=oneline

需要友情提示的是,你看到的一大串类似 1094adb…的是 commit id(版本号),和 SVN 不一样,Git 的 commit id 不是 1,2,3…… 递增的数字,而是一个 SHA1 计算出来的一个非常大的数字,用十六进制表示,而且你看到的 commit id 和我的肯定不一样,以你自己的为准。为什么 commit id 需要用这么一大串数字表示呢?因为 Git 是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用 1,2,3……作 为版本号,那肯定就冲突了。

每提交一个新版本,实际上 Git 就会把它们自动串成一条时间线。如果使用可视化工具,例如 Sourcetree 查看 Git 历史,就可以更清楚地看到提交历史的时间线。

好了,现在我们启动时光穿梭机,准备把readme.txt回退到上一个版本。

首先,Git 必须知道当前版本是哪个版本,在 Git 中,用 HEAD 表示当前版本,也就是最新的提交 1094adb…(注意我的提交ID和你的肯定不一样),上一个版本就是 HEAD^,上上一个版本就是 HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成 HEAD~100。

1
2
$ git reset --hard HEAD^
HEAD is now at e475afc add distributed

–hard参数有啥意义?这个后面再讲。

看看 readme.txt 的内容是不是版本add distributed。

1
2
3
$ cat readme.txt
Git is a distributed version control system.
Git is free software.

果然被还原了。

1
2
3
4
5
6
7
版本1:wrote a readme file
Git is a version control system.
Git is free software.

版本2:add distributed
Git is a distributed version control system.
Git is free software.

最新的那个版本append GPL已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?

办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个 append GPL 的commit id是1094adb…,于是就可以指定回到未来的某个版本:

1
2
$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL:

现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的 commit id 怎么办?

在Git中,总是有后悔药可以吃的。当你用 $ git reset --hard HEAD^ 回退到add distributed 版本时,再想恢复到 append GPL,就必须找到append GPL的commit id。Git 提供了一个命令 git reflog 用来记录你的每一次命令:

1
2
3
4
5
$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file

终于舒了口气,从输出可知,append GPL的commit id是1094adb,现在,你又可以乘坐时光机回到未来了。


撤销修改

1. 本地修改,未 git add

假如你对本地的 readme.txt 做了修改,但是还未 git add,这时候你想要删除刚才的修改,一种方法是手动删除你的修改,另一种方法是利用以下命令:

1
$ git checkout -- readme.txt

命令 git checkout -- readme.txt 意思就是,把 readme.txt 文件在工作区的修改全部撤销。git checkout 其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

2. 本地修改,已经 git add

假如你对本地的 readme.txt 做了修改,而且已经 git add 把修改添加到了暂存区,还未提交。这时候,我们可以用命令 git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区:

1
$ git reset HEAD readme.txt

这个命令相当于撤销 git add,之前的修改统统回到工作区,如果要撤销工作区的修改,用 1 中的 git checkout 就可以了。

3. 本地修改,已经 git add而且提交

这时候,只能用版本回退的方法,退回到旧版本。

  • HEAD指向的版本就是当前版本,因此,Git 允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id

  • 穿梭前,用 git log 可以查看提交历史,以便确定要回退到哪个版本。

  • 要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。

git 本地版本库管理

1
2
3
4
5
6
7
8
9
10
11
$ git status 查看工作区状态
$ git add 将工作区的修改添加到暂存区
$ git commit -m “” 将暂存区添加到版本库

$ git log 版本状态
$ git log --pretty=oneline 版本信息 单行显示
$ git reflog 所有 git 操作记录
$ git reset --hard HEAD^ 版本回退
$ git reset --hard 3628164 版本回退,版本号为:3628164
$ git checkout -- readme.txt 撤销修改/删除,用版本库里的版本替换工作区的版本 (自修改后还没有被放到暂存区,e.g. 还未 git add)
$ git reset HEAD readme.txt 撤销修改/删除(若修改后已经 git add)

git 仓库管理

第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

    $ ssh-keygen -t rsa -C "youremail@example.com"

你需要把邮件地址换成你自己的邮件地址,一路回车,使用默认值即可。如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

第2步:登陆GitHub,打开“Account settings”,然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容

github 添加库

登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库learngit。

$ git remote add origin git@github.com:fangbin/learngit.git

添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。

$ git push -u origin master

把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

从现在起,只要本地作了提交,就可以通过命令:

$ git push origin master

github 从远程库克隆

登陆GitHub,创建一个新的仓库,名字叫gitskills

远程库已经准备好了,下一步是用命令git clone克隆一个本地库:

$ git clone git@github.com:fangbin/gitskills.git