认识工作区(workspace),暂存区(index)stage区
本地仓库,远程仓库
git基本命令:git init,git status,git add,git commit,HEAD,git log
git diff,git checkout,git reset,git rm

请结合上图

工作区和暂存区

  1. 工作区 (workspace):受 git 管理的文件 / 文件夹,但未提交到暂存区
  2. 暂存区 (Index):也称为 stage 区,版本管理中间态

本地仓库和远程仓库

  1. 本地仓库 (Repository):本地分支,将于远程仓库建立联系
  2. 远程仓库 (Remote):如 github,gitee,远程 git 服务器(如公司局域网内自己搭建)

分支

分支:master 分支,dev 分支,feature 分支,release 分支,hotfix 分支等,在不同的分支做不同的事情,并在合适的时候合并分支。可参看git分支管理

基本命令

  1. 初始化:git init
  2. 添加文件:git add
  3. 提交文件:git commit
  4. 查看状态:git status
  5. diff 比较:git diff/git diff/git diff HEAD/git diff --cached
  6. 推送:git push
  7. 克隆:git clone
  8. 拉取:git pull

git从本地提交到远程仓库基本流程

  1. git init让本地工作目录受 git 管理,并自动创建本地唯一一个 master 分支,此时文件都处于工作区 (workspace)
  2. git status查看工作区文件状态
  3. 通过git add可将工作区受管理的文件提交到暂存区 (index)
  4. 通过git commit提交到本地仓库 (repository)
  5. 本地仓库可以通过git push推送文件到远程仓库 (remote)

git从远程拉取到本地基本流程

  1. git clone可直接从远程仓库克隆到本地,git 自动把本地的 master 分支和远程的 master 分支对应起来了,并且远程仓库的默认名称是 origin
  2. 通过git pull获取远程最新代码

看了上面的还不明白,接下来我们通过示例来加上理解

使用示例

初始化版本库 git init

git init:将 F:\gitTest 目录变成 git 可以管理的仓库

$ git init
Initialized empty Git repository in F:/gitTest/.git/

而且当前目录下多了一个.git目录,这个目录是 git 用来跟踪管理版本库的, 可以理解为 git 的数据库。删除后,git 就不能管理该目录了,慎删。

查看状态 git status

git status:查看工作区文件状态

$ touch readme.txt
$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        readme.txt
nothing added to commit but untracked files present (use "git add" to track)

可见当前文件的状态为红色untracked(未被跟踪的)(在 git bash 中的颜色显示),说白了,这个文件 git 当前不能跟踪管理,你爱怎么修改怎么修改,.git目录中该文件相关信息。

添加文件 git add

git add:提交文件到暂存区

$ git add readme.txt
$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   readme.txt

可见当前的文件的状态是绿色的新文件,并提示可以用git rm --cached <file>来撤销提交暂存区,回到unstage状态

提交 git commit

git commit: 提交文件到本地仓库

$ git commit -m '1 add a readme file'
[master (root-commit) 6f3789d] 1 add a readme file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 readme.txt

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

可见提交后无可提交的文件,当前为干净的工作区
-m "comment": 添加提交注释,必须且 comment 明确,便于查看

修改后再次提交git add 再 git commit

如修复 bug,我们就要修改文件,然后再次提交。

$ 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")

$ git add readme.txt
$ git commit -m '2 bug fixed'
[master 033d254] 2 bug fixed
 1 file changed, 1 insertion(+)

可以看出文件修改后,文件的状态变成修改状态 (modified),而且还要 add 再 commit

多个文件的提交git commit -a

如果改动了多个文件,一个个写名字很麻烦。加上-a/--all参数, 会将所有跟踪文件的修改暂存后并 commit

$ git commit -a -m '3 commit new file'
[master 8da3c81] 3 commit new file
 2 files changed, 2 insertions(+)
 create mode 100644 readme2.txt
 create mode 100644 readme3.txt

增补提交git commit --amend

若忘记提交某个文件,可使用增补提交,注意区别

$ git log
commit 8da3c811e5b6e4143b70fa052f44d8f4ca87bf07 (HEAD -> master)
Author: MonkeyJerry
Date:   Fri Dec 13 22:51:55 2019 +0800

    3 commit new file

commit 033d254f2d87d270861e679f9e85c338699ab407
Author: MonkeyJerry 
Date:   Fri Dec 13 22:04:52 2019 +0800

    2 bug fixed

commit 6f3789d90d85bfcadc18fc7444611883f9d23f4b
Author: MonkeyJerry 
Date:   Fri Dec 13 21:44:51 2019 +0800

    1 add a readme file
------------------------------------------------------------
$ touch readme4.txt
$ git add readme4.txt
$ git commit --amend -m '4 add readme4 file'
[master f7738ae] 4 add readme4 file
 Date: Fri Dec 13 22:51:55 2019 +0800
 3 files changed, 2 insertions(+)
 create mode 100644 readme2.txt
 create mode 100644 readme3.txt
 create mode 100644 readme4.txt
--------------------------------------------------------------
$ git log
commit f7738ae2a69f74fdc035483e3cb4405bfbb801d1 (HEAD -> master)
Author: MonkeyJerry 
Date:   Fri Dec 13 22:51:55 2019 +0800

    4 add readme4 file

commit 033d254f2d87d270861e679f9e85c338699ab407
Author: MonkeyJerry 
Date:   Fri Dec 13 22:04:52 2019 +0800

    2 bug fixed

commit 6f3789d90d85bfcadc18fc7444611883f9d23f4b
Author: MonkeyJerry 
Date:   Fri Dec 13 21:44:51 2019 +0800

    1 add a readme file
  • commit id: 类似这一串6f3789d90d85bfcadc18fc7444611883f9d23f4b就是 commit id,这个 id 是唯一的,可以唯一确定某次提交。
  • git log:查看版本库中提交的历史记录
    从上可以看出,--amend增补提交,通过创建一个新的 commit 来替代了当前分支的最新一次提交。
  • git reflog:查看版本库中所有次的提交历史,不区分是否增补
$ git reflog
f7738ae (HEAD -> master) HEAD@{0}: commit (amend): 4 add readme4 file
8da3c81 HEAD@{1}: commit: 3 commit new file
033d254 HEAD@{2}: commit: 2 bug fixed
6f3789d HEAD@{3}: commit (initial): 1 add a readme file

如上,我们可以看到所有的历史记录。

为什么要搞个暂存区

我们一般add后,立马又会commit,那为什么还要搞个暂存区呢?有必要吗。
有必要,有了暂存区,我们就可以选择性的把要提交的修改放进去,然后再一次性提交所有的修改。
比如 readme1 已经完成了,我们可以提交,但 readme2 还在开发中,并不需要提交。

$ git add readme1.txt
$ git status
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:   readme1.txt
        modified:   readme2.txt
$ git add readme1.txt
$ git commit -m 'add completed file readme1'
$ 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:   readme2.txt

no changes added to commit (use "git add" and/or "git commit -a")

什么是HEAD

从上面的打印,我们已经看到很多次HEAD了,HEAD可以看做是一个指针 / 游标,一般指向当前分支的最后一次提交。HEAD的值存在.git/HEAD
HEAD:最后一次 commit
HEAD^: 上一次提交
HEAD^^: 上上次提交
HEAD~n: 上 n 次提交

比较 git diff

比较暂存区,工作区以及已经 commit 到本地仓库的文件修改的差异

  • git diff: 比较暂存区和工作区的差异
  • git diff --cached: 比较暂存区和最后一次 commit 的差异
  • git diff HEAD: 比较工作区和最后一次 commit 的差异
# git diff 暂存区和工作区
# 在工作区修改了hello.py
$ 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:   hello.py

no changes added to commit (use "git add" and/or "git commit -a")
$ git diff
diff --git a/hello.py b/hello.py
index 00950d9..d3e46f2 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('Hello World')
\ No newline at end of file
# git diff HEAD最后一次commit和工作区
$ git diff HEAD
diff --git a/hello.py b/hello.py
index 00950d9..d3e46f2 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('Hello World')
\ No newline at end of file
# git diff --cached 暂存区和最后一次提交
# 添加这次修改到暂存区,然后比较暂存区和最后一次提交的差异
$ git add hello.py
$ git diff --cached
diff --git a/hello.py b/hello.py
index 00950d9..d3e46f2 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('Hello World')
\ No newline at end of file

检出git checkout 和 重置/回退git reset

git checkout 检出

检出,从哪里检出?暂存区 / commit 区。这个命令可以从暂存区或者 commit 区的内容来反悔用的。

  • git checkout:列出可以从暂存区检出的文件
  • git checkout /git checkout -- : 从暂存区检出文件并覆盖工作区,但暂存区不清除
  • git checkout .: 检出暂存区所有文件到工作区

提交到暂存区后反悔了

# 当前暂存区和工作区的区别
$ git diff
diff --git a/hello.py b/hello.py
index 00950d9..d3e46f2 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('Hello World')
\ No newline at end of file
-----------------------------------------
# 列出可以检出的文件
$ git checkout
M       hello.py
------------------------------------------
# 从暂存区检出覆盖工作区
$ git checkout hello.py
$ cat hello.py
print('hello world')
  • git checkout : 从 commit 区检出并覆盖暂存区和工作区
# 当前暂存区和commit区的区别
$ git diff --cached
diff --git a/hello.py b/hello.py
index 00950d9..d3e46f2 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('Hello World')
\ No newline at end of file

# 当前工作区和commit区的区别
$ git diff HEAD
diff --git a/hello.py b/hello.py
index 00950d9..d3e46f2 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('Hello World')
\ No newline at end of file

# 从commit区检出,覆盖工作区和暂存区
$ git checkout HEAD hello.py
# 检出后三区一致
$ git diff
$ git diff --cached
$ git diff HEAD

检出操作在工作中使用时请慎重,避免误覆盖

git reset 重置 / 回退

重置

重置,重置哪个区的文件?重置暂存区 / 工作区,看着很像git checkout,git checkout可以从暂存区或者commit区重置,而git reset只能从 commit 区来重置暂存区 / 工作区,都是用来反悔用的。主要用于版本回退。

  • git reset:列出将被 reset 的文件
  • git reset file:重置暂存区,用最后一次 commit 覆盖,工作区不影响
# 当前暂存区和commit区区别
$ git diff --cached
diff --git a/hello.py b/hello.py
index 00950d9..8ca3a26 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('hello monkeyjerry')
\ No newline at end of file

# 工作区和暂存区无区别
$ git diff
# 列出可重置的文件
$ git reset
Unstaged changes after reset:
M       hello.py

# 重置暂存区(用commit区)
$ git reset hello.py
Unstaged changes after reset:
M       hello.py
# 可见暂存区被commit区覆盖掉,当前无差异
$ git diff --cached
# 暂存区被覆盖后,而工作区无影响,则暂存区和暂存区存在差异
$ git diff
diff --git a/hello.py b/hello.py
index 00950d9..8ca3a26 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('hello world')
\ No newline at end of file
+print('hello monkeyjerry')
\ No newline at end of file

版本回退

  • git reset --hard HEAD/HEAD^/HEAD~1/:--hard强行重置暂存区和工作区,用指定commit 区的提交覆盖
# 当前有三次提交如下
$ git reflog
3ebb8c8 (HEAD -> master) HEAD@{0}: commit: append GPL
8c9a928 HEAD@{1}: commit: add distributed
75158ae HEAD@{2}: commit (initial): wrote a readme file
# 当前工作区和暂存区有区别
$ git diff
diff --git a/readme.txt b/readme.txt
index 99e0e11..a69519e 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,3 @@
 Git is a distributed version control system.
-Git is free software distributed under the GPL.
\ No newline at end of file
+Git is free software distributed under the GPL.
+modify workspace.
\ No newline at end of file

# 回退到add distributed版本,用HEAD^方式
$ git reset --hard HEAD^
HEAD is now at 8c9a928 add distributed
# 可见--hard同时覆盖了工作区,使得暂存区和工作区都被add distributed版本覆盖
$ git diff

# 回退到wrote a readme file版本,用commit id方式
# commit id可通过git reflog获得
$ git reset --hard 75158ae
HEAD is now at 75158ae wrote a readme file
$ git diff

删除文件 git rm

  • git rm --cached :将文件从暂存区删除,也就是说,我提交到暂存区后,反悔了
# 当前是未被跟踪的文件
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        hello.py

nothing added to commit but untracked files present (use "git add" to track)

# 提交到暂存区
$ git add hello.py
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   hello.py
# 反悔了git rm --cached
$ git rm --cached hello.py
rm 'hello.py'
# 回到未被追踪的状态
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        hello.py

nothing added to commit but untracked files present (use "git add" to track)
  • 手动删除反悔了,怎么办?git checkout 从暂存区 / commit 区检出。如果没有,无能无力。
  • 手动删除,真的想删除,git status一直提示 deleted 文件,再 commit 一次即可
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    readme.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ git commit readme.txt -m 'deleted file'
[master 59c051e] deleted file
 1 file changed, 2 deletions(-)
 delete mode 100644 readme.txt
$ git status
On branch master
nothing to commit, working tree clean

文件的几种状态

通过上面的练习,我们发现对于文件,无非增加,删除,修改,git status查看当前工作区的文件状态

  • Untracked:增加一个文件,未 add 到 stage 区
  • deleted:删除了的工作区文件
  • modified:修改了已经 add 或者 commit 的文件
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        file_status

nothing added to commit but untracked files present (use "git add" to track)
-----------------------------------------------------------------------------
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    readme.txt
------------------------------------------------------------------------------
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