Git学习笔记

安装

在 Windows 环境中,最简单快捷的方法是使用 msysGit。

http://msysgit.github.io/

安装过程中,可以设置调用 Git 的环境变量。一般只会用到 msysGit 中附属的 Git Bash 命令提示符,所以请选择最上面的Use Git Bash only,然后进行下一步。

image-20240101072709809

image-20240101072738346

换行符的处理

在所示的页面中,选择换行符的相关设置。
GitHub 中公开的代码大部分都是以 Mac 或 Linux 中的 LF(Line Feed)换行。然而,由于 Windows 中是以 CRLF(Carriage Return +Line Feed)换行的,所以在非对应的编辑器中将不能正常显示。
Git 可以通过设置自动转换这些换行符。使用 Windows 环境的各位,请选择推荐的“Checkout Windows-style, commit Unix-style line endings”选项。换行符在签出时会自动转换为 CRLF,在提交时则会自动转换为LF。

image-20240101072858375

Git Bash

顺利安装好 msysGit 之后,Git Bash 会作为一个应用程序添加进系统,接下来请启动它。双击之后,会弹出一个名为 Git Bash 的命令提示符,它附属于 msysGit。如果是按照上述介绍的流程进行安装,那么 git 命令就只能在 Git Bash 中使用,在 Windows 附属的命令提示符中则无法运行。

image-20240101073119720

从名字中带有 Bash 就不难猜到,Git Bash 中照搬了许多 Bash 命令,习惯 Linux 的人用起来会感觉比 Windows 命令提示符更得心应手。借这个机会,不妨也熟悉一下 Windows 的 CLI(Command Line Interface,命令行界面)操作。

初始设置

设置姓名和邮箱地址

首先来设置使用 Git 时的姓名和邮箱地址。名字请用英文输入。

1
$ git config --global user.name "Firstname Lastname"
1
$ git config --global user.email "your_email@example.com"

这个命令,会在“~/.gitconfig”中以如下形式输出设置文件。

1
2
3
[user]
name = Firstname Lastname
email = your_email@example.com

想更改这些信息时,可以直接编辑这个设置文件。这里设置的姓名和邮箱地址会用在 Git 的提交日志中。由于在 GitHub 上公开仓库时,这里的姓名和邮箱地址也会随着提交日志一同被公开,所以请不要使用不便公开的隐私信息
在 GitHub 上公开代码后,前来参考的程序员可能来自世界任何地方,所以请不要使用汉字,尽量用英文进行描述。当然,如果您不想使用真名,完全可以使用网络上的昵称。

提高命令输出的可读性

顺便一提,将 color.ui 设置为 auto 可以让命令的输出拥有更高的可读性。

1
$ git config --global color.ui auto

“~/.gitconfig”中会增加下面一行。

1
2
[color]
ui = auto

这样一来,各种命令的输出就会变得更容易分辨。

Github

设置头像

只要使用创建 GitHub 账户时注册的邮箱在 Gravatar 上设置头像,GitHub 的头像就会变成您设置好的样子。

设置 SSH Key

运行下面的命令创建 SSH Key。

1
2
3
4
5
6
$ ssh-keygen -t rsa -C "your_email@example.com"
Generating public/private rsa key pair.
Enter file in which to save the key
(/Users/your_user_directory/.ssh/id_rsa): `按回车键`
Enter passphrase (empty for no passphrase): `输入密码`
Enter same passphrase again: `再次输入密码`

密码根据情况输入,不输入一般也可以。一路回车即可。

id_rsa 文件是私有密钥,id_rsa.pub 是公开密钥。

将公钥添加到github设置中,私钥继续默认保存在个人PC上。需要时它们会自动两相验证,完成身份确认。

添加公开密钥

image-20240101075107597

添加成功之后,创建账户时所用的邮箱会接到一封提示“公共密钥添加完成”的邮件。
完成以上设置后,就可以用手中的私人密钥与 GitHub 进行认证和通信了。让我们来实际试一试。

1
2
3
4
$ ssh -T git@github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is `fingerprint值` .
Are you sure you want to continue connecting (yes/no)? `输入yes`

出现如下结果即为成功。

1
2
Hi hirocastest! You've successfully authenticated, but GitHub does not 
provide shell access.

可以Follow感兴趣的用户,对于仓库,也可以使用 Watch 功能获取最新的开发信息。如果您经常使用的某个软件正在 GitHub 上进行开发,不妨去 Watch 一下。

创建仓库

Initialize this repository with a README

在 Initialize this repository with a README 选项上打钩,随后GitHub 会自动初始化仓库并设置 README 文件,让用户可以立刻clone 这个仓库。如果想向 GitHub 添加手中已有的 Git 仓库,建议不要勾选,直接手动 push。

Add .gitignore

下方左侧的下拉菜单非常方便,通过它可以在初始化时自动生成 .gitignore 文件 A。这个设定会帮我们把不需要在 Git 仓库中进行版本管理的文件记录在 .gitignore 文件中,省去了每次根据框架进行设置的麻烦。下拉菜单中包含了主要的语言及框架,选择今后将要使用的即可。

Add a license

右侧的下拉菜单可以选择要添加的许可协议文件。如果这个仓库中包含的代码已经确定了许可协议,那么请在这里进行选择。随后将自动生成包含许可协议内容的 LICENSE 文件,用来表明该仓库内容的许可协议。

连接仓库

GitHub Flavored Markdown

在 GitHub 上进行交流时用到的 Issue、评论、Wiki,都可以用Markdown 语法表述,从而进行标记。准确地说应该是 GitHub Flavored Markdown(GFM)语法。该语法虽然是 GitHub 在 Markdown 语法基础上扩充而来的,但一般情况下只要按照原本的 Markdown 语法进行描述就可以。
关于 Markdown 语法的解说,网上也有相关资料可查

编写代码
1
2
3
4
 `hello_word.php的内容`
<?php
echo "Hello World!";
?>

由于 hello_word.php 还没有添加至 Git 仓库,所以显示为 Untracked files

1
2
3
4
5
6
7
$ git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# hello_world.php
nothing added to commit but untracked files present (use "git add" to track)
提交

将 hello_word.php 提交至仓库。这样一来,这个文件就进入了版本管理系统的管理之下。今后的更改管理都交由 Git 进行。

1
2
3
4
5
$ git add hello_world.php
$ git commit -m "Add hello world script by php"
[master d23b909] Add hello world script by php
1 file changed, 3 insertions(+)
create mode 100644 hello_world.php

通过 git add 命令将文件加入暂存区,再通过 git commit 命令提交。
添加成功后,可以通过 git log 命令查看提交日志。

1
2
3
4
5
6
$ git log
commit d23b909caad5d49a281480e6683ce3855087a5da
Author: hirocastest <hohtsuka@gmail.com>
Date: Tue May 1 14:36:58 2012 +0900
Add hello world script by php
`略`
进行 push

之后只要执行 push,GitHub 上的仓库就会被更新。

1
2
3
4
5
6
7
8
$ git push
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 328 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:hirocastest/Hello-World.git
46ff713..d23b909 master -> master

这样一来代码就在 GitHub 上公开了。


git init——初始化仓库

要使用 Git 进行版本管理,必须先初始化仓库。Git 是使用 git init 命令进行初始化的。请实际建立一个目录并初始化仓库。

1
2
3
4
5
$ mkdir git-tutorial
$ cd git-tutorial
$ git init
Initialized empty Git repository in /Users/hirocaster/github/github-book
/git-tutorial/.git/

如果初始化成功,执行了 git init 命令的目录下就会生成 .git 目录。这个 .git 目录里存储着管理当前目录内容所需的仓库数据。
在 Git 中,我们将这个目录的内容称为“附属于该仓库的工作树”。文件的编辑等操作在工作树中进行,然后记录到仓库中,以此管理文件的历史快照。如果想将文件恢复到原先的状态,可以从仓库中调取之前的快照,在工作树中打开。开发者可以通过这种方式获取以往的文件。

git status——查看仓库的状态

git status命令用于显示 Git 仓库的状态。工作树和仓库在被操作的过程中,状态会不断发生变化。在 Git 操作过程中时常用 git status 命令查看当前状态,可谓基本中的基本。

1
2
3
4
5
6
$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)

结果显示了我们当前正处于 master 分支下。接着还显示了没有可提交的内容。所谓提交(Commit),是指“记录工作树中所有文件的当前状态”。
尚没有可提交的内容,就是说当前我们建立的这个仓库中还没有记录任何文件的任何状态。这里,我们建立 README.md 文件作为管理对象,为第一次提交做前期准备。

1
2
3
4
5
6
7
8
9
10
$ touch README.md
$ git status
# On branch master
#
# Initial commit
## 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)

可以看到在 Untracked files 中显示了 README.md 文件。类似地,只要对 Git 的工作树或仓库进行操作,git status命令的显示结果就会发生变化。

git add——向暂存区中添加文件

如果只是用 Git 仓库的工作树创建了文件,那么该文件并不会被记入 Git 仓库的版本管理对象当中。因此我们用 git status命令查看README.md 文件时,它会显示在 Untracked files 里。
要想让文件成为 Git 仓库的管理对象,就需要用 git add命令将其加入暂存区(Stage 或者 Index)中。暂存区是提交之前的一个临时区域。

1
2
3
4
5
6
7
8
9
10
11
$ git add README.md
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#

README.md 文件加入暂存区后,git status命令的显示结果发生了变化。可以看到,README.md 文件显示在 Changes to be committed 中了。

git commit——保存仓库的历史记录

git commit命令可以将当前暂存区中的文件实际保存到仓库的历史记录中。通过这些记录,我们就可以在工作树中复原文件。

实际运行一下 git commit命令。

1
2
3
4
$ git commit -m "First commit"
[master (root-commit) 9f129ba] First commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md

-m 参数后的 "First commit"称作提交信息,是对这个提交的概述。

记述详细提交信息

刚才我们只简洁地记述了一行提交信息,如果想要记述得更加详细,请不加 - m,直接执行 git commit命令。执行后编辑器就会启动,并显示如下结果。

1
2
3
4
5
6
7
8
9
10
11
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#

在编辑器中记述提交信息的格式如下。
● 第一行:用一行文字简述提交的更改内容
● 第二行:空行
● 第三行以后:记述更改的原因和详细内容

只要按照上面的格式输入,今后便可以通过确认日志的命令或工具看到这些记录。
在以 #(井号)标为注释的 Changes to be committed(要提交的更改)栏中,可以查看本次提交中包含的文件。将提交信息按格式记述完毕后,请保存并关闭编辑器,以 #(井号)标为注释的行不必删除。随后,刚才记述的提交信息就会被提交。

中止提交

如果在编辑器启动后想中止提交,请将提交信息留空并直接关闭编辑器,随后提交就会被中止。

查看提交后的状态

执行完 git commit命令后再来查看当前状态。

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

当前工作树处于刚刚完成提交的最新状态,所以结果显示没有更改。

git log——查看提交日志

git log命令可以查看以往仓库中提交的日志。包括可以查看什么人在什么时候进行了提交或合并,以及操作前后有怎样的差别。

先来看看刚才的 git commit命令是否被记录了。

1
2
3
4
5
$ git log
commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
Date: Sun May 5 16:06:49 2013 +0900
First commit

如上所示,屏幕显示了刚刚的提交操作。commit 栏旁边显示的“9f129b……”是指向这个提交的哈希值。Git 的其他命令中,在指向提
交时会用到这个哈希值。
Author 栏中显示我们给 Git 设置的用户名和邮箱地址。Date 栏中显示提交执行的日期和时间。再往下就是该提交的提交信息。

只显示提交信息的第一行

如果只想让程序显示第一行简述信息,可以在 git log命令后加上 --pretty=short。这样一来开发人员就能够更轻松地把握多个提交。

只显示指定目录、文件的日志

只要在 git log 命令后加上目录名,便会只显示该目录下的日志。如果加的是文件名,就会只显示与该文件相关的日志。

1
$ git log README.md
显示文件的改动

如果想查看提交所带来的改动,可以加上 -p 参数,文件的前后差别就会显示在提交信息之后。

1
$ git log -p

比如,执行下面的命令,就可以只查看 README.md 文件的提交日志以及提交前后的差别。

1
$ git log -p README.md

git diff——查看更改前后的差别

git diff 命令可以查看工作树、暂存区、最新提交之间的差别。

我们在刚刚提交的 README.md 中写点东西。

1
# Git教程

这里用 Markdown 语法写下了一行题目。

查看工作树和暂存区的差别

执行 git diff 命令,查看当前工作树与暂存区的差别。(a和b是两个不同分支)

1
2
3
4
5
6
7
8
$ git diff

diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git教程

由于我们尚未用 git add 命令向暂存区添加任何东西,所以程序只会显示工作树与最新提交状态之间的差别。

这里解释一下显示的内容。“+”号标出的是新添加的行,被删除的行则用“-”号标出。我们可以看到,这次只添加了一行。
git add 命令将 README.md 文件加入暂存区。

1
$ git add README.md
查看工作树和最新提交的差别

如果现在执行 git diff 命令,由于工作树和暂存区的状态并无差别,结果什么都不会显示。要查看与最新提交的差别,请执行以下命令。

1
2
3
4
5
6
7
$ git diff HEAD
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git教程

不妨养成这样一个好习惯:在执行 git commit 命令之前先执行git diff HEAD命令,查看本次提交与上次提交之间有什么差别,等确认完毕后再进行提交。这里的 HEAD 是指向当前分支中最新一次提交的指针
由于我们刚刚确认过两个提交之间的差别,所以直接运行 git commit 命令。

1
2
3
$ git commit -m "Add index"
[master fd0cbf0] Add index
1 file changed, 1 insertion(+)

保险起见,我们查看一下提交日志,确认提交是否成功。

1
2
3
4
5
6
7
8
9
10
11
12
$ git log
commit fd0cbf0d4a25f747230694d95cac1be72d33441d
Author: hirocaster <hohtsuka@gmail.com>
Date: Sun May 5 16:10:15 2013 +0900

Add index

commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
Date: Sun May 5 16:06:49 2013 +0900

First commit

成功查到了第二个提交。

分支的操作

git branch——显示分支一览表

git branch命令可以将分支名列表显示,同时可以确认当前所在分支(标星号*的)。

git checkout -b——创建、切换分支

如果想以当前的 main (默认)分支为基础创建新的分支,我们需要用到git checkout -b命令。

切换到 feature-A 分支并进行提交

执行下面的命令,创建并切换到名为 feature-A 的分支。

1
2
$ git checkout -b feature-A
Switched to a new branch 'feature-A'

等效于

1
2
$ git branch feature-A
$ git checkout feature-A

不断对一个分支(例如feature-A)进行提交的操作,我们称为“培育分支”。

切换回 main 分支
1
2
$ git checkout main
Switched to branch 'main'
切换回上一个分支
1
2
$ git checkout -
Switched to branch 'feature-A'

像上面这样用“-”(连字符)代替分支名,就可以切换至上一个分支。当然,将“-”替换成 feature-A 同样可以切换到 feature-A 分支。

特性 (Feature) 分支

Git 与 Subversion(SVN)等集中型版本管理系统不同,创建分支时不需要连接中央仓库,所以能够相对轻松地创建分支。因此,当今大部分工作流程中都用到了特性(Topic)分支
特性分支顾名思义,是集中实现单一特性(主题),除此之外不进行任何作业的分支。在日常开发中,往往会创建数个特性分支,同时在此之外再保留一个随时可以发布软件的稳定分支。稳定分支的角色通常由 main 分支担当。

基于特定主题的作业在特性分支中进行,主题完成后再与 main 分支合并。

主干 (main) 分支

主干分支是刚才我们讲解的特性分支的原点,同时也是合并的终点。通常人们会用 main 分支作为主干分支。

有时我们需要让这个主干分支总是配置在正式环境中,有时又需要用标签 Tag 等创建版本信息,同时管理多个版本发布。拥有多个版本发布时,主干分支也有多个

git merge——合并分支

假设 feature-A 已经实现完毕,想要将它合并到主干分支 main 中。首先切换到 main 分支,然后把 feature A分支“拽”着合并进来。

1
$ git checkout main

然后合并 feature-A 分支。为了在历史记录中明确记录下本次分支合并,我们需要创建合并提交。因此,在合并时加上 --no-ff 参数。

`--no-ff`参数介绍

在Git版本控制系统中,--no-ff是一个合并(merge)操作的选项。ff在这里的含义是“Fast-forward”,也就是快进模式。

为了更清楚地解释--no-ff参数的含义,我们需要先了解一下Git的Fast-forward模式。

在Git中,如果一个分支(例如,feature分支)是从另一个分支(例如,master分支)创建的,然后在feature分支上进行了提交,而在此期间master分支没有任何变动,那么当feature分支合并回master分支时,Git默认会采取Fast-forward模式。在Fast-forward模式中,Git只需简单地将master分支的指针向前“快进”到feature分支的最新提交,就完成了合并。结果就是,合并后的提交历史线性无分叉,你无法看到分支曾经存在过。

而当使用--no-ff选项进行合并时,Git会禁用Fast-forward模式,即使当前的合并可以进行Fast-forward。Git会创建一个新的merge commit,即使这个commit并不总是必要的。这样,你可以从commit历史中显式地看到一个合并发生过,以及哪些提交属于哪个分支。这对于保持明确、易于理解的历史记录非常有用。

简单说,--no-ff选项让合并后的Git历史记录保留分支信息,给出更丰富的项目历史信息。

bash
1
$ git merge --no-ff feature-A

随后编辑器会启动,用于录入合并提交的信息。

1
2
3
4
5
6
7
Merge branch 'feature-A'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
image-20240108143516094