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)
这些信息的含义是:
On branch master:当前工作在 master 分支下,关于分支我们后面会讲,现在先跳过;
No commits yet:目前版本仓库里还没有任何版本记录,Git 中的 commit 就是版本的概念;
from datetime import datetime
print(datetime.now().strftime('今天是%Y年%m月%d日'))
[ ] 同时我们也可以改一下之前有的 README.md 文件,随便改点什么都可以,然后回到命令行界面运行 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.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")
第一段 Changes not staged for commit: 列出的是已经加入版本仓库但有了新变化的文件(README.md),我们可以像前面一样用 git add 来将新的变化加入 staging area,然后再 commit。
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
new file: hello.py
new file: today.py
我们可以看到有三个文件,两个新的,一个修改的,放在 staging area 等待提交。这个时候我们考虑到 README.md 的修改和添加的两个新文件是两件事,最好分开提交,所以我们想先把第一项从 staging area 移出来,这时候就用到 git reset 命令了:
git reset HEAD README.md
注意,git reset 是个颇为复杂的命令,要把它完全搞清楚远远超出了这个小小的 tutorial 的篇幅,有兴趣的话可以看看 Git from the bottom up 中的相关介绍(最好整篇都读)。
[ ] 再看 git status:
On branch master
Changes to be committed:
(use "git reset HEAD <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 checkout -- <file>..." to discard changes in working directory)
modified: README.md
1. Git 与 GitHub 入门
[ ] 使用 git 节约时间、提高效率。
[ ] 有一个活跃的 Github 账户,参与人类史上前所未有的共同协作时代。
[ ] 在 Github 这个地球上第一个给人们提供“用作品社交”方式的平台上,用作品社交。
[ ] 掌握所有 git 和 版本控制(version control)的重要的基本概念和基本操作。
[ ] 并对使用“分布式版本控制系统(Distributed Version Control Sysytem, DVCS)”的两个主流方法有所了解。
[ ] 如果想更系统和深入了解 git,最好的内容是官方教科书 Pro Git,值得一读再读,务必确保理解完整。
[ ] 视频讲解:Git 和 GitHub 简明入门
版本控制解决什么问题
Git 基本概念
Git 基本用法:A Tutorial
在了解了上面的基本概念之后,下面就请跟着我们的指引一步步的操作来熟悉 git 的基本用法吧。
使用前的基本设置
git config
后面的参数带有两个横线--
):基本操作
Code
子目录,在其下创建一个新的子目录用于下面整个例子:最后一个
pwd
命令会打印出你当前所在目录供确认,这就是我们的工作目录(working directory),里面还没有文件。.git
子目录下初始化了一个空的 repo,这时候如果我们输入ls
来查看是看不到这个.git
目录的,因为.
打头的目录是隐藏的,我们可以用ls -la
来显示它。这个
.git
目录下就是我们的版本仓库,使用 git 的特定数据格式保存。这也就意味着,我们的工作目录已经成为一个“由 git 进行版本控制”的目录,我们在这里做的任何事情都会被 git 跟踪,也可以作为版本提交到 repo 中去。上面的命令将显示目前工作目录的状态,比如有没有文件被修改,目前还什么都没有。这个命令对我们初学者很有用,我们应该养成习惯经常执行一下,看看在 git 的视角发生了什么。
touch
命令创建一个空的名为README.md
的文件,下一行则是用 VSCode 打开这个空的文件,你可以随意输入一点什么,保存然后回到命令行界面。git status
可以看到下面的提示:这些信息的含义是:
On branch master
:当前工作在 master 分支下,关于分支我们后面会讲,现在先跳过;No commits yet
:目前版本仓库里还没有任何版本记录,Git 中的 commit 就是版本的概念;Untracked files:
以及后面的文件列表:列出了工作目录下存在但还没有加入版本仓库的文件,并且提示可以用git add
来将文件加入到版本仓库中;[ ] 最后一行是对当前状态的综述,告诉我们还没有准备提交的文件变化,但有些新文件存在了,可以用
git add
来将文件加入版本仓库。我们照做:git status
可以看到下面的提示:这次就没有
Untracked files
了,因为README.md
这个文件已经放到了Changes to be committed
列表下,表示这个文件的变化已经准备好提交(commit),其实就是表示它被我们放到了暂存区域(staging area)。下面还有一行提示,告诉我们如果想把文件“移出暂存区域(unstage)”,可以用git rm --cached <file>
这个命令,我们后面会试试这个命令。这个命令会 commit 目前在 staging area 的任何文件变化到版本仓库,在实际执行前会打开文本编辑器让我们输入 commit message 也就是版本说明:
回到命令行可以看到执行结果:
这些信息告诉我们:
master 分支下创建了一个新的 commit(可以理解为版本,下面不再注释),因为是整个版本仓库的第一个,所以标记为
root-commit
;这个 commit 的编号(commit id)为
7451a00
,这是一个全局唯一的十六进制数(你那里应该是别的数字),以后我们可以用这个编号来管理这个 commit;然后是我们输入的 commit message;
最后是这次 commit 的一些 summary 信息(涉及一个文件变化,三个新的行被插入到版本仓库中等等)。
[ ] 现在执行
git status
可以看到:也就是说我们刚提交完,工作目录和仓库里的最新 commit 是完全一样的,没有什么可以提交的新变化。
[ ] 下面我们来多做些操作,多 commit 几次试试。
code .
用 VSCode 打开当前目录,VSCode 会列出当前目录下所有文件(也就是刚才创建的README.md
),我们可以在 VSCode 里新建几个文件,里面填入一些内容,比如可以创建两个 Python 的程序源文件(很快我们就知道这些程序是啥意思以及怎么写了):hello.py:
today.py:
README.md
文件,随便改点什么都可以,然后回到命令行界面运行git status
可以看到类似下面的信息:第一段
Changes not staged for commit:
列出的是已经加入版本仓库但有了新变化的文件(README.md
),我们可以像前面一样用git add
来将新的变化加入 staging area,然后再 commit。第二段
Untracked files:
和之前没什么区别,列出了新出现还未加入版本仓库的文件。-[ ] 现在我们可以把所有变化都加入 staging area:
这个命令中的
.
表示将当前目录及其下面变化的所有文件都加入到 staging area,完成后看git status
:我们可以看到有三个文件,两个新的,一个修改的,放在 staging area 等待提交。这个时候我们考虑到
README.md
的修改和添加的两个新文件是两件事,最好分开提交,所以我们想先把第一项从 staging area 移出来,这时候就用到git reset
命令了:注意,
git reset
是个颇为复杂的命令,要把它完全搞清楚远远超出了这个小小的 tutorial 的篇幅,有兴趣的话可以看看 Git from the bottom up 中的相关介绍(最好整篇都读)。git status
:[ ] 应我们的要求
README.md
已经变成了 not staged 的状态,现在我们可以运行git commit
命令来提交那两个新加的 Python 文件,输入 commit message 之后提交成功。[ ] 然后我们可以再来提交
README.md
的修改了:git status
看看状态。版本历史
git log
命令列出了版本仓库中已有的 commit history,每一个 commit 一段,从新到旧排列(最后的 commit 在最上面)。最新的 commit 后面有个HEAD -> master
的标志,这个和分支有关,我们后面会讲。每一段开头都是
commit
字样后面跟着 commit id,非常长,但我们一般可以用前几位来代替;之后是提交作者、提交日期时间,然后是 commit message。信息简明扼要、一目了然,谁什么时候做了什么都被清晰记录在案,真是非常合适的“工作证明(Proof of Work)”。[ ] 按
q
键退回到命令行界面。[ ] 现在我们可以回到上面列出的任何一个 commit 的状态,我们来试试:
会有一大段提示说明文字,提到什么
HEAD
之类的奇怪概念,先不管它(后面到分支一节会讲),看看你的工作目录,是不是回到了你最初提交README.md
一个文件的状态?新加入的文件和对README.md
的修改都不见了,仿佛是进行了时间旅行一般。不用担心,时间旅行的只是你的 working directory,所有的修改记录都还在 repo 里。你会发现一切又回到了最新的状态(再一次,我们会在关于分支的一节讲这个 master 是啥)。
前一行命令比较指定 commit 和工作目录当前状态,第二行命令比较两个指定的 commit。输出的内容是一种专门的 diff format,是 Unix 系统做文件比较的标准输出格式,其中列出了有差异的文件,以及这些文件中有差异的行,列出了哪些行是新增,哪些行被删除,那些行有变化。对于现在的你来说这个格式可能有点难懂,没有关系,以后会熟悉起来的,而且 VSCode 提供了图形化的界面来做文件比较和合并,很多时候也更好用一些。
这个简单的 tutorial 就到此结束。
[ ] 了解非常重要的分支(branch)概念及其在协同中的用法,还有如何利用 GitHub 来简化分享与协同。
[ ] 学习 关于 git 新版本的变化
在 git 2.23 及以后的版本中引入了两个实验性的新命令:
git switch
和git restore
,其目的是以后替代语焉不详的git checkout
命令,这些新的命令(尤其是git restore
)从 2.23 版本开始出现在交互提示中:代替git reset HEAD <file>...
用于从 staging area 撤出修改,以及代替git checkout -- <file>...
用于回退修改。所以如果你已经更新到 2.23 或者更新的版本,可能看到的信息提示会与这个教程中不一样,但没关系,因为:
关于 2.23 引入的变化,
git
官网有一篇详细的说明。分支和协同
git tag
命令处理与 tag 有关的操作。如果不带任何参数执行git tag
会输出仓库中存在的所有 tag,如果我们想在目前的最新的 commit 上打一个标签 v1.0,表示这是我们的 1.0 版本的状态,可以这么做:git log
可以看到最新的 commit 后面出现了tag: v1.0
的字样,表示在这个 commit 上打上了一个名叫 “v1.0” 的标签,以后我们就可以用这个标签来引用这个 commit,而不需要用又长有没有实际意义的 commit id。这种相当于 commit 别名的 tag 叫“轻量标签(lightweight tag)”,Git 还支持一种带标注的标签(annotated tag),这里就不详细介绍了,本质差不多,只是增加了标签本身的属性,比如是谁打的,什么时候打的等等。不管是哪种标签,我们都可以将其用在需要指定一个 commit 的场合,比如 checkout、diff 等操作中。
[ ] 如果要删除某个标签用
git tag -d tag_name
就可以了。[ ] 假定我们当前最新 commit 为
51b61b3
,此时我们创建新分支bug/fix1
:git branch
在当前 commit 上创建分支,注意创建分支并不会自动切换到新创建的分支,目前我们还工作在master
分支,HEAD
指针指向master
分支。bug/fix1
分支,这需要用checkout
命令:切换分支其实就是让
HEAD
指针指向该分支。这时候HEAD
指向的bug/fix1
分支指针就会跟着我们提交的新 commit 走了,比如我们提交了新的f843665
号 commit,就会变成这样:f843665
号 commit 成功修复了 bug,我们在bug/fix1
分支上的工作告一段落,需要切回主分支:于是
HEAD
指针指回master
分支,现在轮到master
分支指针跟着我们提交的新 commit 走了。[ ] 在合适的时间,团队里某位成员可以进行分支合并,假定是把
bug/fix1
分支合并到master
分支(实际上可以把任意分支合并到任意分支),那么大致的流程如下:git fetch origin
master
分支下:git checkout master
bug/fix1
分支合并到master
分支:git merge bug/fix1
GitHub 简介
[ ] 在 GitHub 创建一个新的 repo,然后把你在自己本地已经建好的 repo 放上去,这个很简单,在你的 GitHub 主页选择 “New”,然后照着页面说明一路做下去就行了;
[ ] 把 GitHub 上已有的 repo 克隆到自己本地。在你看中的 repo 首页按那个绿色的 “Clone or download” 按钮,拷贝下面文字框里的 repo 地址,然后在你的命令行界面贴到
git clone
命令后面执行就行了。[ ] 学习 GitHub Flow,它包含以下几个步骤:
[ ] 创建属于你的分支:可以直接在 GitHub 上操作,也可以 clone 回本地在本地创建,然后在 push 回去;需要时就创建,多少都可以,只要合理;
[ ] 在且只在你自己的分支中工作和提交 commit;
[ ] 当一个分支上的工作告一段落,可以发起一个 pull request,具体来说,就是 GitHub 会对你的工作分支和某个分支(比如
master
)进行比较,给出差异列表,你可以选择全部或者部分差异打包成一个 pull request,pull request 提交之后这个 repo 的所有人都会看到,大家可以对这个 pull request 的内容进行讨论,做代码检查(code review),必要时还会进行测试;[ ] 经过某种机制(比如团队全员或者一个决策小组投票)这个 pull request 被认可,那么有权限的管理员可以在 GitHub 上执行一个合并(merge)操作,这个 pull request 的内容就会合并到主要分支(比如
master
)。Reference
Logging
2020-02-09 10:35:54 continue 2020-02-08 14:54:03 initialize