xuzhengfu / pilot

进入编程世界的第一课
1 stars 0 forks source link

x3-git-github Git 与 GitHub 入门 #18

Open xuzhengfu opened 4 years ago

xuzhengfu commented 4 years ago

1. 了解 git 的一些最核心的基本概念和术语

1.1 了解 “工作目录”

工作目录(working directory)就是我们工作的目录,这个目录以及下面所有的子目录和文件就是我们操作的对象,在 git 环境下,工作目录是我们实际操作的、文件系统里真正存在的对象。

1.2 了解 “仓库”

仓库(repository,经常被简称为 repo)是 git 为我们建立和维护的版本历史数据库,这个数据库躲在一个叫 .git 的隐藏目录下,通常我们不需要直接操作它,git 命令会帮助我们来操作它。

1.3 了解 “暂存区域”

暂存区域(staging area)则是工作目录和仓库之间的中间过渡区,有时也叫索引(index),不过我们觉得“暂存区域”的叫法更清晰易懂。当我们在工作目录里做了一些修改之后,可能只想把一部分修改作为一个版本提交到仓库里保存下来,这时候就可以选择几个修改先放到暂存区域,然后再把暂存区域里的修改提交到版本仓库。

1.4 了解 “检取”

如果我们想检取(checkout)某个历史版本来操作,那么只要提供给版本号,git 就从仓库里找到对应版本然后把工作目录变成那个版本的样子,我们就可以在这个版本上工作了。

1.5 了解 “本地仓库和远程仓库”

假设 A 创建一个 git 版本仓库,将仓库的访问地址告诉 B,B 就可以“克隆(clone)”这个版本仓库到自己的电脑中(当然前提是网络连通且具有相应的权限),而且这两个仓库可以随时同步各自的修改变化。

那么如果有很多人呢?通常我们会设置一台电脑作为公共仓库服务器,一个团队成员(通常是负责管理版本的人)创建(init)好仓库然后“推送(push)”到公共仓库服务器上,这个服务器上的仓库就成为团队共享的仓库(team repo),其他人就可以克隆 team repo 到自己的电脑,team repo 成为是所有人公共的“(origin)”。

如此这般之后,所有人都可以在自己电脑上工作,在自己的本地仓库中建立新的版本,并在需要时把本地仓库中的版本 推送(push) 到 team repo,也可以从 team repo 抓取(pull) 其他人提交的版本到自己的本地仓库。如果这个过程中产生了冲突(conflict),比如两个人提交的两个版本中有对同一个文件的不相容的修改,有多种选项来进行解决。

2. 跟着指引一步步地操作来熟悉 git 的基本用法

2.1 确认已经按照之前的指引配置好了你的系统

2.2 在使用之前对 git 进行一些基本设置

git config --global user.name "zhengfu"
git config --global user.email "791546968@qq.com"
git config --global core.editor "code -w"
git config --list

结果如下:

$ git config --list
credential.helper=osxkeychain
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
user.name=zhengfu
user.email=791546968@qq.com
core.editor=code -w

2.3 基本操作

cd ~/Code
mkdir learn-git
cd learn-git
pwd

最后一个 pwd 命令会打印出你当前所在目录供确认,这就是我们的工作目录(working directory),里面还没有文件。

$ git init
Initialized empty Git repository in /Users/xuzhengfu/Code/learn-git/.git/

这个命令成功后会提示已经在当前目录下的 .git 子目录下初始化了一个空的 repo。

结果如下:

$ ls -la
total 0
drwxr-xr-x  3 xuzhengfu  staff   96 Feb 10 14:35 .
drwxr-xr-x  6 xuzhengfu  staff  192 Feb 10 14:30 ..
drwxr-xr-x  9 xuzhengfu  staff  288 Feb 10 14:37 .git

这个 .git 目录下就是我们的版本仓库,使用 git 的特定数据格式保存。这也就意味着,我们的工作目录已经成为一个“由 git 进行版本控制”的目录,我们在这里做的任何事情都会被 git 跟踪,也可以作为版本提交到 repo 中去。

$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)
touch README.md
code README.md
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    README.md

nothing added to commit but untracked files present (use "git add" to track)
git add README.md
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   README.md
$ git commit
hint: Waiting for your editor to close the file... 

回到命令行可以看到执行结果:

[master (root-commit) 5c59fbd] testing of first committing
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
On branch master
nothing to commit, working tree clean

hello.py:

print('Hello world!')

today.py:

from datetime import datetime
print(datetime.now().strftime('今天是%Y年%m月%d日'))
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    hello.py
    today.py

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

这个命令中的 . 表示将当前目录及其下面变化的所有文件都加入到 staging area。

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   README.md
    new file:   hello.py
    new file:   today.py
git restore --staged README.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   hello.py
    new file:   today.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md
$ git commit
[master 6b9e427] add hello.py today.py
 2 files changed, 3 insertions(+)
 create mode 100644 hello.py
 create mode 100644 today.py
xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on master [!]
$ git add README.md

xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on master [+]
$ git commit
[master d23e083] update README
 1 file changed, 2 insertions(+), 1 deletion(-)
$ git status
On branch master
nothing to commit, working tree clean

2.4 查看版本的历史记录

$ git log
commit d23e08336f508002fde876dd2acc95fd564fbca7 (HEAD -> master)
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:24:39 2020 +0800

    update README

commit 6b9e427f2fbf1732fde22b340f8509c25c1a4642
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:21:22 2020 +0800

    add hello.py today.py

commit 5c59fbd1ebe8bb056de850080dbd70d3b3cc96fe
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 15:02:26 2020 +0800

    testing of first committing
git checkout 5c59fbd

结果如下:

$ git checkout 5c59fbd
Note: switching to '5c59fbd'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 5c59fbd testing of first committing
$ git checkout master
Previous HEAD position was 5c59fbd testing of first committing
Switched to branch 'master'
git diff 5c59fbd
$ git diff 5c59fbd
diff --git a/README.md b/README.md
index 7f118ff..e8233a7 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
-Hello, Git!
\ No newline at end of file
+Hello, Git!
+this is the first change to README.md.
\ No newline at end of file
diff --git a/hello.py b/hello.py
new file mode 100644
index 0000000..735bdcc
--- /dev/null
+++ b/hello.py
@@ -0,0 +1 @@
+print('Hello world!')
\ No newline at end of file
diff --git a/today.py b/today.py
new file mode 100644
index 0000000..26624e2
--- /dev/null
+++ b/today.py
@@ -0,0 +1,2 @@
+from datetime import datetime
+print(datetime.now().strftime('今天是%Y年%月%d日'))
\ No newline at end of file
git diff 5c59fbd 6b9e427

结果如下:

$ git diff 5c59fbd 6b9e427
diff --git a/hello.py b/hello.py
new file mode 100644
index 0000000..735bdcc
--- /dev/null
+++ b/hello.py
@@ -0,0 +1 @@
+print('Hello world!')
\ No newline at end of file
diff --git a/today.py b/today.py
new file mode 100644
index 0000000..26624e2
--- /dev/null
+++ b/today.py
@@ -0,0 +1,2 @@
+from datetime import datetime
+print(datetime.now().strftime('今天是%Y年%月%d日'))
\ No newline at end of file

3. 了解非常重要的分支(branch)概念及其在协同中的用法

tag 和 branch 其实都是指向 commit 的指针,区别在于 tag 指向一个固定的 commit,而 branch 会随着你的操作始终跟着最新的 commit。

git tag v1.0
git tag v0.9 6b9e427

标签(tag)给一个 commit 打上标记,帮助我们记忆,方便我们使用,而分支(branch)更进一步,能帮助我们标记一串 commit,是并行开发(或者叫并行创作)的必备工具。

并行开发就是要做的事有不止一条线索,有的人在做一条线,有的人在做另一条线;或者一个人的一段时间在做一条线,另一段时间在做另一条线。

举个例子来说,一个产品的 v1.0 版本上线之后,开始继续开发 v2.0 的新功能,这是一条线;当发现 v1.0 的一些错误时(不论自己发现还是用户报告),就需要立刻处理立刻修复,这时候不可能在新功能开发的基础上去修问题,而是要在 v1.0 那个版本基础上去修复,修复好的版本 v1.0.1 发布到生产环境上。这样新功能开发和老功能修错互不影响,到合适时候把 v1.0.1 的修改合并到 v2.0 的代码中就好了。

git 的理念是高度鼓励使用分支的,凡要做点新事情都可以开分支,在不影响其他人和其他任务的情况下用一个独立的分支去试,试好了再合并回主分支就好了,那么 git 是怎么做到的呢?

在 git 中,branch 和 tag 一样,都是指向 commit 的指针,区别在于 tag 是固定的,而 branch 是会移动的。

每个 git 版本仓库创建时都会创建一个 branch 叫 master,即主分支,在我们创建其他 branch 之前,这个主分支会跟着我们提交的 commit 走,永远指向最新的一个 commit。

现在假定进展到某一个时刻,在主分支之外需要一个另外的分支来修某个特定的 bug,于是创建了一个新分支叫 bug/fix1,这时候实际上就有两个 branch 指针指向同一个 commit,那么问题就来了,我们下一次提交 commit 之后,这两个指针会怎么走?都跟着最新 commit 走吗?这么“亦步亦趋”还叫什么分支呢?

这时候那个前面露过一面的 HEAD 就出现了,这也是一个指针,不过这是指向一个 branch 的指针,HEAD 指向哪个 branch,哪个 branch 就会跟着我们的最新 commit 走。

git branch bug/fix1
$ git log
commit d23e08336f508002fde876dd2acc95fd564fbca7 (HEAD -> master, bug/fix1)
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:24:39 2020 +0800

    update README

commit 6b9e427f2fbf1732fde22b340f8509c25c1a4642
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:21:22 2020 +0800

    add hello.py today.py

commit 5c59fbd1ebe8bb056de850080dbd70d3b3cc96fe
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 15:02:26 2020 +0800

    testing of first committing
xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on master
$ git checkout bug/fix1
Switched to branch 'bug/fix1'

xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on bug/fix1

这时候 HEAD 指向的 bug/fix1 分支指针就会跟着我们提交的新 commit 走了。 ls

$ git status
On branch bug/fix1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on bug/fix1 [!]
$ git add README.md

xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on bug/fix1 [+]
$ git status
On branch bug/fix1
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   README.md

xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on bug/fix1 [+]
$ git commit
[bug/fix1 9caf512] fix bug.
 1 file changed, 2 insertions(+), 1 deletion(-)

xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on bug/fix1
$ git status
On branch bug/fix1
nothing to commit, working tree clean
$ git log
commit 9caf51201b5451b850330579e6456cf3b805a547 (HEAD -> bug/fix1)
Author: zhengfu <791546968@qq.com>
Date:   Tue Feb 11 10:55:08 2020 +0800

    fix bug.

commit d23e08336f508002fde876dd2acc95fd564fbca7 (master)
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:24:39 2020 +0800

    update README

commit 6b9e427f2fbf1732fde22b340f8509c25c1a4642
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:21:22 2020 +0800

    add hello.py today.py

commit 5c59fbd1ebe8bb056de850080dbd70d3b3cc96fe
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 15:02:26 2020 +0800

    testing of first committing
xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on bug/fix1
$ git checkout master
Switched to branch 'master'

xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on master
xuzhengfu at xuzhengfudeiMac in ~/Code/learn-git on master
$ git log
commit d23e08336f508002fde876dd2acc95fd564fbca7 (HEAD -> master)
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:24:39 2020 +0800

    update README

commit 6b9e427f2fbf1732fde22b340f8509c25c1a4642
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 16:21:22 2020 +0800

    add hello.py today.py

commit 5c59fbd1ebe8bb056de850080dbd70d3b3cc96fe
Author: zhengfu <791546968@qq.com>
Date:   Mon Feb 10 15:02:26 2020 +0800

    testing of first committing

现在轮到 master 分支指针跟着我们提交的新 commit 走了。

如果是多人协同的情况,上述两个分支上的工作甚至可以同时进行,只要一个人在自己机器上让 HEAD 指向 master 分支,TA就工作在 master 分支上;另一个人让 HEAD 指向 bug/fix1 分支,TA就工作在 bug/fix1 分支上;两人的工作都可以 push 回 team repo,互不影响。

4. 了解如何利用 GitHub 来简化分享与协同

GitHub 的核心是提供 git 仓库的托管服务,也就是说你可以把版本仓库建在 GitHub 的服务器上,让全世界都能访问到,当然你自己的机器上也会有一份,你可以把你的新创作 push 到 GitHub,也可以从 GitHub 上 pull 回别人的贡献。

使用 GitHub 最基础的场景有两个:

无论哪种场景,最终的情况都一样,就是你的本地有一个仓库对应到 GitHub 上某个仓库,你可以在本地工作,并通过 GitHub 与这个世界上任何人进行合作!

Reference

  1. google "git remote add"
  2. https://help.github.com/en/github/using-git/adding-a-remote
  3. google "git push -u"
  4. google "git push u flag meaning"
  5. https://stackoverflow.com/questions/18867824/what-does-the-u-flag-mean-in-git-push-u-origin-master
  6. https://git-scm.com/docs/git-push/1.6.2
  7. https://git-scm.com/book/zh/v2/Git-分支-远程分支

Logging

2020-02-11 20:16:29 initialize