1. Git 是什么可以吃吗?

Git 是一个分布式版本控制系统,由Linus Torvalds 创作,他也是Linux 内核的最早作者。Git 的命名很有意思,该词源自英语俚语,意思大约是「混账」,是Linus Torvalds 的自嘲:

“I’m an egotistical bastard, and I name all my projects after myself. Frist Linux, now git.“

2. 人人都爱版本控制系统

以一份需要多方共同维护的产品需求文档为例,如果没有版本控制,你很可能被管理多个文件副本的恐惧支配,”我昨天和今天存的这两份文件有什么不一样?” “把文件邮件给程序猿、设计狮,他们发来了文件的修改版,这两个修改版怎么合并在一起?”

(图片From 廖雪峰的Git教程

最终你们决定不再使用副本,在同一份文件上工作,并手动记录下每次更新的内容。

版本 用户 说明 日期
1 产品汪 创建PRD,描述产品“核心功能1-5” 2016/06/12 03:04
2 设计狮 添加了部分功能的交互效果 2016/07/09 12:09
3 程序猿 删除“核心功能2-5” 2016/07/09 16:51
4 产品汪 恢复“核心功能2-5” 2016/07/12 01:51

然而,这一份独一无二的文件存储在哪里?如何保证每次只有一名同事在对它进行编辑?能否自动记录每次编辑的内容?如果一次编辑出错了,能否回滚到编辑前的版本?

Linux 的创始人Linus 也面临同样的问题,世界各地的人都在为Linux 编写代码,如何管理这些代码?

事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手 工方式合并代码!

附录:Git的诞生

3. 分布式版本控制系统:每个节点都是完整的版本库

  • 集中式(SVN、CVS、VSS):版本库存放在「中央服务器」

  • 分布式(Git):没有「中央服务器」,每个人电脑上都有完整的版本库

    集中式的服务器挂了所有人都挂了,因为完整仓库只存在服务器上,分布式如果github挂了你可以重新建一个服务器,然后把任何一个人的仓库clone过去

3. 安装Git

4. 创建一个版本库init add commit

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

  • 4.1 mkdir 创建一个空目录(选择已有文件的目录也可以):
1
2
3
$ mkdir learngit
$ cd learngit
$ pwd
  • 4.2 git init 把目录变成Git 可以管理的仓库:
1
2
$ git init
Initialized empty Git repository in /Users/breaking_red/learngit/.git/

可以看到testgit 目录下生成了隐藏文件夹.git,ls -ah 可以查看隐藏文件

  • 4.3 将文件添加至仓库:
  1. ~/learngit 目录(或其子目录)下创建文件readme.txt, 将默认编码设置为UTF-8

    1
    2
    Git is a version control system.
    Git is free software.
  2. git add 告诉Git,将文件添加到仓库:

1
$ git add readme.txt
  1. git commit 告诉Git,将文件提交到仓库,-m "xxx" 是对本次提交内容的说明:
1
2
3
4
$ git commit -m "wrote a readme file"
[master (root-commit) 1e4e95e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt

commit 命令返回:更新1个文件,插入2行内容

可以一次 commit 多个文件,即多次 add 一次 commit

1
2
3
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files"

5. 哪些文件被修改过status 修改了什么diff

修改 readme.txt 中的内容:

1
2
Git is a distributed version control system.
Git is free software.
  • 5.1 在 ~/testgit 下运行 git status 查看仓库当前的状态:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ git status
    On branch master
    Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    modified: readme.txt
    no changes added to commit (use "git add" and/or "git commit -a")
  • 5.2 git diff 比较未提交至仓库的文件与已提交至仓库的文件的不同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ git diff
    diff --git a/readme.txt b/readme.txt
    index d8036c1..013b5bc 100644
    --- a/readme.txt
    +++ b/readme.txt
    @@ -1,2 +1,2 @@
    -Git is a version control system.
    +Git is a distributed version control system.
    Git is free software.
    \ No newline at end of file

6. 选择重置到你喜欢的版本log reset —hard reflog

再次修改 readme.txt 并提交至Git:

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 c5eb549] append GPL
1 file changed, 1 insertion(+), 1 deletion(-)
  • 6.1 使用 git log 查看 readme.txt 的版本记录:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $ git log
    commit c5eb5497c67f4752bb53d34db54060fa99fc44ac
    Author: Breakingred <837423954@qq.com>
    Date: Wed Apr 19 16:38:07 2017 +0800
    append GPL
    commit 792a5afcc0efc6af27f530184f42369cd6159894
    Author: Breakingred <837423954@qq.com>
    Date: Wed Apr 19 16:36:49 2017 +0800
    add distributed
    commit 1e4e95e625fe4af8b35315044692cd48aa695a73
    Author: Breakingred <837423954@qq.com>
    Date: Wed Apr 19 16:33:26 2017 +0800
    wrote a readme file

    注意, 5 中的修改没有 commit ,所以版本记录中只有两次的 commit 记录

    加上 --pretty=oneline 获得版本号( commit id) + 版本信息的输出:

    1
    2
    3
    4
    $ git log --pretty=oneline
    c5eb5497c67f4752bb53d34db54060fa99fc44ac append GPL
    792a5afcc0efc6af27f530184f42369cd6159894 add distributed
    1e4e95e625fe4af8b35315044692cd48aa695a73 wrote a readme file
  • 6.2 使用 git reset 将文件重置到需要的版本,当前版本用 head 表示,上一个版本 head^ ,上上个版本 head^^, 再上一个版本 head^3 ,现在讲版本回滚到 add distributed

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

    cat 查看 readme.txt 内容:

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

    再看 git log

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ git log
    commit 792a5afcc0efc6af27f530184f42369cd6159894
    Author: Breakingred <837423954@qq.com>
    Date: Wed Apr 19 16:36:49 2017 +0800
    add distributed
    commit 1e4e95e625fe4af8b35315044692cd48aa695a73
    Author: Breakingred <837423954@qq.com>
    Date: Wed Apr 19 16:33:26 2017 +0800
    wrote a readme file

    append GPL 不见了!

  • 6.3 只要窗口没有关闭,可以找到 append GPL 的版本号(版本号可不写全)并恢复:

    1
    2
    $ git reset --hard c5eb5497c67f4752bb53d34db5406
    HEAD is now at c5eb549 append GPL

    这样就又回到 append GPL 版本了,万一窗口关闭了也不必担心没有了 commit idreflog 会记录你的每一次命令,丢失的版本号又找回来啦:

    1
    2
    3
    4
    5
    6
    $ git reflog
    c5eb549 HEAD@{0}: reset: moving to c5eb5497c67f4752bb53d34db5406
    792a5af HEAD@{1}: reset: moving to HEAD^
    c5eb549 HEAD@{2}: commit: append GPL
    792a5af HEAD@{3}: commit: add distributed
    1e4e95e HEAD@{4}: commit (initial): wrote a readme file

7. 从工作区到暂存区的原理

来自:廖雪峰的Git教程

可见的目录如 learngit 就是工作区,工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

  • 在工作区修改文件 readme.txt ,新建文件 LICENSE ,检查状态 get status

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ git status
    On branch master
    Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    modified: readme.txt
    Untracked files:
    (use "git add <file>..." to include in what will be committed)
    LICENSE
    no changes added to commit (use "git add" and/or "git commit -a")

    readme.txt 被修改了,而 LICENSE 还从来没有被添加过,所以它的状态是 Untracked

  • add 将工作区文件修改添加至暂存区,检查状态 get status

    1
    2
    3
    4
    5
    6
    7
    $ git status
    On branch master
    Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)
    new file: LICENSE
    modified: readme.txt

  • commit 将暂存区文件提交至当前分支,目前提交至Git自动创建的唯一分支 master ,检查状态 get status

    1
    2
    3
    $ git status
    On branch master
    nothing to commit, working tree clean