Contents

Git 简介

1. 概述

在本教程中,我们将讨论使用 Git 时最常使用的命令。

我们将从安装和配置开始,然后创建我们的第一个本地存储库。接下来,我们将学习如何提交更改并将它们与远程存储库同步。

此外,我们将讨论分支并学习一些高级技术,例如修改提交和操作提交历史。

2. 什么是 Git?

**Git是一个版本控制系统 (VCS),它允许随着时间的推移保存和跟踪对文件的更改,**而不会覆盖以前的快照。它可以帮助开发人员在项目上进行协作。

与其主要竞争对手SVN 不同,Git 还实现了分布式工作流系统。这意味着每个使用 Git 的开发人员都拥有整个存储库的本地副本。Git 还允许在没有与中央存储库的持续连接的情况下异步工作。

3. Git安装

我们可以 在 Windows、Mac 和 Linux 等最常见的操作系统上安装 Git 。事实上,在大多数 Mac 和 Linux 机器上,Git 都是默认安装的。

要查看我们是否已经安装了 Git,让我们打开一个终端并执行:

$ git version
git version 2.24.3 (Apple Git-128)

此外,Git 带有用于提交 ( git-gui ) 和浏览 ( gitk )的内置 GUI 工具。还有很多第三方工具  或 IDE 插件可以增强体验。

4.  git help - 一个方便的手册

在我们创建我们的第一个存储库之前,让我们运行git help 命令。它显示了有关 Git 本身的有用信息

$ git help
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]
...

我们还可以通过多种方式查看特定命令的手册:

$ git --help init
$ git help init
$ git init --help

上述所有三个变体都返回相同的输出。

使用*-g*选项,我们还可以访问内部指南列表来发展我们的技能:

$ git help -g
The common Git guides are:
   attributes          Defining attributes per path
   cli                 Git command-line interface and conventions
   core-tutorial       A Git core tutorial for developers
...
$ git help core-tutorial

要打印教程,我们需要提供其名称作为参数。

5.  git config - 配置 Git

安装 Git 后,我们可以使用git config 命令轻松配置它,该命令允许管理选项

Git 支持不同级别的选项,例如systemgloballocal工作树文件

虽然system设置是系统范围的,并且适用于系统上的每个用户及其所有存储库,但global级别是指用户特定的设置。

local配置特定于单个存储库,当我们不向git config命令传递任何选项时,它是 Git 使用的默认级别。

worktree和 file 级别是更高级的配置级别,可以应用于存储库中的单个分支或文件。

此外, Git 通过首先检查local级别来解析选项的有效值,如果未设置选项,则直到system**级别。

例如,让我们配置提交历史中使用的用户名:

$ git config --global user.name "Blogdemo User"

我们刚刚在全球范围内设置了我们的名称。

要覆盖单个存储库的选项,我们可以在其目录中使用*–local*标志。

要打印有效选项列表,我们使用:

$ git config -l
user.name=Blogdemo User

我们可以执行git –help config命令来获取所有可用选项的详细信息。

6. 创建存储库

接下来,我们需要创建一个存储库。为此,我们有两种选择——  可以从头开始在本地创建一个新的存储库,或者可以克隆一个现有的存储库

6.1. git init - 初始化一个新的存储库

如果我们决定初始化一个新的存储库,我们需要使用git init 命令。它将当前目录转换为 Git 存储库并开始跟踪其内容:

$ mkdir simple-repo; cd simple-repo; git init
Initialized empty Git repository in /simple-repo/.git/

Git 还在其中创建了一个名为 .git 的隐藏目录。这个目录存储了 Git 创建和使用的所有对象和引用 ,作为我们项目历史的一部分。这些文件是在提交期间创建的,并指向我们文件的特定修订。

之后,在大多数情况下,我们希望将我们已经创建的存储库与远程存储库连接起来。我们使用**git remote 命令来管理当前存储库的远程链接**:

git remote add origin https://github.com/Blogdemo/demo.git

我们刚刚添加了一个名为origin的新远程并将其连接到官方 Blogdemo GitHub 存储库。

6.2. git clone – 克隆一个外部仓库

有时我们想为现有的存储库做出贡献。首先,我们需要在本地下载现有的存储库。

git clone 命令将存储库克隆到一个新目录中

Cloning into 'repo'...

完成后,创建的新目录包含项目的所有文件、分支和历史记录。

此外,克隆的存储库已经配置并与外部源连接:

$ cd demo
$ git remote -v

Git 将使用这些原始链接来管理任何进一步的更改。

7. Git 工作流程

在我们配置了本地存储库之后,我们就可以应用第一个更改了。但在我们这样做之前,让我们检查一下 Git 如何跟踪这些更改。

我们的本地存储库由 Git 维护的三个不同的树组成。

第一个是工作目录,它包含文件的实际版本。

对文件进行更改后,我们可以将文件移动到充当暂存区的Index中。我们使用git add命令来做到这一点。**Index 中的文件开始被 Git 跟踪。

最后,**我们可以使用git commit命令将更改应用并保存到本地存储库中。**提交更改会更新存储库的 HEAD,它始终指向我们所做的最后一次提交。

这三个步骤用于维护本地更改。但正如我们所知,存储库也可能包含外部源。最后一步是同步两个存储库并发布我们的更改。

8. 做出改变

现在我们知道了 Git 的跟踪系统是如何工作的,我们已经准备好将我们的第一个更改应用到我们的存储库。

8.1. git status - 显示当前更改

让我们创建一个简单的文件并将其添加到我们的存储库中。之后,我们执行git status 命令并分析其输出:

$ "Hello World" >> myfile.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	myfile.txt
nothing added to commit but untracked files present (use "git add" to track)

该命令打印我们更改的当前详细状态。第一部分显示本地和远程分支是否同步。

接下来,输出显示working tree的状态——当前修改文件的列表及其维护状态。如我们所见,myfile.txt文件当前位于工作目录区域中,并且未被 Git 跟踪。

8.2. git add – 跟踪更改

要开始跟踪更改,我们需要使用 git add 命令将它们移动到Index 中:

$ git add myfile.txt
$ git stage *

我们可以通过用空格分隔它们来一次指定多个文件。我们还可以使用星号指定所有文件。

或者,我们也可以使用git stage 命令,它是git add命令的同义词。

现在让我们验证状态:

$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   myfile.txt

正如我们所见,Git 已经开始跟踪我们的文件。

8.3. git restore和gitignore - 取消跟踪更改

Git 允许从Index 中删除文件。如果我们错误地将更改移入其中并**想暂时禁用跟踪它们,我们使用git restore**

$ git restore -S myfile.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	myfile.txt

我们刚刚再次将文件移动到工作区 ,并将其排除在进一步提交之外,直到我们再次暂存它。-S ( -staged ) 标志告诉 Git 只恢复存储库的*Index *。

我们还可以永久排除文件并禁用跟踪它们。为此,我们需要创建一个.gitignore 文件。此文件包含文件名模式,并应用于当前目录及其子目录中的所有文件。任何进一步的添加操作都会忽略匹配这些模式的文件。

8.4. git commit – 保存更改

让我们恢复最后的更改并将我们的文件再次移动到暂存区:

$ git add myfile.txt

现在,是时候保存我们的工作了,所以我们需要进行一次提交。

提交是一个 Git 对象,它就像我们存储库在特定时间的快照。

要提交更改,让我们使用git commit 命令:

$ git commit -m "My first commit"
[master 8451901] My first commit
 1 file changed, 1 insertion(+)
 create mode 100644 myfile.txt

我们刚刚在本地创建了第一个提交。

git commit命令包含许多额外的选项来执行更复杂的操作,我们可以使用git commit –help命令进行检查。

最有用的是*-m*标志,它指定描述当前快照中所做更改的提交消息。

最后,让我们检查一下状态:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean

现在,我们的工作树不包含任何额外的更改,但本地存储库包含比其外部源更多的提交。因此,要发布我们的更改,我们应该将本地更改与源同步。

8.5. git log & git show – 检查提交

一旦我们创建了提交,我们就可以检查它的详细信息。提交包括许多额外的元数据,如作者、时间戳等。

要**打印当前分支的提交列表,我们使用git log **命令:

$ git log
commit 845190154ed7a491a6143669c4ce88058fb93f8a (HEAD -> master)
Author: ...
Date: ...
    My first commit
commit 9a1e11ec981b41e4b4b9c245a7a96cd6707f4705 (origin/master, origin/HEAD)
...

该列表默认按时间倒序显示当前分支的提交历史。

每个条目都包含一般元数据,例如提交的 id(唯一的 SHA-1 校验和)、作者、日期和给定消息。

当我们想更深入地了解单个提交时,我们使用 git show 命令打印其详细信息,然后是请求的提交 id:

$ git show 845190154ed7a491a6143669c4ce88058fb93f8a
commit 845190154ed7a491a6143669c4ce88058fb93f8a (HEAD -> master)
Author: ...
Date:...
    My first commit
diff --git a/myfile.txt b/myfile.txt
new file mode 100644
index 0000000..557db03
--- /dev/null
+++ b/myfile.txt
@@ -0,0 +1 @@
+Hello World

这一次,输出还**显示了提交与使用git diff **命令完成的先前快照的差异。

8.6. git stash – 搁置更改

**git stash 命令暂时搁置我们所做的更改,**恢复工作目录以匹配HEAD提交。这使我们能够快速切换上下文并开始处理其他事情。

让我们创建另一个文件并将其添加到Staging Area。之后,让我们执行git stash

$ touch myfile-2.txt; git add *
$ git stash push
Saved working directory and index state WIP on master: 8451901 My first commit

现在,让我们尝试列出文件:

$ ls myfile-2.txt
ls: myfile-2.txt: No such file or directory

我们可以看到现在该文件不存在。这是因为所有挂起的更改都已从工作目录中删除并保存在存储中。

我们可以使用list选项打印所有隐藏的修改:

$ git stash list
stash@{0}: WIP on master: 8451901 My first commit

由于我们没有提供它的描述,因此该存储库默认列为WIP,位于…。我们可以使用命令行上的*-m*标志将默认值更改为更具描述性的消息。

要检查其详细信息,我们使用show选项:

$ git stash show
 myfile-2.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

输出打印有关存储在最新存储中的更改的信息。

最后,如果我们想恢复更改,我们使用pop选项:

$ git stash pop
...
$ ls myfile-2.txt 
myfile-2.txt

我们刚刚从存储列表中删除了一个隐藏状态并将其应用到当前状态之上。

9. 操作提交历史

现在我们已经了解了如何在存储库中保存更改,让我们修改之前保存的提交。在以下部分中,我们将介绍最常见的用例。

9.1.git commit –amend – 向 Commit 添加其他更改

假设我们在提交更改时忘记包含一个文件。当然,我们可以在最后一个提交的基础上创建另一个提交,但这可能会使更改历史变得混乱。

在这种情况下,我们可能希望 Git 重写我们上次的提交,并使用amend选项包含我们忘记的文件。

让我们回顾一下最后一次提交:

$ git show --summary
commit 845190154ed7a491a6143669c4ce88058fb93f8a (HEAD -> master)
Author: ...
Date: ...
    My first commit
 create mode 100644 myfile.txt

让我们的my-file2.txt 从存储中弹出,让我们使用 amend选项提交它:

$ git commit --amend
[master 0ed9f03] My first commit
 2 files changed, 1 insertion(+)
 create mode 100644 myfile-2.txt
 create mode 100644 myfile.txt

我们可以注意到 Git 将文件添加到了我们最后一次提交中,并结合了这些更改。

9.2. git rebase - 重新应用提交

修改提交的更高级技术是通过git rebase 命令。它在另一个基础上重新应用历史提交,允许我们动态更改它们。

让我们在我们的存储库中创建另一个提交:

$ touch myfile-3.txt
$ git add *
$ git commit -m "My second commit"

现在,我们应该有两个单一的提交——My first commit和 My second commit

让我们开始以交互模式重新设置两个提交:

git rebase -i HEAD~2

这将打开一个编辑器,我们可以在其中使用命令操作历史记录:

pick 82d8635 My first commit
pick 6d58108 My second commit
# Rebase 9a1e11e..82d8635 onto 9a1e11e (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# d, drop <commit> = remove commit 
...

在顶部,我们有变基提交列表,然后是手册。我们在这里有很多选择。我们可以通过交换行来更改顺序,或者*reword 提交消息,或者将它们squash *为一个,edit甚至 *drop *单个提交。指令行将从上到下应用。

9.3. git reset - 回滚到特定状态

有时,我们可能想要删除当前状态并恢复到历史快照。为此,我们使用git reset 选项:

$ git reset 82d8635

它在指定的提交之后撤消所有提交,在本地保留更改并将它们移动到暂存区。但是,如果我们想删除所有工作更改,我们可以使用*–hard*标志。

10. 同步存储库

在存储库本地工作到现在之后,终于到了发布我们的更改的时候了。

**在上传它们之前,我们应该始终将我们的本地副本与远程同步,**以避免在发布过程中发生冲突。

10.1. git fetch – 更新参考资料

在我们实施更改时,其他人可能已将更改发布到同一分支。所以我们应该检查它们并将它们与我们的本地存储库同步。

git fetch 命令帮助我们这样做:

$ git fetch

这将从源存储库下载对象和引用

我们应该注意到这个动作永远不会修改当前的工作树。这是因为我们只能检查我们存储库的更新提交历史。如果我们发现任何未决的更改,我们必须更进一步。

10.2. git merge - 应用传入的更改

在发布代码之前,我们必须合并同一分支上的任何传入更改。如果我们不这样做,发布过程可能会失败。

让我们更新我们的分支:

$ git merge origin/master

git merge 命令是一个非常强大的工具。 **它从给定的参考下载所有新的变化,并将它们与当前工作树结合起来,**选择正确的合并策略。许多更改将自动应用,即使相同文件上存在修改。

但有时,没有简单的方法来合并更改。在这种情况下,我们有一个合并冲突,我们必须在继续之前手动解决它。我们需要编辑失败的文件,准备最终版本,并提交更改。

10.3. git pull – 立即更新和应用

git pull 命令无非就是git fetch和 git merge合二为一: 

$ git pull origin/master

它检查给定分支的最新更改并将它们与当前分支合并,就像git fetchgit merge一样。这是更新当前分支最常用的方法。

此外,拉取更改可能还需要额外的手动操作来解决合并冲突。

10.4. git push – 发布本地提交

一旦我们同步了本地存储库并修复了未决的合并冲突,我们终于准备好发布我们的提交了。我们需要选择远程目标和本地分支。

让我们执行git push 命令:

$ git push origin master

这将使用本地提交的所有提交更新远程存储库的master分支。

最后,我们检查历史:

$ git log
commit 6d5810884c3ce63ca08084959e3a21405a1187df (HEAD -> master, origin/master, origin/HEAD)
Author: ...
Date: ...
    My second commit

我们完成了!我们刚刚将更改发送到远程存储库。

11. Git 分支

现在,让我们谈谈分支。以前,我们故意跳过任何分支操作。所有更改都在master分支上完成,这是每个 Git 存储库的默认更改。

分支用于开发彼此隔离的功能。我们使用其他分支进行开发,并在完成后将它们合并回主分支。

11.1. git branch – 管理分支

git branch 帮助我们管理分支。要创建一个新的,我们只需指定它的名称:

$ git branch new-branch

在我们将本地分支推送到远程存储库之前,其他人无法使用本地分支。

我们现在可以通过列出所有分支来查看新创建的分支:

$ git branch --list --all
* master
  new-branch
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

如果我们想删除一个本地分支,我们执行:

$ git branch -d new-branch

11.2. git checkout - 更改当前分支

如果我们想切换当前分支,我们使用git checkoutgit switch 函数:

$ git switch new-branch
Switched to branch 'new-branch'
$ git checkout master
Switched to branch 'master'

我们刚刚从master更改为new-branch ,然后使用这两个命令再次变回master

尽管两者的工作方式相似,但git switch命令只允许切换分支。相比之下,git checkout是一个更复杂的命令,使我们能够额外管理工作树文件、重置分支或将文件恢复到特定版本。