Git使用笔记

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。为了将平时写的一些代码托管到Github,有必要学习一下git的基本使用方法。怕自己忘记了,撰写学习笔记如下:

配置Git

Git跟踪项目必须配置用户名和电子邮件。当然如果一开始没有设置,在第一次提交时Git也会提示输入相关信息。

1
2
$ git config --global user.name "username"
$ git config --global user.email "[email protected]"

创建项目

创建一个项目,即创建一个文件夹,将相关文件保存在文件夹内。譬如,创建名为git_pratice的文件,并初始化仓库:

1
2
$ git init
Initialized empty Git repository in git_pratice/.git/

输出表明Git在git_practice文件夹中初始化了一个空仓库。仓库是程序中被Git主动跟踪的一组文件,这些文件都存储在隐藏文件夹.git中,因此千万不要去更改这个文件夹,更不能删除。

将文件加入仓库

检查状态

项目已经创建,我们先用git status来看看项目的状态:

1
2
3
4
5
6
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

在Git中,分枝(branch)是项目的一个版本。从上述输出可以看出,我们位于分枝master上。后面的输出表明,当前尚未进行任何提交,并且无可提交内容(毕竟当前文件夹为空么)。

将文件加入仓库

在git_pratice文件夹中新建Python程序文件hello_world.py,我们将使用这个文件来探索Git的基本功能。然后,我们再用git status来看看项目的状态:

1
2
3
4
5
6
7
8
9
10
$ git status
On branch master

No commits yet

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

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

根据输出信息,可以发现Git已经发现hello_world.py文件,并提示用git add <file>将文件加入仓库:

1
2
3
4
5
6
7
8
9
10
$ git add .
$ git status
On branch master

No commits yet

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

命令git add .表示将项目中未被跟踪的所有文件都加入到仓库,它不提交这些文件,而只是让Git开始关注这些文件。标签new file意味着这些文件是新添加到仓库的。

忽略文件

有时候文件下会产生一些我们无需跟踪的文件,譬如Python程序会在执行.py文件时产生.pyc的文件,并自动保存在_pycache_目录中。为了让Git忽略这个目录,可以创建一个名为.gitignore的特殊文件,其内容为:_pycache_。这样可以让Git忽略该目录下的所有文件。这个例子里,我并没有创建这个文件,所以如果你创建了,下面的实例可能会略有差异。

执行提交

第一次提交

下面执行第一次提交:

1
2
3
4
5
6
7
8
$ git commit -m "started project"
[master (root-commit) 1f96432] started project
1 file changed, 11 insertions(+)
create mode 100644 hello_world.py

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

执行命令git commit -m “started project”进行提交,-m参数后面跟的是记录信息,记录信息被记录到项目的历史记录中便于查看变化。反馈输出信息显示,1个文件发生了改变。再后的状态检查显示,工作目录是干净的。

查看提交历史

Git记录所有的项目提交,可以通过git log查看历史:

1
2
3
4
5
6
$ git log
commit 1f964320343750dd72c8adcb580589034df7df48 (HEAD -> master)
Author: Bruce <[email protected]>
Date: Wed Jan 22 16:26:25 2020 +0800

started project

每次提交时,Git都会生成一个40个字符长度的唯一ID,它记录了谁提交的、提交时间和提交描述信息。Git还提供了一个选项,让我们能够打印提交历史条目的简洁版本:

1
2
$ git log --prettty=oneline
1f964320343750dd72c8adcb580589034df7df48 (HEAD -> master) started project

标记--pretty=oneline指定显示两项最重要信息:提交ID和提交描述信息。

第二次提交

为了展示版本控制的强大威力,我们对项目进行修改:在hello_world.py中再添加一行代码。然后我们来查看项目状态:

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 restore <file>..." to discard changes in working directory)
modified: hello_world.py

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

可以发现,Git注意到了这个文件发生了变化。输出指出了我们当前所在的分枝、被修改文件的名称,并指出所做的修改尚未提交。下面我们来提交所做的修改,并查看状态:

1
2
3
4
5
6
7
8
9
10
11
$ git commit -am "Extending greetings"
[master bd51047] Extending greetings
1 file changed, 1 insertion(+)

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

$ git log --pretty=oneline
bd5104714620cbe895d3e681122ce04140926508 (HEAD -> master) Extending greetings
1f964320343750dd72c8adcb580589034df7df48 started project

我们再次执行了提交,并在执行命令git commit时指定了标记-am,标记a表示将仓库中所有修改了的文件都加入到当前提交中(如果两次提交中间又创建了新文件,可再次执行git add .将这些文件加入到仓库)。标记m用于记录提交信息。

撤销修改

撤销修改

为了展示撤销修改功能,我们对hello_world.py进行修改,再加入一行代码print('Oh, no!.'),并保存这个文件。我们查看状态 ,可以发现Git已经注意到了所做的修改。

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 restore <file>..." to discard changes in working directory)
modified: hello_world.py

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

如果修改是我们想要的,我们可以如前所示进行提交。但如果不是我们要得,而要恢复到最后一个提交。为此,我们不在文本编辑器中对hello_world.py执行任何操作,而是在终端中执行如下命令:

1
2
3
4
5
6
7
$ git checkout .
Updated 1 path from the index

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

命令git checkout .放弃最后一次提交后所做的修改,将项目恢复到最后一次提交的状态。我们使用文本编辑器打开hello_world.py,发现其已经恢复到之前的版本。

检出到以前的提交

我们可以检出到提交历史中的任何一个版本,而不仅仅是最后一次提交,为此可以在命令git checkout后面加指定提交ID的前6个字符,而不是.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ git log --pretty=oneline
bd5104714620cbe895d3e681122ce04140926508 (HEAD -> master) Extending greetings
1f964320343750dd72c8adcb580589034df7df48 started project

$ git checkout 1f9643
Note: switching to '1f9643'.

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 1f96432 started project

$ git status
HEAD detached at 1f96432
nothing to commit, working tree clean

检出以前的历史版本后,将离开分枝master,并进入Git所说的分离头指针(detached HEAD)。查看hello_world.py文件,发现现在里面只有语句print('Hello World!'),即回到了初始状态。

如果要回到分枝master,可以如下操作:

1
2
3
4
5
6
7
$ git checkout master
Previous HEAD position was 1f96432 started project
Switched to branch 'master'

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

这让我们重新回到了分枝master。如果查看hello_world.py文件的话,它里面又有两句输出了。除非使用Git高级功能,否则在检出历史提交后最好不要对项目做任何修改。当然如果参与项目的开发人员只有你自己,而你又想放弃最近的提交,并恢复到以前的状态。为此,可以如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git log --pretty=oneline
bd5104714620cbe895d3e681122ce04140926508 (HEAD -> master) Extending greetings
1f964320343750dd72c8adcb580589034df7df48 started project

$ git reset --hard 1f9643
HEAD is now at 1f96432 started project

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

$ git log --pretty=oneline
1f964320343750dd72c8adcb580589034df7df48 (HEAD -> master) started project

查看状态,可以发现我们依然在分枝master上,而历史记录显示我们已经恢复到第一次提交的历史版本。

分枝与合并

分枝在本地完成,速度快。要创建一个分枝,我们使用branch命令,但该命令只是创建一个新分枝,不会将我们带入该分支,所以我们需要使用checkout命令来更改分枝。

1
2
3
$ git branch test
$ git checkout test
Switched to branch 'test'

对于其他分支的更改不会反映在主分支master上。如果想将更改提交到主分支,则需切换到master分枝,然后合并。譬如,我们修改hello_world.py,增加print("this is for test brance"),然后提交。最后切换到master,并合并分枝test。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ git status
On branch test
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: hello_world.py

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

$ git add .
$ git commit -m 'test for branche'
[test 141b867] test for branche
1 file changed, 1 insertion(+)

$ git checkout master
Switched to branch 'master'

$ git merge test
Updating 1f96432..141b867
Fast-forward
hello_world.py | 1 +
1 file changed, 1 insertion(+)

查看hello_world.py文件,发现新增的语句已经合进去。如果想删除分枝,我们使用-d标识。

1
2
$ git branch -d test
Deleted branch test (was 141b867).

发布与取回

发布版本

上述操作后,所有的文件依然存放在本机上。很多时候,尤其是多人写作开发时,开发者都会将项目发不到Git服务器上,进行协作开发。GitHub是世界上最有名的通过Git进行版本控制的软件源代码托管服务平台,本博客的代码示例都会发布在我的GitHub上。

我们先从服务器上克隆一个库

1
$ git clone ssh://example.com/~/www/project.git

对克隆后的文件修改后可以再推送到服务器上。

1
$ git push ssh://example.com/~/www/project.git

取回更新

如果已经安装上述命令进行push,下面的命令则表示当前分支自动与唯一一个追踪分枝进行合并,即更新。

1
$ git pull

如果从非默认位置更新,需指定url。

1
$ git pull http://git.example.com/project.git

删除

删除文件

如果想从仓库中删除文件,我们使用rm

1
$ git rm filename

仓库

如果仓库的历史记录搞乱了,而又不知道如何恢复。在这种情况下应对寻求项目小组其他成员帮助。如果项目开发人员只有自己一个人,可继续使用这些文件,但要将项目的历史记录阐述,即删除.git目录。这不会影响任何文件的当前状态,只会删除所有的提交,因此无法检出项目的其他记录而已。删除.git文件夹后,可以重新git init进行初始化。

参考文献

[1] 埃里克·马瑟斯. Python编程. Translated by 袁国忠.  图灵程序设计丛书·Python系列 ISBN: 9787115428028. 人民邮电出版社, 2016.

[2] Git五分钟教程